在 运行 时间加载动态库会产生不一致和意外的结果、缺少符号和空 PLT 条目。为什么?
Loading a dynamic library at run-time yields inconsistent and unexpected results, missing symbols and empty PLT entries. Why?
我已经和这个问题斗争了很长一段时间,但一直找不到解决方案,甚至找不到解释。很抱歉,如果问题很长,但请耐心等待,因为我只是想 100% 清楚地说明问题,希望比我更有经验的人能够弄明白。
我为所有片段保留了 C 语法高亮显示,因为它使它们更清晰一点,即使不是真的正确。
我想做什么
我有一个 C 程序,它使用动态库 (libzip
) 中的一些函数。在这里它被归结为一个最小的可重现示例(它基本上什么都不做,但它工作得很好):
#include <zip.h>
int main(void) {
int err;
zip_t *myzip;
myzip = zip_open("myzip.zip", ZIP_CREATE | ZIP_TRUNCATE, &err);
if (myzip == NULL)
return 1;
zip_close(myzip);
return 0;
}
通常,要编译它,我会简单地做:
gcc -c prog.c
gcc -o prog prog.o -lzip
正如预期的那样,这将创建一个需要 libzip
到 运行 的 ELF:
$ ldd prog
linux-vdso.so.1 (0x00007ffdafb53000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f81eedc7000)
/lib64/ld-linux-x86-64.so.2 (0x00007f81ef780000)
libzip.so.4 => /usr/lib/x86_64-linux-gnu/libzip.so.4 (0x00007f81ef166000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f81eebad000)
(libz
只是 libzip
的依赖项)
我真正想做的是自己使用dlopen()
加载库。很简单的任务,不是吗?嗯,是的,或者至少我是这么认为的。
要实现这一点,我只需要调用 dlopen
并让加载器完成它的工作:
#include <zip.h>
#include <dlfcn.h>
int main(void) {
void *lib;
int err;
zip_t *myzip;
lib = dlopen("libzip.so", RTLD_LAZY | RTLD_GLOBAL);
if (lib == NULL)
return 1;
myzip = zip_open("myzip.zip", ZIP_CREATE | ZIP_TRUNCATE, &err);
if (myzip == NULL)
return 1;
zip_close(myzip);
return 0;
}
当然,既然要自己手动加载库,这次就不link了:
# Create prog.o
gcc -c prog.c
# Do a dry-run just to make sure all symbols are resolved
gcc -o /dev/null prog.o -ldl -lzip
# Now recompile only with libdl
gcc -o prog prog.o -ldl -Wl,--unresolved-symbols=ignore-in-object-files
标志 --unresolved-symbols=ignore-in-object-files
告诉 ld
不要担心我的 prog.o
在 link 时间有未解析的符号(我想在 运行时间).
问题
上面的应该可以工作™,确实它看起来确实......但是我有两台机器,作为一个迂腐的书呆子我只是想"well, better make sure and compile it on both of them".
第一台机器
x86-64,Linux 4.9,Debian 9,gcc
6.3.0,ld
2.28。这里 一切都按预期工作。
我可以清楚地看到符号在那里:
$ readelf --dyn-syms prog
Symbol table '.dynsym' contains 15 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
===> 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND zip_close
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dlopen@GLIBC_2.2.5 (3)
===> 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND zip_open
7: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
9: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
10: 0000000000201040 0 NOTYPE GLOBAL DEFAULT 25 _edata
11: 0000000000201048 0 NOTYPE GLOBAL DEFAULT 26 _end
12: 0000000000201040 0 NOTYPE GLOBAL DEFAULT 26 __bss_start
13: 00000000000006a0 0 FUNC GLOBAL DEFAULT 11 _init
14: 0000000000000924 0 FUNC GLOBAL DEFAULT 15 _fini
PLT 条目也如预期的那样存在并且看起来不错:
$ objdump -j .plt -M intel -d prog
Disassembly of section .plt:
00000000000006c0 <.plt>:
6c0: ff 35 42 09 20 00 push QWORD PTR [rip+0x200942] # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
6c6: ff 25 44 09 20 00 jmp QWORD PTR [rip+0x200944] # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
6cc: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
00000000000006d0 <zip_close@plt>:
6d0: ff 25 42 09 20 00 jmp QWORD PTR [rip+0x200942] # 201018 <zip_close>
6d6: 68 00 00 00 00 push 0x0
6db: e9 e0 ff ff ff jmp 6c0 <.plt>
00000000000006e0 <dlopen@plt>:
6e0: ff 25 3a 09 20 00 jmp QWORD PTR [rip+0x20093a] # 201020 <dlopen@GLIBC_2.2.5>
6e6: 68 01 00 00 00 push 0x1
6eb: e9 d0 ff ff ff jmp 6c0 <.plt>
00000000000006f0 <zip_open@plt>:
6f0: ff 25 32 09 20 00 jmp QWORD PTR [rip+0x200932] # 201028 <zip_open>
6f6: 68 02 00 00 00 push 0x2
6fb: e9 c0 ff ff ff jmp 6c0 <.plt>
程序运行没有任何问题:
$ ./prog
$ echo $?
0
即使使用调试器查看它的内部,我也可以清楚地看到符号像任何普通动态符号一样得到正确解析:
0x55555555479b <main+43> lea rax, [rbp - 0x14]
0x55555555479f <main+47> mov rdx, rax
0x5555555547a2 <main+50> mov esi, 9
0x5555555547a7 <main+55> lea rdi, [rip + 0xc0] <0x7ffff7ffd948>
0x5555555547ae <main+62> call zip_open@plt <0x555555554620>
|
v ### PLT entry:
0x555555554620 <zip_open@plt> jmp qword ptr [rip + 0x200a02] <0x555555755028>
|
v
0x555555554626 <zip_open@plt+6> push 2
0x55555555462b <zip_open@plt+11> jmp 0x5555555545f0
|
v ### PLT stub:
0x5555555545f0 push qword ptr [rip + 0x200a12] <0x555555755008>
0x5555555545f6 jmp qword ptr [rip + 0x200a14] <0x7ffff7def0d0>
|
v ### Symbol gets correctly resolved
0x7ffff7def0d0 <_dl_runtime_resolve_fxsave> push rbx
0x7ffff7def0d1 <_dl_runtime_resolve_fxsave+1> mov rbx, rsp
0x7ffff7def0d4 <_dl_runtime_resolve_fxsave+4> and rsp, 0xfffffffffffffff0
0x7ffff7def0d8 <_dl_runtime_resolve_fxsave+8> sub rsp, 0x240
第二台机器
x86-64,Linux 4.15,Ubuntu 18.04,gcc
7.4,ld
2.30。这里,发生了一些非常奇怪的事情。
编译没有产生任何警告或错误,但是我没有看到符号:
$ readelf --dyn-syms prog
Symbol table '.dynsym' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dlopen@GLIBC_2.2.5 (3)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
那里有 PLT 条目 ,但它们被零填充,甚至无法被 objdump
识别:
$ objdump -j .plt -M intel -d prog
Disassembly of section .plt:
0000000000000560 <.plt>:
560: ff 35 4a 0a 20 00 push QWORD PTR [rip+0x200a4a] # 200fb0 <_GLOBAL_OFFSET_TABLE_+0x8>
566: ff 25 4c 0a 20 00 jmp QWORD PTR [rip+0x200a4c] # 200fb8 <_GLOBAL_OFFSET_TABLE_+0x10>
56c: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
...
# ^^^
# Here, these three dots are actually hiding another 0x10+ bytes filled of 0x0
# zip_close@plt should be here instead...
0000000000000580 <dlopen@plt>:
580: ff 25 42 0a 20 00 jmp QWORD PTR [rip+0x200a42] # 200fc8 <dlopen@GLIBC_2.2.5>
586: 68 00 00 00 00 push 0x0
58b: e9 d0 ff ff ff jmp 560 <.plt>
...
# ^^^
# Here, these three dots are actually hiding another 0x10+ bytes filled of 0x0
# zip_open@plt should be here instead...
当程序 运行 时,dlopen()
工作正常并将 libzip
加载到内存中,但是当 zip_open()
被调用时,它只会生成一个分段错误:
$ ./prog
Segmentation fault (code dumped)
使用调试器查看,问题更加明显(以防还不够明显)。填充为零的 PLT 条目最终解码为一堆 add
取消引用 rax
的指令,其中包含无效地址并使程序出现段错误并死亡:
0x5555555546e5 <main+43> lea rax, [rbp - 0x14]
0x5555555546e9 <main+47> mov rdx, rax
0x5555555546ec <main+50> mov esi, 9
0x5555555546f1 <main+55> lea rdi, [rip + 0xc6]
0x5555555546f8 <main+62> call dlopen@plt+16 <0x555555554590>
|
v ### Broken PLT enrty (all 0x0, will cause a segfault):
0x555555554590 <dlopen@plt+16> add byte ptr [rax], al
0x555555554592 <dlopen@plt+18> add byte ptr [rax], al
0x555555554594 <dlopen@plt+20> add byte ptr [rax], al
0x555555554596 <dlopen@plt+22> add byte ptr [rax], al
0x555555554598 <dlopen@plt+24> add byte ptr [rax], al
0x55555555459a <dlopen@plt+26> add byte ptr [rax], al
0x55555555459c <dlopen@plt+28> add byte ptr [rax], al
0x55555555459e <dlopen@plt+30> add byte ptr [rax], al
### Next PLT entry...
0x5555555545a0 <__cxa_finalize@plt> jmp qword ptr [rip + 0x200a52] <0x7ffff7823520>
|
v
0x7ffff7823520 <__cxa_finalize> push r15
0x7ffff7823522 <__cxa_finalize+2> push r14
问题
- 所以,首先...为什么会这样?
- 我认为这应该有效,不是吗?如果不是,为什么?为什么只在两台机器中的一台上?
- 但最重要的是:我该如何解决这个问题?
对于问题 3,我想强调的是,我想自己加载库,而不是 linking 它,所以请不要仅仅评论这是不好的做法,或者其他什么。
The above Should Just Work™, and indeed it does seem to...
不,它不应该,如果它看起来像,那更像是一个意外。一般来说,使用 --unresolved-symbols=...
是一个非常糟糕的主意™,而且几乎永远不会如你所愿。
解决方案很简单:您只需要查找 zip_open
和 zip_close
,如下所示:
int main(void) {
void *lib;
zip_t *p_open(const char *, int, int *);
void *p_close(zip_t*);
int err;
zip_t *myzip;
lib = dlopen("libzip.so", RTLD_LAZY | RTLD_GLOBAL);
if (lib == NULL)
return 1;
p_open = (zip_t(*)(const char *, int, int *))dlsym(lib, "zip_open");
if (p_open == NULL)
return 1;
p_close = (void(*)(zip_t*))dlsym(lib, "zip_close");
if (p_close == NULL)
return 1;
myzip = p_open("myzip.zip", ZIP_CREATE | ZIP_TRUNCATE, &err);
if (myzip == NULL)
return 1;
p_close(myzip);
return 0;
}
要添加到 EmployedRussian 的答案中,您可以借助 Implib.so 工具实现您需要的内容。它将为所有库符号(例如 zip_open
)生成存根,这些存根将在内部调用 dlopen
/dlsym
并将调用从您的程序转发到共享库:
$ gcc -c prog.c
$ implib-gen.py path/to/libzip.so
$ gcc -o prog prog.o libzip.tramp.S libzip.init.c -ldl
(请注意,您不再需要花哨的链接器标志和链接器空运行)。
附带说明一下,您尝试做的事情称为延迟加载,是 Windows DLLS 的 standard feature。
我已经和这个问题斗争了很长一段时间,但一直找不到解决方案,甚至找不到解释。很抱歉,如果问题很长,但请耐心等待,因为我只是想 100% 清楚地说明问题,希望比我更有经验的人能够弄明白。
我为所有片段保留了 C 语法高亮显示,因为它使它们更清晰一点,即使不是真的正确。
我想做什么
我有一个 C 程序,它使用动态库 (libzip
) 中的一些函数。在这里它被归结为一个最小的可重现示例(它基本上什么都不做,但它工作得很好):
#include <zip.h>
int main(void) {
int err;
zip_t *myzip;
myzip = zip_open("myzip.zip", ZIP_CREATE | ZIP_TRUNCATE, &err);
if (myzip == NULL)
return 1;
zip_close(myzip);
return 0;
}
通常,要编译它,我会简单地做:
gcc -c prog.c
gcc -o prog prog.o -lzip
正如预期的那样,这将创建一个需要 libzip
到 运行 的 ELF:
$ ldd prog
linux-vdso.so.1 (0x00007ffdafb53000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f81eedc7000)
/lib64/ld-linux-x86-64.so.2 (0x00007f81ef780000)
libzip.so.4 => /usr/lib/x86_64-linux-gnu/libzip.so.4 (0x00007f81ef166000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f81eebad000)
(libz
只是 libzip
的依赖项)
我真正想做的是自己使用dlopen()
加载库。很简单的任务,不是吗?嗯,是的,或者至少我是这么认为的。
要实现这一点,我只需要调用 dlopen
并让加载器完成它的工作:
#include <zip.h>
#include <dlfcn.h>
int main(void) {
void *lib;
int err;
zip_t *myzip;
lib = dlopen("libzip.so", RTLD_LAZY | RTLD_GLOBAL);
if (lib == NULL)
return 1;
myzip = zip_open("myzip.zip", ZIP_CREATE | ZIP_TRUNCATE, &err);
if (myzip == NULL)
return 1;
zip_close(myzip);
return 0;
}
当然,既然要自己手动加载库,这次就不link了:
# Create prog.o
gcc -c prog.c
# Do a dry-run just to make sure all symbols are resolved
gcc -o /dev/null prog.o -ldl -lzip
# Now recompile only with libdl
gcc -o prog prog.o -ldl -Wl,--unresolved-symbols=ignore-in-object-files
标志 --unresolved-symbols=ignore-in-object-files
告诉 ld
不要担心我的 prog.o
在 link 时间有未解析的符号(我想在 运行时间).
问题
上面的应该可以工作™,确实它看起来确实......但是我有两台机器,作为一个迂腐的书呆子我只是想"well, better make sure and compile it on both of them".
第一台机器
x86-64,Linux 4.9,Debian 9,gcc
6.3.0,ld
2.28。这里 一切都按预期工作。
我可以清楚地看到符号在那里:
$ readelf --dyn-syms prog
Symbol table '.dynsym' contains 15 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
===> 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND zip_close
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dlopen@GLIBC_2.2.5 (3)
===> 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND zip_open
7: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
9: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
10: 0000000000201040 0 NOTYPE GLOBAL DEFAULT 25 _edata
11: 0000000000201048 0 NOTYPE GLOBAL DEFAULT 26 _end
12: 0000000000201040 0 NOTYPE GLOBAL DEFAULT 26 __bss_start
13: 00000000000006a0 0 FUNC GLOBAL DEFAULT 11 _init
14: 0000000000000924 0 FUNC GLOBAL DEFAULT 15 _fini
PLT 条目也如预期的那样存在并且看起来不错:
$ objdump -j .plt -M intel -d prog
Disassembly of section .plt:
00000000000006c0 <.plt>:
6c0: ff 35 42 09 20 00 push QWORD PTR [rip+0x200942] # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
6c6: ff 25 44 09 20 00 jmp QWORD PTR [rip+0x200944] # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
6cc: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
00000000000006d0 <zip_close@plt>:
6d0: ff 25 42 09 20 00 jmp QWORD PTR [rip+0x200942] # 201018 <zip_close>
6d6: 68 00 00 00 00 push 0x0
6db: e9 e0 ff ff ff jmp 6c0 <.plt>
00000000000006e0 <dlopen@plt>:
6e0: ff 25 3a 09 20 00 jmp QWORD PTR [rip+0x20093a] # 201020 <dlopen@GLIBC_2.2.5>
6e6: 68 01 00 00 00 push 0x1
6eb: e9 d0 ff ff ff jmp 6c0 <.plt>
00000000000006f0 <zip_open@plt>:
6f0: ff 25 32 09 20 00 jmp QWORD PTR [rip+0x200932] # 201028 <zip_open>
6f6: 68 02 00 00 00 push 0x2
6fb: e9 c0 ff ff ff jmp 6c0 <.plt>
程序运行没有任何问题:
$ ./prog
$ echo $?
0
即使使用调试器查看它的内部,我也可以清楚地看到符号像任何普通动态符号一样得到正确解析:
0x55555555479b <main+43> lea rax, [rbp - 0x14]
0x55555555479f <main+47> mov rdx, rax
0x5555555547a2 <main+50> mov esi, 9
0x5555555547a7 <main+55> lea rdi, [rip + 0xc0] <0x7ffff7ffd948>
0x5555555547ae <main+62> call zip_open@plt <0x555555554620>
|
v ### PLT entry:
0x555555554620 <zip_open@plt> jmp qword ptr [rip + 0x200a02] <0x555555755028>
|
v
0x555555554626 <zip_open@plt+6> push 2
0x55555555462b <zip_open@plt+11> jmp 0x5555555545f0
|
v ### PLT stub:
0x5555555545f0 push qword ptr [rip + 0x200a12] <0x555555755008>
0x5555555545f6 jmp qword ptr [rip + 0x200a14] <0x7ffff7def0d0>
|
v ### Symbol gets correctly resolved
0x7ffff7def0d0 <_dl_runtime_resolve_fxsave> push rbx
0x7ffff7def0d1 <_dl_runtime_resolve_fxsave+1> mov rbx, rsp
0x7ffff7def0d4 <_dl_runtime_resolve_fxsave+4> and rsp, 0xfffffffffffffff0
0x7ffff7def0d8 <_dl_runtime_resolve_fxsave+8> sub rsp, 0x240
第二台机器
x86-64,Linux 4.15,Ubuntu 18.04,gcc
7.4,ld
2.30。这里,发生了一些非常奇怪的事情。
编译没有产生任何警告或错误,但是我没有看到符号:
$ readelf --dyn-syms prog
Symbol table '.dynsym' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dlopen@GLIBC_2.2.5 (3)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
那里有 PLT 条目 ,但它们被零填充,甚至无法被 objdump
识别:
$ objdump -j .plt -M intel -d prog
Disassembly of section .plt:
0000000000000560 <.plt>:
560: ff 35 4a 0a 20 00 push QWORD PTR [rip+0x200a4a] # 200fb0 <_GLOBAL_OFFSET_TABLE_+0x8>
566: ff 25 4c 0a 20 00 jmp QWORD PTR [rip+0x200a4c] # 200fb8 <_GLOBAL_OFFSET_TABLE_+0x10>
56c: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
...
# ^^^
# Here, these three dots are actually hiding another 0x10+ bytes filled of 0x0
# zip_close@plt should be here instead...
0000000000000580 <dlopen@plt>:
580: ff 25 42 0a 20 00 jmp QWORD PTR [rip+0x200a42] # 200fc8 <dlopen@GLIBC_2.2.5>
586: 68 00 00 00 00 push 0x0
58b: e9 d0 ff ff ff jmp 560 <.plt>
...
# ^^^
# Here, these three dots are actually hiding another 0x10+ bytes filled of 0x0
# zip_open@plt should be here instead...
当程序 运行 时,dlopen()
工作正常并将 libzip
加载到内存中,但是当 zip_open()
被调用时,它只会生成一个分段错误:
$ ./prog
Segmentation fault (code dumped)
使用调试器查看,问题更加明显(以防还不够明显)。填充为零的 PLT 条目最终解码为一堆 add
取消引用 rax
的指令,其中包含无效地址并使程序出现段错误并死亡:
0x5555555546e5 <main+43> lea rax, [rbp - 0x14]
0x5555555546e9 <main+47> mov rdx, rax
0x5555555546ec <main+50> mov esi, 9
0x5555555546f1 <main+55> lea rdi, [rip + 0xc6]
0x5555555546f8 <main+62> call dlopen@plt+16 <0x555555554590>
|
v ### Broken PLT enrty (all 0x0, will cause a segfault):
0x555555554590 <dlopen@plt+16> add byte ptr [rax], al
0x555555554592 <dlopen@plt+18> add byte ptr [rax], al
0x555555554594 <dlopen@plt+20> add byte ptr [rax], al
0x555555554596 <dlopen@plt+22> add byte ptr [rax], al
0x555555554598 <dlopen@plt+24> add byte ptr [rax], al
0x55555555459a <dlopen@plt+26> add byte ptr [rax], al
0x55555555459c <dlopen@plt+28> add byte ptr [rax], al
0x55555555459e <dlopen@plt+30> add byte ptr [rax], al
### Next PLT entry...
0x5555555545a0 <__cxa_finalize@plt> jmp qword ptr [rip + 0x200a52] <0x7ffff7823520>
|
v
0x7ffff7823520 <__cxa_finalize> push r15
0x7ffff7823522 <__cxa_finalize+2> push r14
问题
- 所以,首先...为什么会这样?
- 我认为这应该有效,不是吗?如果不是,为什么?为什么只在两台机器中的一台上?
- 但最重要的是:我该如何解决这个问题?
对于问题 3,我想强调的是,我想自己加载库,而不是 linking 它,所以请不要仅仅评论这是不好的做法,或者其他什么。
The above Should Just Work™, and indeed it does seem to...
不,它不应该,如果它看起来像,那更像是一个意外。一般来说,使用 --unresolved-symbols=...
是一个非常糟糕的主意™,而且几乎永远不会如你所愿。
解决方案很简单:您只需要查找 zip_open
和 zip_close
,如下所示:
int main(void) {
void *lib;
zip_t *p_open(const char *, int, int *);
void *p_close(zip_t*);
int err;
zip_t *myzip;
lib = dlopen("libzip.so", RTLD_LAZY | RTLD_GLOBAL);
if (lib == NULL)
return 1;
p_open = (zip_t(*)(const char *, int, int *))dlsym(lib, "zip_open");
if (p_open == NULL)
return 1;
p_close = (void(*)(zip_t*))dlsym(lib, "zip_close");
if (p_close == NULL)
return 1;
myzip = p_open("myzip.zip", ZIP_CREATE | ZIP_TRUNCATE, &err);
if (myzip == NULL)
return 1;
p_close(myzip);
return 0;
}
要添加到 EmployedRussian 的答案中,您可以借助 Implib.so 工具实现您需要的内容。它将为所有库符号(例如 zip_open
)生成存根,这些存根将在内部调用 dlopen
/dlsym
并将调用从您的程序转发到共享库:
$ gcc -c prog.c
$ implib-gen.py path/to/libzip.so
$ gcc -o prog prog.o libzip.tramp.S libzip.init.c -ldl
(请注意,您不再需要花哨的链接器标志和链接器空运行)。
附带说明一下,您尝试做的事情称为延迟加载,是 Windows DLLS 的 standard feature。