一、引言
Android应用程序在运行的时候,不管是dex还是native的so,都要加载到内存里面,所以直接把与之对应的内存dump出来分析是一个不错的主意,ndk给我们提供了可供开发者远程调试的gdb和gdbserver,但是一些功能比如gcore不能用,本文通过重新编译gdb和gdbserver ,从而可以达到使用gcore把内存dump出来的目的。以下使用的android源码是android4.4.
二、开发环境
Ubuntu 12.04
android-ndk-r9
三、解决过程
首先编译arm-linux-androideabi-gdb,android-ndk-r9中自带了gdbserver和arm-linux-androideabi-gdb,但是使用gdbserver和arm-linux-androideabi-gdb调试手机的时候,用gcore出现Command notimplemented for this target,原因是ndk里面自带的arm-linux-androideabi-gdb把gcore移除了,所以需要自己编译一个arm-linux-androideabi-gdb
以下是编译过程:
首先下载源码
mkdir gdb_build
git clone https://android.googlesource.com/toolchain/build.git git clone https://android.googlesource.com/toolchain/gdb.git
把build和gdb的源码下载完毕
执行
cd gdb_build
./configure --target=arm-linux-androideabi --prefix=gdb_build/gdb/gdb-7.6/arm-linux
make
make install
等待编译完毕。
arm-linux-androideabi-gdb就在gdb_build/gdb/gdb-7.6/arm-linux下生成了
在远程调试的时候可能发现如下错误
remote g packet reply is too long
需要修改gdb-7.6/gdb/remote.c中把如下2行注释掉
//if (buf_len > 2 * rsa->sizeof_g_packet) // error(_("Remote 'g' packet reply is too long: %s"), rs->buf);
修改成
if (buf_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 (rsa->regs[i].offset >= rsa->sizeof_g_packet) rsa->regs[i].in_g_packet = 0; else rsa->regs[i].in_g_packet = 1; }
以上编译的是gdb7.6,需要与之版本对应的gdbserver。
编译gdbserver流程:
参考http://www.omappedia.org/wiki/Android_-_How-to_Rebuild_gdbserver
需要android源码,首先创建sysroot目录,
cd gdb_build ./build/build-sysroot.sh ~/workspace/androidsrc/out/target/product/generic/ ./sysroot
然后修改~/workspace/androidsrc/ndk/build/tools/build-gdbserver.sh脚本,
把
if [ "$NOTHREADS" !="yes" ] ; then
# We're going to rebuild libthread_db.o fromits source
#that is under sources/android/libthread_db and place its header
# andobject file into the build sysroot.
LIBTHREAD_DB_DIR=$ANDROID_NDK_ROOT/sources/android/libthread_db/gdb-$GDB_VERSION
if[ ! -d "$LIBTHREAD_DB_DIR" ] ; then
dump "ERROR: Missing directory: $LIBTHREAD_DB_DIR"
exit 1
fi
#Small trick, to avoid calling ar, we store the single object file
#with an .a suffix. The linker will handle that seamlessly.
runcp $LIBTHREAD_DB_DIR/thread_db.h $BUILD_SYSROOT/usr/include/
run$TOOLCHAIN_PREFIX-gcc --sysroot=$BUILD_SYSROOT -o$BUILD_SYSROOT/usr/lib/libthread_db.a -c $LIBTHREAD_DB_DIR/libthread_db.c
if [$? != 0 ] ; then
dump "ERROR: Could not compile libthread_db.c!"
exit 1
fi
fi
修改为
<<NOT_NEEDED # Removelibthread_db to ensure we use exactly the one we want. rm -f$BUILD_SYSROOT/usr/lib/libthread_db* rm -f$BUILD_SYSROOT/usr/include/thread_db.h if ["$NOTHREADS" != "yes" ] ; then #We're going to rebuild libthread_db.o from its source #that is under sources/android/libthread_db and place its header # andobject file into the build sysroot. LIBTHREAD_DB_DIR=$ANDROID_NDK_ROOT/sources/android/libthread_db/gdb-$GDB_VERSION if[ ! -d "$LIBTHREAD_DB_DIR" ] ; then dump "ERROR: Missing directory: $LIBTHREAD_DB_DIR" exit 1 fi #Small trick, to avoid calling ar, we store the single object file #with an .a suffix. The linker will handle that seamlessly. runcp $LIBTHREAD_DB_DIR/thread_db.h $BUILD_SYSROOT/usr/include/ run$TOOLCHAIN_PREFIX-gcc --sysroot=$BUILD_SYSROOT -o$BUILD_SYSROOT/usr/lib/libthread_db.a -c $LIBTHREAD_DB_DIR/libthread_db.c if [$? != 0 ] ; then dump "ERROR: Could not compile libthread_db.c!" exit 1 fi fi NOT_NEEDED
修改~/workspace/androidsrc/ndk/build/tools/build-gdbserver.sh
把
get_toolchain_install () { localNDK=”$1” shift echo"$ NDK/$(get_toolchain_install_subdir “%@”)” }
修改为
get_toolchain_install () { echo"$1/prebuilts/gcc/$HOST_TAG/arm/$TOOLCHAIN" }
然后执行
sudo ~/workspace/androidsrc/ndk/build/tools/build-gdbserver.sh ~/workspace/gdb_build ~/workspace/androidsrcarm-linux-androideabi-4.6 --verbose --build-out=~/workspace/gdb_build/install--gdb-version=7.6 --sysroot=~/workspace/gdb_build/sysroot
编译的时候可能会出现2个错误,
~/workspace/gdb_build/gdb/gdb-7.6/gdb/gdbserver/gdb_proc_service.h:79:1:error: unknown type name 'elf_gregset_t' ~/workspace/gdb_build/gdb/gdb-7.6/gdb/gdbserver/linux-low.c:113:3:error: conflicting types for 'Elf32_auxv_t' ~/workspace/gdb_build/install/sysroot/usr/include/elf.h:40:3:note: previous declaration of 'Elf32_auxv_t' was here ~/workspace/gdb_build/gdb/gdb-7.6/gdb/gdbserver/linux-low.c:128:3:error: conflicting types for 'Elf64_auxv_t' ~/workspace/gdb_build/install/sysroot/usr/include/elf.h:47:3:note: previous declaration of 'Elf64_auxv_t' was here
elf_gregset_t类型不识别,Elf32_auxv_t和Elf64_auxv_t定义冲突。
修改~/workspace/gdb_build/gdb/gdb-7.6/gdb/gdbserver/linux-low.c的结构体Elf32_auxv_t和Elf64_auxv_t,把他的名字改变。
修改
~/workspace/gdb_build/gdb/gdb-7.6/gdb/gdbserver/gdb_proc_service.h
在
#ifndef HAVE_PRGREGSET_T typedef elf_gregset_t prgregset_t; #endif
上面添加
typedef unsigned long elf_greg_t; typedef elf_greg_t elf_gregset_t[35];编译好的gdbserver在~/workspace/gdb_build/install生成。