九鼎创展论坛

标题: 精通Android游戏开发 [打印本页]

作者: armeasy    时间: 2012-1-6 09:40
标题: 精通Android游戏开发
2.2.1 缺少符号时的调试
运行这个项目之前,先要确保本地库中不缺少任何符号。这可能很棘手,因为即使缺少符号,共享库也有可能成功编译,而不会告诉你任何有关缺少符号的信息。更糟糕的是,如果缺少符号,而你试图运行程序,运行时将无法正常加载库,要想知道哪里出了问题可能很让人头疼。
对于这个问题,可以用一个很容易的办法解决:编写一个简单的C程序调用这个库(见代码清单2-8)。链接时,编译器会告诉你本地库是否缺少符号,然后可以加以修正。在从应用本身测试之前,如果想从命令行测试本地库,这个测试程序也很有用。
代码清单2-8 本地库的一个简单测试程序
#include <stdio.h>
extern int lib_main(int argc, char **argv);
//void _start(int argc, char **argv)
int main(int argc, char **argv)
{
    int i;
    printf("Argc=%d Argv=%p\n", argc, argv);
    for ( i = 0 ; i < argc ; i++ ) {
        printf("Main argv[%d]=%s\n", i, argv);
    }
    printf("Starting Lib main sub\n");
    lib_main(argc, argv) ;
    exit (0);
}
testlib编译这个程序,或者使用辅助脚本手动编译,如下:
agcc -c     testlib.c
ald -o testlib testlib.o -L. -lch02
注意链接时必须有-L. -lch02,这会告诉编译器要链接libch02.so,并在当前文件夹中搜索库。
现在,启动模拟器来测试这个库。
2.2.2 在设备上测试动态库
要测试这个库,需要使用pushlib把文件上传到设备,这个目标展开为以下命令:
adb push libcg02.so /data
adb push testlib /data
然后登录到设备,切换到/data文件夹,并执行测试程序:
$ adb shell
# cd /data
# chmod 777 lib * test*
# ./testlib
bionic/linker/linker.c:1581| ERROR:   833 could not load 'libch02.so'

bionic/linker/linker.c:1641| ERROR: failed to link ./testlib
bionic/linker/linker.c:1741| ERROR: CANNOT LINK EXECUTABLE './testlib'

这是在模拟器的1.5版本中运行程序。可以看到,库无法加载。下一节会说明出现了什么问题。如果在SDK 1.0 R2版本中尝试这个过程则会成功。
注意,这并不是说无法从Java应用加载库,只是说明本地链接器/system/bin/linker存在一个问题。
2.2.3 用strace调试
出于某种原因,共享库的本地测试程序在SDK 1.0 R2版本中能顺利运行,但是在1.5 R2中无法加载。由输出我们可以得到一个线索:文件bionic/linker/linker.c:1581无法加载库。对于这种情况,有一个简单易用的Linux工具(strace)可以提供帮助。
strace工具会运行指定的命令,直到退出。它将截获并记录进程执行的系统调用以及进程接收的信号。每个系统调用的名称、其参数以及返回值都将输出。这是一个很有用的诊断和调试工具,在没有提供源代码的情况下,可以利用这个工具解决程序中出现的问题。你会发现,即使是正常程序,也能通过跟踪来了解系统及其系统调用的大量信息。
本书源代码提供了strace的一个Android版本。(strace 现在已经内置到Android SDK 1.5及以后版本中。)下面就来看看发生了什么,如代码清单2-9所示。
代码清单2-9 strace工具输出
$ adb push strace /data
$ adb shell
# cd /data
# ./strace ./testlib
execve("./testlib", ["./testlib"], [/* 10 vars */]) = 0

