Android动态链接库无法解析主程序的符号
我可以成功地用 C 语言创建一个链接到库的程序,并且能够调用该库的函数。如果该库从主程序调用函数,则会出现错误:
root@android:/data/local/tmp # ./helloworld
link_image[1966]: 637 could not load needed library 'libhello.so' for './helloworld' (reloc_library[1315]: 637 cannot locate 'crossfunction'...) CANNOT LINK EXECUTABLE
代码位于两个 C 文件中,并且我还包含 Makefile。 hello.c 是包含 main.c(主程序)调用的函数 hello 的库。函数 hello 尝试调用函数 crossfunction,但这在 android 中不起作用(在 Linux 中它工作得很好)。我怀疑来自android链接器,但到目前为止还没有证据(请参阅https://android.googlesource.com/platform/bionic/+/froyo-release/linker/README.TXT)。
另一个好的提示可能是 libhello.so 文件中 crossfunction 的 readelf 输出中的 NOTYPE 分配。请看下文。
5: 00000000 0 NOTYPE GLOBAL DEFAULT UND crossfunction
编译器标志或链接器标志中可能有任何提示吗?
::::::::::::::
main.c
::::::::::::::
#include <stdio.h>
extern void hello(const char* name);
int main(void) {
hello("World!");
}
void crossfunction(void) {
printf("This is called from the library\n");
}
::::::::::::::
hello.c
::::::::::::::
#include <stdio.h>
extern void crossfunction(void);
static char *s;
void hello(const char* name) {
s = "my second name";
printf("Hello %s %s!\n", s, name);
crossfunction();
}
为了编译,我将agcc包装器与android ndk结合使用 https://github.com/nitomartinez/agcc
这是 Makefile:
OBJECTS=main.o
LIB=libhello.so
LIBOBJ=hello.o
TARGET=helloworld
TARGETDIR=/data/local/tmp
CC=agcc
.PHONY: all install run clean distclean
all: $(TARGET) $(LIB)
hello.o: hello.c Makefile
$(CC) $(CFLAGS) -c -o hello.o hello.c
$(TARGET): $(OBJECTS) $(LIB) Makefile
$(CC) -Wl,-rpath=$(TARGETDIR) -lhello -L . -o $(TARGET) $(OBJECTS)
$(LIB): $(LIBOBJ) Makefile
$(CC) -shared -o $(LIB) $(LIBOBJ)
install: $(TARGET)
adb push $(TARGET) $(TARGETDIR)/$(TARGET)
adb push $(LIB) $(TARGETDIR)/$(LIB)
run: install
adb shell "export LD_LIBRARY_PATH=$(TARGETDIR); $(TARGETDIR)/$(TARGET) "
我查看了 readelf 位,但发现 .dynsym、.rel.plt 和 .symtab 部分没有实质性差异。
对于 helloworld
Relocation section '.rel.plt' at offset 0x33c contains 3 entries:
Offset Info Type Sym.Value Sym. Name
0000954c 00000416 R_ARM_JUMP_SLOT 00008368 hello
00009550 00000516 R_ARM_JUMP_SLOT 00008374 puts
00009554 00000816 R_ARM_JUMP_SLOT 00008380 __libc_init
Symbol table '.dynsym' contains 16 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 000083b0 32 FUNC GLOBAL DEFAULT 7 crossfunction
2: 00008450 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end
3: 00009558 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__
4: 00008368 0 FUNC GLOBAL DEFAULT UND hello
5: 00008374 0 FUNC GLOBAL DEFAULT UND puts
...
Symbol table '.symtab' contains 62 entries:
Num: Value Size Type Bind Vis Ndx Name
...
41: 000083b0 32 FUNC GLOBAL DEFAULT 7 crossfunction
42: 00008450 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end
43: 00009558 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__
44: 00008368 0 FUNC GLOBAL DEFAULT UND hello
45: 00008374 0 FUNC GLOBAL DEFAULT UND puts
...
55: 00008390 32 FUNC GLOBAL DEFAULT 7 main
...
和 libhello.so
Relocation section '.rel.plt' at offset 0xae8 contains 7 entries:
Offset Info Type Sym.Value Sym. Name
000032cc 00000516 R_ARM_JUMP_SLOT 00000000 crossfunction
000032d0 00000616 R_ARM_JUMP_SLOT 00000000 printf
000032d4 00000f16 R_ARM_JUMP_SLOT 00000000 __cxa_begin_cleanup
000032d8 00001516 R_ARM_JUMP_SLOT 00000000 memcpy
000032dc 00001f16 R_ARM_JUMP_SLOT 00000000 abort
...
Symbol table '.dynsym' contains 64 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
...
5: 00000000 0 NOTYPE GLOBAL DEFAULT UND crossfunction
6: 00000000 0 FUNC GLOBAL DEFAULT UND printf
...
19: 00000b88 100 FUNC GLOBAL DEFAULT 7 hello
21: 00000000 0 FUNC GLOBAL DEFAULT UND memcpy
...
Symbol table '.symtab' contains 138 entries:
Num: Value Size Type Bind Vis Ndx Name
25: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
79: 00000000 0 NOTYPE GLOBAL DEFAULT UND crossfunction
80: 00000000 0 FUNC GLOBAL DEFAULT UND printf
I can succesfully create a program in C that links to a library, and is able to call functions of that library. If that library calls a function from the main program an error arises:
root@android:/data/local/tmp # ./helloworld
link_image[1966]: 637 could not load needed library 'libhello.so' for './helloworld' (reloc_library[1315]: 637 cannot locate 'crossfunction'...) CANNOT LINK EXECUTABLE
The code is in two C files, and I also include the Makefile. hello.c is the library which holds the function hello which is called by main.c (the main program). The function hello tries to invoke the function crossfunction and that doesn't work in android (in Linux it works perfectly well). I suspect from the android linker, but have no proof so far (please see https://android.googlesource.com/platform/bionic/+/froyo-release/linker/README.TXT).
Another good hint might be the NOTYPE assignment in the readelf output for the crossfunction in the libhello.so file. Please see below.
5: 00000000 0 NOTYPE GLOBAL DEFAULT UND crossfunction
Any hints perhaps in the compiler flags or in linker flags?
::::::::::::::
main.c
::::::::::::::
#include <stdio.h>
extern void hello(const char* name);
int main(void) {
hello("World!");
}
void crossfunction(void) {
printf("This is called from the library\n");
}
::::::::::::::
hello.c
::::::::::::::
#include <stdio.h>
extern void crossfunction(void);
static char *s;
void hello(const char* name) {
s = "my second name";
printf("Hello %s %s!\n", s, name);
crossfunction();
}
To compile I use the agcc wrapper with the android ndk https://github.com/nitomartinez/agcc
Here is the Makefile:
OBJECTS=main.o
LIB=libhello.so
LIBOBJ=hello.o
TARGET=helloworld
TARGETDIR=/data/local/tmp
CC=agcc
.PHONY: all install run clean distclean
all: $(TARGET) $(LIB)
hello.o: hello.c Makefile
$(CC) $(CFLAGS) -c -o hello.o hello.c
$(TARGET): $(OBJECTS) $(LIB) Makefile
$(CC) -Wl,-rpath=$(TARGETDIR) -lhello -L . -o $(TARGET) $(OBJECTS)
$(LIB): $(LIBOBJ) Makefile
$(CC) -shared -o $(LIB) $(LIBOBJ)
install: $(TARGET)
adb push $(TARGET) $(TARGETDIR)/$(TARGET)
adb push $(LIB) $(TARGETDIR)/$(LIB)
run: install
adb shell "export LD_LIBRARY_PATH=$(TARGETDIR); $(TARGETDIR)/$(TARGET) "
I had a look at the readelf bits, but I found no substantial differences in the .dynsym, .rel.plt and .symtab sections.
For helloworld
Relocation section '.rel.plt' at offset 0x33c contains 3 entries:
Offset Info Type Sym.Value Sym. Name
0000954c 00000416 R_ARM_JUMP_SLOT 00008368 hello
00009550 00000516 R_ARM_JUMP_SLOT 00008374 puts
00009554 00000816 R_ARM_JUMP_SLOT 00008380 __libc_init
Symbol table '.dynsym' contains 16 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 000083b0 32 FUNC GLOBAL DEFAULT 7 crossfunction
2: 00008450 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end
3: 00009558 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__
4: 00008368 0 FUNC GLOBAL DEFAULT UND hello
5: 00008374 0 FUNC GLOBAL DEFAULT UND puts
...
Symbol table '.symtab' contains 62 entries:
Num: Value Size Type Bind Vis Ndx Name
...
41: 000083b0 32 FUNC GLOBAL DEFAULT 7 crossfunction
42: 00008450 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end
43: 00009558 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__
44: 00008368 0 FUNC GLOBAL DEFAULT UND hello
45: 00008374 0 FUNC GLOBAL DEFAULT UND puts
...
55: 00008390 32 FUNC GLOBAL DEFAULT 7 main
...
And for libhello.so
Relocation section '.rel.plt' at offset 0xae8 contains 7 entries:
Offset Info Type Sym.Value Sym. Name
000032cc 00000516 R_ARM_JUMP_SLOT 00000000 crossfunction
000032d0 00000616 R_ARM_JUMP_SLOT 00000000 printf
000032d4 00000f16 R_ARM_JUMP_SLOT 00000000 __cxa_begin_cleanup
000032d8 00001516 R_ARM_JUMP_SLOT 00000000 memcpy
000032dc 00001f16 R_ARM_JUMP_SLOT 00000000 abort
...
Symbol table '.dynsym' contains 64 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
...
5: 00000000 0 NOTYPE GLOBAL DEFAULT UND crossfunction
6: 00000000 0 FUNC GLOBAL DEFAULT UND printf
...
19: 00000b88 100 FUNC GLOBAL DEFAULT 7 hello
21: 00000000 0 FUNC GLOBAL DEFAULT UND memcpy
...
Symbol table '.symtab' contains 138 entries:
Num: Value Size Type Bind Vis Ndx Name
25: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
79: 00000000 0 NOTYPE GLOBAL DEFAULT UND crossfunction
80: 00000000 0 FUNC GLOBAL DEFAULT UND printf
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
从版本 2.0 Android 开始,遵循 RTLD_LOCAL 共享库语义。这意味着库中的符号不可用于后续加载的库。您必须将主程序转换为 .so 库,并将其显式链接到 libhelloworld.so。有关详情,请参阅此帖子。
请注意,Android 动态链接器与 Linux 不同。请务必在不同版本的模拟器上测试您的应用程序(从 Api 3 开始),因为机制会随着时间的推移而变化。
From version 2.0 Android follows the RTLD_LOCAL shared library semantics. Meaning that the symbols in libraries are not available for subsequently loaded libraries. You have to turn your main program into a .so library and link that to libhelloworld.so explicitly. See this thread for more info.
Note that Android dynamic linker is different from the Linux one. Be sure to test your apps on emulators of different versions (starting from Api 3), as the mechanisms were changing over time.