Docker上でGDBとQEMU(ユーザモードエミュレーション)を接続してデバッグ

目標

筆者の実機(M1 Mac, AArch64)でx86_64のDockerコンテナを立ち上げ、その上でQEMUを用いてリモートデバッグをする

手順

x86_64なDockerコンテナを起動し、コンテナに入る

docker run -it --platform linux/amd64 ubuntu:latest

qemu-user-static gdb及びコンパイラ含め必要なツールをインストールする

apt update
apt install qemu-user-static gdb gcc vim

プログラムを書いてデバッグ情報付きでコンパイルする(a.outx86_64のバイナリ)

root@6fd1c6bc2337:/home# vim test.c
root@6fd1c6bc2337:/home# cat test.c
#include <stdio.h>

int main(void) {
    printf("Hello World\n");
    return 0;
}

root@6fd1c6bc2337:/home# gcc test.c -g -static
root@6fd1c6bc2337:/home# ./a.out
Hello World

qemu-x86_64-staticTCPの1234ポートを開く

qemu-x86_64-static -g 1234 a.out &

gdbでポートに接続しリモートデバッグを開始する

gdb -q -ex "target remote :1234" a.out

runコマンドが使えないことに注意(continueを使う)

root@6fd1c6bc2337:/home# gdb -q -ex "target remote :1234" a.out
Reading symbols from a.out...
Remote debugging using :1234
0x0000000000401650 in _start ()
(gdb) b main
Breakpoint 1 at 0x40177d: file test.c, line 4.
(gdb) run
The "remote" target does not support "run".  Try "help target" or "continue".
(gdb) c
Continuing.

Breakpoint 1, main () at test.c:4
4      printf("Hello World\n");
(gdb) n
Hello World
5      return 0;

コマンドライン引数ありの場合

コマンドラインを受け取るプログラムを書いてコンパイル

root@6fd1c6bc2337:/home# vim test.c
root@6fd1c6bc2337:/home# cat test.c
#include <stdio.h>

int main(int argc, char **argv) {
    for (int i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    return 0;
}

root@6fd1c6bc2337:/home# gcc test.c -g -static
root@6fd1c6bc2337:/home# ./a.out aaa iii
./a.out
aaa
iii

qemu-x86_64-staticで実行ファイル名の後に引数を書くようにすれば良い

qemu-x86_64-static -g 1234 a.out aaa iii &

リダイレクトについても同様

qemu-x86_64-static -g 1234 a.out aaa iii > output.txt &

なぜこの記事を書いたか

AArch64であるM1 Mac上のx86_64なDockerコンテナで普通にGDBを使おうとして以下のようになりできない

root@6fd1c6bc2337:/home# gdb a.out
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...
(gdb) run
Starting program: /home/a.out
warning: Error disabling address space randomization: Operation not permitted
warning: Could not trace the inferior process.
warning: ptrace: Function not implemented
During startup program exited with code 127.

warning: ptrace: Function not implementedとあるようにGDB内で使われるシステムコールptrace()が削除されている(?)ようだ。 そこで今回その代替となるQEMUが提供するリモートデバッグ機能を利用するに至った。

参考になったサイト

DockerでユーザモードQEMUによるARMエミュレーション環境を構築する - ももいろテクノロジー

https://ughe.github.io/2018/08/14/qemu-gdb-integration