Android C++调试

 

Android C++调试

    Java代码出错的时候,我们会在第一时间想到调试。通过一番调试之后之后,问题就迎刃而解。我们现在很多项目中都会用到JNI调用,其中.so文件的c++代码出错了,什么办呢?

我们还是从一个简单的例子说起吧。现在.so文件有如下一个代码:

Jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,jobject thiz ){ 

int *t;

*t = 1;

    return (*env)->NewStringUTF(env, "Hello from JNI !");

}

   分析上面代码: 由于指针t没有分配内存,所以给它赋值回报错. 接下来我们开始运行调用该so文件的Android项目,在Logcat下会发现如下错误信息:

    10-14 10:31:34.666: INFO/DEBUG(551): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

10-14 10:31:34.675: INFO/DEBUG(551): Build fingerprint: 'generic/sdk/generic/:1.5/CUPCAKE/147336:eng/test-keys'

10-14 10:31:34.675: INFO/DEBUG(551): pid: 6071, tid: 6071  >>> com.example.hellojni <<<

10-14 10:31:34.675: INFO/DEBUG(551): signal 7 (SIGBUS), fault addr 00000000

10-14 10:31:34.685: INFO/DEBUG(551):  r0 0000a9d0  r1 43736118  r2 41049d64  r3 00000001

10-14 10:31:34.685: INFO/DEBUG(551):  r4 befd8540  r5 00000004  r6 804002e5  r7 41049d3c

10-14 10:31:34.695: INFO/DEBUG(551):  r8 befd8520  r9 41049d34  10 41049d20  fp 00000000

10-14 10:31:34.695: INFO/DEBUG(551):  ip 804002e5  sp befd8510  lr ad00e3b8  pc 804002e4  cpsr 20000030

10-14 10:31:34.746: INFO/DEBUG(551):          #00  pc 000002e4  /data/data/com.example.hellojni/lib/libhello-jni.so

10-14 10:31:34.756: INFO/DEBUG(551):          #01  pc 0000e3b4  /system/lib/libdvm.so

10-14 10:31:34.765: INFO/DEBUG(551): stack:

    观察上面的日志文件,我们会发现c++代码调用的堆栈信息. 在最顶端堆栈的是libhello-jni.so(上面代码编译在此so文件里面),以及用红色标识出来的地址000002e4, 我们正是通过该地址找到出错的函数。

    ndk 提供了windows c++二进制分析的工具。最新已经是ndk1.6了,可以到网上很容易获得。其中arm-eabi-objump.exe 工具是可以用于分析so文件。如下所示:

    arm-eabi-objump.exe  –S  libhello-jni.so > test.txt 输出重定向到test.txt中。

    打开test.txt,我们看到如下信息:

    libhello-jni.so:     file format elf32-littlearm

Disassembly of section .text:

000002e4 <Java_com_example_hellojni_HelloJni_stringFromJNI>:

    2e4:    b510        push    {r4, lr}

    2e6:    2301        movs    r3, #1

    2e8:    601b        str r3, [r3, #0]

    2ea:    4905        ldr r1, [pc, #20]   (300 <.text+0x1c>)

    2ec:    6804        ldr r4, [r0, #0]

    2ee:    4a05        ldr r2, [pc, #20]   (304 <.text+0x20>)

    2f0:    23a7        movs    r3, #167

    2f2:    4479        add r1, pc

    2f4:    009b        lsls    r3, r3, #2

    2f6:    58e3        ldr r3, [r4, r3]

    2f8:    1889        adds    r1, r1, r2

    2fa:    4798        blx r3

    2fc:    bd10        pop {r4, pc}

    2fe:    0000        lsls    r0, r0, #0

    300:    10ae        asrs    r6, r5, #2

    302:    0000        lsls    r0, r0, #0

    304:    ef64 ffff   undefined

    我们可以看到红色的编译地址2e4. 和我们之前看到地址2e4一样。基本上可以定位是Java_com_example_hellojni_HelloJni_stringFromJNI函数的某一位置出错。仔细分析,可以定位到之前的问题。

 

接下来我还要介绍下面常用的二进制分析工具

AR 用来建立、修改、提取静态库文件.

NM 列出目标文件的符号表中定义的符号。常见的链接或者运行时发生的unresolved symbol类型的错误可以用NM来辅助调试

OBJDUMP  objdump是所有二进制工具之母,能够显示一个目标文件中所有的信息,通常我们用它来反汇编.text节中的二进制指令

READELF  readelf可用来显示ELF格式可执行文件的信息

SIZE

size命令可以列出目标文件每一段的大小以及总体的大小。默认情况下,对于每个目标文件或者一个归档文件中的每个模块只产生一行输出。size可以用来简单快速的了解ELF文件各个段的情况

ldd可用来显示执行文件需要哪些共享库, 共享库装载管理器在哪里找到了需要的共享库

 

工具的使用我这边就不想详细多说,网上对这些工具介绍有海量的信息。我想强调的是,要想深入解决c++的问题,我们要熟练掌握这些工具的使用,这对我们调试Android native code有非常大的帮助。

 

后面介绍些NDK的一些知识

 

1.  加载so 文件.

只要在项目工程上建立 如下目录结构:

java代码中如下调用  System.loadlibrary(“ds”);  而不需要像我之前所说的要从Assert目录底下拷贝到应用程序目录下。

 

2.介绍Application.mk一些参数

APP_MODULES

这个变量必须有,用来描述你应用程序需要的所有原生模块(Android.mk文件描述)
这是用空格分割的模块名字列表,这个模块名字与在Android.mk文件中LOCAL_MODULE定义是一样的。

APP_PROJECT_PATH

这个变量必须有,应该给出你的程序项目的绝对路径,这是用于复制或安装剥离的共享库的版本到APK生成器所知道的位置。


APP_OPTIM
这个变量是可选的,可以定义成两个值'release' or 'debug'. 用于修改编译程序模块时的优化层级。

'release'
模式是默认的,会产生高优化的文件,’debug’模式会生成不优化的文件,使得调试更容易进行。

主意,调试’release’’debug’文件都是可能的,但是'release'版在调试节提高的信息很少,一些变量被优化输出,无法检查,代码被重排序,使得跟踪代码很困难,堆栈追踪也不可靠,等等

APP_CFLAGS
一套编译器选项,在编译模块中的C代码时选用。这可能用于根据应用程序来改变模块的生成,以代替修改Android.mk文件本身。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值