使用 dlopen/dlsym 打开 C++ 共享库 - dlsym returns NULL
Using dlopen/dlsym to open C++ shared library - dlsym returns NULL
我还没有处理过 C++ 中的共享库,遇到了一些麻烦。我想创建一个共享库,然后在该库上获取一个 C 函数。所以这是我的共享库文件:
extern int nothing();
//sym.cpp
int nothing() {
return 0;
}
下面是我的 dlopen/dlsym 脚本:
//symtest.c
#include <stdio.h>
#include <dlfcn.h>
int main(){
void *handle;
handle = dlopen("/path/to/lib/sym.so",RTLD_NOW);
int (*onload)(void *, void **, int);
onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,"nothing");
if(onload==NULL) {
printf("NULL");
}
return 0;
}
编译并运行如下:
$ g++ -shared -fPIC -o sym.so sym.cpp
$ gcc symtest.c -ldl -o symtest
$ ./symtest
NULL
为什么我得到的是 NULL?我很确定这个符号正在导出,至少通过观察以下命令的输出。
纳米:
$ nm -CD sym.so | grep " T "
0000000000000670 T nothing()
000000000000067c T _fini
0000000000000518 T _init
objdump:
$ objdump -T sym.so
sym.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000518 l d .init 0000000000000000 .init
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize
0000000000200970 g D .bss 0000000000000000 Base _end
0000000000200968 g D .got.plt 0000000000000000 Base _edata
0000000000200968 g D .bss 0000000000000000 Base __bss_start
0000000000000518 g DF .init 0000000000000000 Base _init
000000000000067c g DF .fini 0000000000000000 Base _fini
0000000000000670 g DF .text 000000000000000b Base _Z7nothingv
这里有一个更大的图景 (),但我查看了一些 Redis 源代码以生成一个简约示例。有人知道我在这里做错了什么吗?
根据要求,nm 没有 -C 选项:
$ nm -D sym.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000000670 T _Z7nothingv
0000000000200968 B __bss_start
w __cxa_finalize
w __gmon_start__
0000000000200968 D _edata
0000000000200970 B _end
000000000000067c T _fini
0000000000000518 T _init
C++ 具有函数重载功能,因此可以有多个具有相同名称和不同参数的函数。
由于目标文件仅存储名称,因此 C++ 应用了所谓的名称修改。它添加了额外的符号来表示电子邮件参数名称以区分不同的版本。
使用 dlsym 时,必须使用经过修饰的名称才能获取函数地址。
现在,由于名称修改是特定于平台的,因此通常最好使用 C 链接(没有名称修改)
这可以通过 extern "C"
声明来完成。
我还没有处理过 C++ 中的共享库,遇到了一些麻烦。我想创建一个共享库,然后在该库上获取一个 C 函数。所以这是我的共享库文件:
extern int nothing();
//sym.cpp
int nothing() {
return 0;
}
下面是我的 dlopen/dlsym 脚本:
//symtest.c
#include <stdio.h>
#include <dlfcn.h>
int main(){
void *handle;
handle = dlopen("/path/to/lib/sym.so",RTLD_NOW);
int (*onload)(void *, void **, int);
onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,"nothing");
if(onload==NULL) {
printf("NULL");
}
return 0;
}
编译并运行如下:
$ g++ -shared -fPIC -o sym.so sym.cpp
$ gcc symtest.c -ldl -o symtest
$ ./symtest
NULL
为什么我得到的是 NULL?我很确定这个符号正在导出,至少通过观察以下命令的输出。
纳米:
$ nm -CD sym.so | grep " T "
0000000000000670 T nothing()
000000000000067c T _fini
0000000000000518 T _init
objdump:
$ objdump -T sym.so
sym.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000518 l d .init 0000000000000000 .init
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize
0000000000200970 g D .bss 0000000000000000 Base _end
0000000000200968 g D .got.plt 0000000000000000 Base _edata
0000000000200968 g D .bss 0000000000000000 Base __bss_start
0000000000000518 g DF .init 0000000000000000 Base _init
000000000000067c g DF .fini 0000000000000000 Base _fini
0000000000000670 g DF .text 000000000000000b Base _Z7nothingv
这里有一个更大的图景 (
根据要求,nm 没有 -C 选项:
$ nm -D sym.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000000670 T _Z7nothingv
0000000000200968 B __bss_start
w __cxa_finalize
w __gmon_start__
0000000000200968 D _edata
0000000000200970 B _end
000000000000067c T _fini
0000000000000518 T _init
C++ 具有函数重载功能,因此可以有多个具有相同名称和不同参数的函数。
由于目标文件仅存储名称,因此 C++ 应用了所谓的名称修改。它添加了额外的符号来表示电子邮件参数名称以区分不同的版本。
使用 dlsym 时,必须使用经过修饰的名称才能获取函数地址。
现在,由于名称修改是特定于平台的,因此通常最好使用 C 链接(没有名称修改)
这可以通过 extern "C"
声明来完成。