getpid()                                = 835
gettid()                                = 835
sigaction(SIGILL, {0xb0001a99, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGABRT, {0xb0001a99, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGBUS, {0xb0001a99, [], SA_RESTART}, {SIG_DFL}, 0) = 0
stat64("/system/lib/libch02.so", 0xbef9ea58) = -1 ENOENT (No such file or directory)
stat64("/lib/libch02.so", 0xbef9ea58)   = -1 ENOENT (No such file or directory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40000000
mprotect(0x40000000, 4096, PROT_READ)   = 0
fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 1), ...}) = 0
brk(0)                                  = 0x11000
brk(0x11000)                            = 0x11000
brk(0x12000)                            = 0x12000
mprotect(0x40000000, 4096, PROT_READ|PROT_WRITE) = 0
mprotect(0x40000000, 4096, PROT_READ)   = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
write(1, "bionic/linker/linker.c:1581| ERR"..., 70bionic/linker/linker.c:1581| ERROR:   835
could not load 'libch02.so'
) = 70
write(1, "bionic/linker/linker.c:1641| ERR"..., 61bionic/linker/linker.c:1641| ERROR: failed
to link ./testlib
) = 61
write(1, "bionic/linker/linker.c:1741| ERR"..., 71bionic/linker/linker.c:1741| ERROR: CANNOT LINK EXECUTABLE './testlib'
) = 71
exit_group(-1)                          = ?
Process 835 detached

下面这两行对问题源由给出了一条线索:
stat64("/system/lib/libch02.so", 0xbef9ea58) = -1 ENOENT (No such file or directory)
stat64("/lib/libch02.so", 0xbef9ea58)   = -1 ENOENT (No such file or directory)
链接器首先尝试从设备的/system/lib文件夹打开库。这是一个只读的文件系统,用户定义的库无法保存在这里。接下来,链接器搜索/lib文件夹,这个文件夹不存在,所以链接失败。链接器并没有在当前目录中搜索,问题就出在这里!
好在,这个问题不会妨碍在Java应用中加载库(只要其中没有缺少符号)。
如果在SKD 1.0 R2中运行同样的命令序列,可以看到第二行变成:
stat64("./libch02.so", 0xbef9ea58)   = 0 OK
所以,程序可以成功运行。
说明    很难讲文件bionic/linker/linker.c从1.0版本到1.5版本有哪些变化,因为Google对此没有提供任何支持。对此我只能猜测:有可能开发人员忘记在当前文件夹搜索,也可以使用一些新的编译选项来告诉链接器在哪里搜索。

2.2.4 静态编译
最后,如果你希望编写一个命令行工具在设备中运行,必须静态地编译。可以考虑代码清单2-10中的简单程序,它会把命令行参数打印到stdout
代码清单2-10 简单命令行程序
#include <stdio.h>
int main(int argc, char **argv)
{
    int i;
    for ( i = 0 ; i < argc ; i++ ) {
        printf("Main argv[%d]=%s\n", i, argv);
    }
    printf("Hello World\n");
    exit( 0);
}
如果使用辅助脚本并提供-static选项来编译这个程序,会看到以下错误:
agcc -c     main.c
ald -static -o a.out main.o
arm-none-linux-gnueabi-ld: cannot find -lc
链接器无法找到C运行时库libc.so,尽管库路径本身并没有错误。应该记得,ald使用了-nostdlib,所以它不会链接标准库。
如果采用静态编译(使用-static),必须在加载时去掉-nostdlib。因此,正确的链接命令应该如下:
user@ubuntu:~/ch02.Project/native$ arm-none-linux-gnueabi-ld
--dynamic-linker=/system/bin/linker
-rpath /system/lib
-rpath /home/user/tmp/android/system/lib
-L/home/user/tmp/android/system/lib
-static -o a.out main.o -lc -lm
/home/user/mydroid/prebuilt/darwin-x86/toolchain/

arm-eabi-4.2.1/lib/gcc/arm-eabi/4.2.1/libgcc.a
arm-none-linux-gnueabi-ld: warning: cannot find entry symbol _start;
defaulting to 000080e0

现在可以在设备中测试a.out了:
$ make pushbin
$ adb shell
# ./a.out test
Main[0]=test







欢迎光临 九鼎创展论坛 (http://bbs.9tripod.com/) Powered by Discuz! X3.2