拥有./library.so(rary.cpp
)和./main(main.cpp
)可执行文件,两者共享相同的api.h
。
唯一的方法(void method (void)
)只有api.h
中的签名,实际的实现位于main.cpp
。
在编译时rary.cpp
不包括方法的实际定义:在创建main.cpp
文件时从不提及library.so
。如果我通过dlopen
和dlsym
访问共享文件,那么从library.so
到方法的任何调用都会实际引用仅在主程序中实现的方法。
我没想到会发生这种情况,据我所知library.so
从未出现在方法的实现中,所以编译器(我认为)应该抱怨未实现的方法正在调用。
所以我的问题是:
<小时/>
#include "api.hpp"
#include <iostream>
#include <dlfcn.h>
using std::cout;
using std::endl;
void method (void)
{
cout << "method happened" << endl;
}
void method (int num)
{
cout << "method happened with num " << num << endl;
}
int main (void)
{
cout << "started main" << endl;
typedef void(* initfunc)();
void * handle = dlopen("./library.so", RTLD_LAZY);
initfunc func = reinterpret_cast<initfunc> (dlsym(handle, "init"));
func();
return 0;
}
#include "api.hpp"
#include <iostream>
using std::cout;
using std::endl;
extern "C" void init (void)
{
method();
method(69);
cout << "library ran" << endl;
}
void method (void);
void method (int);
project(apitest CXX)
add_executable(apitest "main.cpp")
add_library(rary MODULE "rary.cpp")
target_link_libraries(apitest dl)
cmake .
make
$ ./apitest
started core
method happened
method happened with num 69
library ran
答案 0 :(得分:2)
Q1。这是正常的吗?是主二进制文件中实现的方法,应该直接从中访问 动态加载模块只是他们的名字? (我认为不能保证如何 编译后可以在二进制文件中实际调用符号
是。默认情况下,符号在外部可见,除非另有特别标记(至少使用GCC)。
Q2。有办法防止这种情况吗?假设我正在为某人为我的插件编写插件提供API 程序,这样他们可以猜出主二进制文件中的方法名称并做一些...黑客攻击?
是的,您可以通过使用选项-fvisibility=hidden
编译程序来防止这种情况。我通过将其添加到CMakeLists.txt:
set (CMAKE_CXX_FLAGS "-fvisibility=hidden")
我更改了init函数的定义,使其标记为可见,如下所示:
extern "C" __attribute__ ((visibility ("default"))) void init (void)
我还修改了你的main.cpp以检查来自dlsym的错误(因为你的原始版本没有):
initfunc func = reinterpret_cast<initfunc> (dlsym(handle, "init"));
if (func == NULL) {
cout << dlerror() << endl;
return 1;
}
执行此操作后,您的程序将输出以下内容:
$ ./apitest
started main
./apitest: symbol lookup error: ./library.so: undefined symbol: _Z6methodv
由于init函数标记为可见,因此可以从main函数调用它。但是method
函数不是,所以你得到一个未定义的符号错误。
Q3。我可以期望这种行为在编译器和操作系统之间保持一致吗?
没有。似乎在Windows上,行为或多或少地反转 - 默认情况下,除非明确标记,否则不会导出符号。
Q4。依靠这种行为我或者是一个好习惯吗?
制作库时的最佳做法是导出构成库公共API的符号。这有很多好处:
GCC Wiki Visibility Page(我找到了此答案的大部分信息)包含大量有关此问题的信息,包括最佳做法提示。
答案 1 :(得分:1)
Linux中的共享库是正常的。
共享库与windows dll不同。在这个主题中它更像是静态库。
编译lib时,它缺少method
。如果你的主要人员没有提供它,它只是无法加载库而dlopen
将失败,你应该使用dlerror
来检查原因。
你可以尝试一下:
handle = dlopen("./library.so", RTLD_LAZY);
if (!handle) {
cerr<< dlerror()<<endl;
exit(-1);
}