from:http://blog.csdn.net/virtualpower/article/details/5660966
前言:dalvik 是 Android 的重要组成部分,掌握其运行机制对理解整个Android系统有着相当之大的帮助。本文将介绍GDB单步调试和Dexdump工具的使用,期望为探索dalvik打下一定的基础。
为了能够更方便的调试dalvik,我们需要编译一个在X86上运行的dalvik和相关工具。编译步骤如下:
首先进入到Android 源码根目录
source build/envsetup.sh (不是网上有些文章写的只输入 build/envsetup.sh)
lunch 2在此之后可以看到TARGET_PRODUCT 为sim。TARGET_ARCH为x86
make 或者 make dalvikvm 和 make dexdump(make 为编译所有程序,比较耗时,有时甚至某些模块编译不过,如为节省时间,可使用make dalvikvm直接编译dalvik, make dexdump直接编译dexdump)
在用gdb启动dalvik时,需要设置一些环境,比较繁琐,这里创建一个脚本来简化这些过程,脚本名为grund.sh,放于Android源码根目录。下面为脚本内容。
#!/bin/sh
base=`pwd`
root=$base/out/debug/host/linux-x86/pr/sim/system
export ANDROID_ROOT=$root
bootpath=$root/framework
export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.police.jar
export ANDROID_DATA=/tmp/dalvik_test
mkdir -p $ANDROID_DATA/dalvik-cache
exec gdb $root/bin/dalvikvm
准备一个简单的java 程序,如hello.java,编译后将hello.jar拷贝至Android 源码根目录。(hello.java与makefile见附录)
进入到Android 源码根目录
./grund.sh(执行上述脚本,之后会看到gdb提示符)
在gdb提示符后输入“set args �Ccp hello.jar hello”
这个时候就可以设置断点,单步跟踪了!如有对gdb不熟的同学,请google之。 main()函数为入口函数,先在main.c 212行设置断点(在gdb提示符后输入“b 212”)
在gdb提示符后输入”r”, OK,我们会看到dalvik被gdb启动执行,然后停于212行,执行JNI_CreateJavaVM函数前先看看gDvm的内容(输入p gDvm)。然后执行JNI_CreateJavaVM函数(输入“n”),再看看gDvm的内容。对比执行前后的变化,可大概知道JNI_CreateJavaVM函数所做的事情。
main.c 249 行代码用于加载hello.class,在249行设置断点。在此中断后,看下slashClass的内容(输入“p slashClass“),slashClass正是”hello”字符串。接下来单步进入执行之(输入”s”),然后查看下函数调用栈(输入”bt”)。可知现在正在执行的是jni.c 中的FindClass函数。通过此方法,可知函数指针指向的是何函数。
main.c 255行代码用于取得hello.java 中main函数编译后的字节码。类似于g步骤,可知此时执行的函数为jni.c中的GetStaticMethodID函数
main.c 273行代码执行main函数编译后的字节码,类似于g步骤,可知此时执行的函数为jni.c 中的2681行。此处为宏定义,不容易找到。但通过gdb调试,可以准确的定位。如此时继续运行程序,”hello world” 就会出现在我们的眼前!
此处只做简单分析,为抛砖引玉用,各位同学可以藉此探究自己感兴趣的内容。在接下来的文章中会详细分析class加载与字节码的执行。
Dexdump 可执行文件放于out目录下,可使用”find out/ -name dexdump”命令来找到dexdump。
“dexdump �Cfhello.jar” 命令可打印jar文件的头部信息。
“dexdump �Cd hello.jar” 可打印所编译的字节码。
头部信息如下:
Opened 'hello.jar', DEX version '035'
DEX file header:
magic: 'dex
035'
checksum: f2f85a9c
signature: 0404...7831
file_size: 740
header_size: 112
link_size: 0
link_off: 0 (0x000000)
string_ids_size: 14
string_ids_off: 112 (0x000070)
type_ids_size: 7
type_ids_off: 168 (0x0000a8)
field_ids_size: 1
field_ids_off: 232 (0x0000e8)
method_ids_size: 4
method_ids_off: 240 (0x0000f0)
class_defs_size: 1
class_defs_off: 272 (0x000110)
data_size: 436
data_off: 304 (0x000130)
其中string_ids,type_ids,field_ids,method_ids,class_defs皆可理解为索引。通过这些索引,可以查到真正的数据存放位置。Data_off为真正的数据存放位置。
字节码如下:
#1: (in Lhello;)
name: 'main'
type: '([Ljava/lang/String;)V'
access: 0x0009 (PUBLIC STATIC)
code-
registers: 3
ins: 1
outs: 2
insns size: 10 16-bit code units
000148:|[000148] hello.main:([Ljava/lang/String;)V
000158: 6200 0000|0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
00015c: 1a01 0900|0002: const-string v1, "hello world" // string@0009
000160: 6e20 0200 1000|0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0002
000166: 2a00 0000 0000|0007: goto/32 #00000000
catches: (none)
positions:
0x0000 line=4
0x0007 line=5
locals:
Virtual methods-
source_file_idx: 10 (hello.java)
附录:
Hello.java :
public class hello{
public static void main(String args[]) {
System.out.println("hello world");
while(true) {}
}
}
Makefile:
ANDROID_SRC_DIR := /android/platform_sim
android_dir_dx = $(ANDROID_SRC_DIR)/out/host/linux-x86/bin/dx
all:
javac hello.java
$(android_dir_dx) --dex --output=hello.jar hello.class
clean:
@rm *.jar *.class
后续dalvik分析之Class 加载篇请参阅:http://blog.csdn.net/VirtualPower/archive/2010/07/06/5715277.aspx