使用gdb除錯android原生程式
Android NDK目錄下的gdb雖然可以除錯android程式,但是這個不包含符號資訊,除錯時需要設定Android系統動態連結庫的符號載入路徑,並且只能除錯擁有除錯資訊的原生程式,而一般情況下,使用 Android NDK編譯的原生程式都不包含除錯資訊,因此無發使用官方的gdb
我們可以手動編譯一個靜態版本的gdb偵錯程式,首先到gdb的官方下載gdb的原始碼,我們這裡下載的版本為7.3.1,下載地址為:ftp://sourceware.org/pub/gdb/gdb-7.3.1.tar.gz,
下載後解壓原始碼,在終端下使用命令安裝編譯gdb所需的軟體包。
sudo apt-get install bison flex libncurses5-dev texinfo gawk libtool
編譯gdb時不要使用自帶的多執行緒庫thread_db.c,應使用Android NDK中的修改版本,位於Android NDk 的sources/android/libthread_db/gdb-7.3.x/libthread_db.c,為了避免相容性的問題,將其編譯成靜態庫,配置gdb編譯指令碼如下
export TOOLCHAIN_PATH=/home/android/tools/android/android-ndk-r8b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86
export PATH=$TOOLCHAIN_PATH/bin:$PATH
export SYSROOT=/home/android/tools/android/android-ndk-r8b/platforms/android-14/arch-arm
export TOOLCHAIN_PREFIX=$TOOLCHAIN_PATH/bin/arm-linux-androideabi
export CC="$TOOLCHAIN_PREFIX-gcc --sysroot=$SYSROOT"
export AR="$TOOLCHAIN_PREFIX-ar"
$CC -o $SYSROOT/usr/lib/libthread_db.o -c /home/android/tools/android/android-nkd-r8b/sources/android/libthread_db/gdb-7.3.x/libthread_db.c
$AR -r $SYSROOT/usr/lib/ibthread_db.a $SYSROOT/usr/lib/libthread_db.o
#配置gdb編譯指令碼
./configure --target=arm-elf-linux --enable-static --disable-stripping -with-libthread-db=$SYSROOT/usr/lib/libthread_db.a
"--target=arm-elf-linux" 指定了被除錯的程式執行的系統平臺,“--enable-static”指定了靜態編譯,“--disable-stripping”指定禁止剝離符號資訊,“--with-libthread-db”手動指定多執行緒庫檔案,在終端上依次執行以上命令會聲稱makefile 我呢間,接下來還需要手動修改gdb-7.3.1/gdb目錄下的remote.c檔案,找到process_G_packet()函式的程式碼,將一下的內容
if(but_len>2*rsa->sizeof_g_packet)
error(_("Remote 'g' packet reply is too long:%s"),rs->but);
修改為
if(but_len>2*rsa->sizeof_g_packet)
{
rsa->sizeof_g_packet=buf_len;
for(i=0;i<gdbarch_num_regs (gdbarch);i++)
{
if(rsa->regs[i].pnum==-1)
continue;
if(rya->regs[i].offset>=rsa->sizeof_g_packet)
rsa->regs[i].in_g_packet=0;
else
rsa->regs[i].in_g_packet=1;
}
}
修改儲存後,在終端上執行make編譯gdb
單獨使用gdb還不能除錯android原生程式 還需要編譯gdbserver,這個的原始碼在 gdb-7.3.1/gdb/gdbserver 目錄下,在終端下執行一下命令配置gdbserver編譯指令碼
export CC="$TOOLCHAIN_PREFIX-gcc --sysroot=$SYSROOT"
export CFLAGS="-02 -D__ANDROID__ -DANDROID -DSTDC_HEADERS -D__GLIBC__"
./configure --host=arm-linux-androideabi --with-libthread-db=$SYSROOT/usr/lib/libthread_db.a
命令執行完成後會在gdb-7.3.1/gdb/gdbserver 目錄下聲稱makefile檔案,開啟該檔案找到 WERROR_CFLAGS的定義,將它的值清空,然後開啟config.h檔案,將“/*#undef HAVE_LWPID_T */”改為“#define HAVE_LWPID_T 1”,修改後儲存,然後在終端下執行make編譯gdbserver.
下面介紹如何除錯,首先準備一個android app,這個用一個名為testapp的程式,在終端下執行以下兩個命令將testapp與gdbserver複製到android石碑的/data/local/tmp目錄
adb push test app /data/local/tmp
dab push gdbserver /data/local/tmp
執行以下兩行命令給兩個檔案加上可執行許可權
adb shell chmod 755 /data/local/tmp/testapp
adb shell chnod 755 /data/local/tmp/gdbserver
接著執行 “adb shell /data/local/tmp/gdbserver:12345 /data/local/tmp/testapp”,啟動dgb除錯伺服器,會輸入如下資訊
Process /data/local/tmp/testapp create; pid=292
Listening on port 12345
程式其實除錯伺服器已經啟動,並且監聽了12345號埠,開啟另一個終端視窗執行以下命令開啟埠轉發
dab forward tcp:12345 tcp:12345
在pc 端的終端下執行./gdb啟動gdb偵錯程式,然後執行以下命令連線gdb除錯伺服器
target remote localhost:12345
命令執行後會輸出
(gdb) target remote localhost:12345
Remote debugging using localhost:12345
warning: Can not parse XML target description; XML support was disabled at compile time
0xb000100 in ?? ()
(gdb)
使用 ida pro 或者objdump 找到程式main()函式地址,例如 0x8580,在gdb Shell 環境下執行命令“b *0x8580” 在main()函式的第一行上設定斷點,輸出資訊如下
(gdb) b *0x8580
Breakpoint 1 at 0x8580
(gdb)
斷點設定完成後輸入continue 讓程式繼續執行,輸出資訊如下
(gdb)continue
Continuing.
Program received signal SIGSEGV,Segmentation fault.
0x00008584 in ??()
執行"set disassemble-next on "設定反彙編顯示程式碼,然後執行“dosas 0x8580,+20”顯示0x8580以下20各字元的反彙編程式碼,輸出資訊如下
(gdb)disas 0x8580,+20
Dump of assembler code from 0x8580 to 0x8594
0x00008580:ldr r0,[pc,#16] ;0x8598
=> 0x00008584:push {r4,lr}
0x00008588:add r0,pc,r0
0x0000858c:bl 0x84f8
0x00008590:mov r0,#0
End of assembler dump.
(gdb)
接下來可以輸入si或者ni 命令來單步除錯了