库无法正确导出功能
Library not exporting function properly
我有 Windows 和 Linux 的共享库(.dll 和 .so)。除了一些用于平台差异的定义外,代码完全相同。 Windows-DLL 正常工作,所有符号都被导出并可供调用者使用。
但在 Linux 下存在问题,当尝试调用其中一个函数时,调用应用程序终止并显示 "symbol not found" 消息。令人惊奇的是,当我查看 .so-file(使用 Midnight Commander)时,我可以看到相关的符号就在那里,所以在我看来,它没有理由失败!
这是库定义的示例。
Header:
#ifdef ENV_LINUX
#ifdef MY_EXPORTS
#define MY_API __attribute ((visibility ("default")))
#else
#define MY_API
#endif
#else
#ifdef MY_EXPORTS
#define MY_API __declspec(dllexport)
#else
#define MY_API __declspec(dllimport)
#endif
#endif
#ifdef __cplusplus
extern "C"
{
#endif
MY_API unsigned char MY_set_connection(const char *address);
#ifdef __cplusplus
};
#endif // __cplusplus
和实施:
MY_API unsigned char MY_set_connection(const char *address)
{
// some code here
}
所以 header 和 C-file 中的函数定义是相同的,库是用
构建的
CCOMPILER=libtool --mode=compile g++ -Wall -Wno-unused -fPIC -shared -fvisibility=hidden $(DBGFLAGS) -DMY_EXPORTS -D_REENTRANT -DENV_LINUX -I. -I.. $(CFLAGS) $(LFLAGS)
LINK=libtool --mode=link g++ -rpath /usr/lib
所以在我看来,没有理由在运行时找不到符号。知道原因是什么吗?
您的 link 出了点问题。该符号是在编译时找到的,但在运行时找不到?您是否使用 dl_open
来运行 link 库?
我把这个简单的例子放在一起调试。
#include <iostream>
#ifdef ENV_LINUX
#ifdef MY_EXPORTS
#define MY_API __attribute ((visibility ("default")))
#else
#define MY_API
#endif
#else
#ifdef MY_EXPORTS
#define MY_API __declspec(dllexport)
#else
#define MY_API __declspec(dllimport)
#endif
#endif
#ifdef __cplusplus
extern "C"
{
#endif
MY_API unsigned char MY_set_connection(const char *address);
#ifdef __cplusplus
};
#endif // __cplusplus
unsigned char MY_set_connection(const char* address)
{
std::cout << "found it" << std::endl;
return 0;
}
然后编译查看符号
/tmp$ g++ -fPIC -g -std=c++11 -shared -fvisibility=hidden -DMY_EXPORTS -D_REENTRANT -DENV_LINUX -o libtest_library.so lib.cpp
/tmp$ nm -D libtest_library.so | grep MY_set_connection
00000000000007c5 T MY_set_connection
现在是一个简单的 main
#ifdef __cplusplus
extern "C"
{
#endif
unsigned char MY_set_connection(const char *address);
#ifdef __cplusplus
};
#endif // __cplusplus
int main(int argc, char* argv[])
{
unsigned char rc = MY_set_connection("test");
return 0;
}
编译并检查符号依赖性
/tmp$ g++ -g -std=c++11 junk.cpp -DENV_LINUX -L . -l test_library
/tmp$ g++ -g junk.cpp -DENV_LINUX -L . -l test_library
/tmp$ nm a.out | grep MY_
U MY_set_connection
所以 MY_set_connection 是 'U' ,这是未定义的,正如预期的那样。符号是 'T',这是文本部分和外部,也是预期的。 运行 该程序按预期运行。用 nm 查看你的符号依赖关系,也许符号状态是错误的。
另一种可能是库或主程序中的 be extern C 签名错误。确保符号在任何一个 nm 输出中都没有被破坏
查看 ldd 输出以确保您 link 在运行时针对正确的库。运行时 link针对错误版本的共享库将给出未定义的符号。
ldd ./a.out
libtest_library.so => /tmp/libtest_library.so (0x00002b5df4ca7000)
我有 Windows 和 Linux 的共享库(.dll 和 .so)。除了一些用于平台差异的定义外,代码完全相同。 Windows-DLL 正常工作,所有符号都被导出并可供调用者使用。
但在 Linux 下存在问题,当尝试调用其中一个函数时,调用应用程序终止并显示 "symbol not found" 消息。令人惊奇的是,当我查看 .so-file(使用 Midnight Commander)时,我可以看到相关的符号就在那里,所以在我看来,它没有理由失败!
这是库定义的示例。
Header:
#ifdef ENV_LINUX
#ifdef MY_EXPORTS
#define MY_API __attribute ((visibility ("default")))
#else
#define MY_API
#endif
#else
#ifdef MY_EXPORTS
#define MY_API __declspec(dllexport)
#else
#define MY_API __declspec(dllimport)
#endif
#endif
#ifdef __cplusplus
extern "C"
{
#endif
MY_API unsigned char MY_set_connection(const char *address);
#ifdef __cplusplus
};
#endif // __cplusplus
和实施:
MY_API unsigned char MY_set_connection(const char *address)
{
// some code here
}
所以 header 和 C-file 中的函数定义是相同的,库是用
构建的CCOMPILER=libtool --mode=compile g++ -Wall -Wno-unused -fPIC -shared -fvisibility=hidden $(DBGFLAGS) -DMY_EXPORTS -D_REENTRANT -DENV_LINUX -I. -I.. $(CFLAGS) $(LFLAGS)
LINK=libtool --mode=link g++ -rpath /usr/lib
所以在我看来,没有理由在运行时找不到符号。知道原因是什么吗?
您的 link 出了点问题。该符号是在编译时找到的,但在运行时找不到?您是否使用 dl_open
来运行 link 库?
我把这个简单的例子放在一起调试。
#include <iostream>
#ifdef ENV_LINUX
#ifdef MY_EXPORTS
#define MY_API __attribute ((visibility ("default")))
#else
#define MY_API
#endif
#else
#ifdef MY_EXPORTS
#define MY_API __declspec(dllexport)
#else
#define MY_API __declspec(dllimport)
#endif
#endif
#ifdef __cplusplus
extern "C"
{
#endif
MY_API unsigned char MY_set_connection(const char *address);
#ifdef __cplusplus
};
#endif // __cplusplus
unsigned char MY_set_connection(const char* address)
{
std::cout << "found it" << std::endl;
return 0;
}
然后编译查看符号
/tmp$ g++ -fPIC -g -std=c++11 -shared -fvisibility=hidden -DMY_EXPORTS -D_REENTRANT -DENV_LINUX -o libtest_library.so lib.cpp
/tmp$ nm -D libtest_library.so | grep MY_set_connection
00000000000007c5 T MY_set_connection
现在是一个简单的 main
#ifdef __cplusplus
extern "C"
{
#endif
unsigned char MY_set_connection(const char *address);
#ifdef __cplusplus
};
#endif // __cplusplus
int main(int argc, char* argv[])
{
unsigned char rc = MY_set_connection("test");
return 0;
}
编译并检查符号依赖性
/tmp$ g++ -g -std=c++11 junk.cpp -DENV_LINUX -L . -l test_library
/tmp$ g++ -g junk.cpp -DENV_LINUX -L . -l test_library
/tmp$ nm a.out | grep MY_
U MY_set_connection
所以 MY_set_connection 是 'U' ,这是未定义的,正如预期的那样。符号是 'T',这是文本部分和外部,也是预期的。 运行 该程序按预期运行。用 nm 查看你的符号依赖关系,也许符号状态是错误的。
另一种可能是库或主程序中的 be extern C 签名错误。确保符号在任何一个 nm 输出中都没有被破坏
查看 ldd 输出以确保您 link 在运行时针对正确的库。运行时 link针对错误版本的共享库将给出未定义的符号。
ldd ./a.out
libtest_library.so => /tmp/libtest_library.so (0x00002b5df4ca7000)