【发布时间】:2021-11-19 13:56:02
【问题描述】:
我正在编写一个程序(macOS,clang++ 编译器,目前只有 AppleSilicon),稍后我可以通过提供使用主程序公共接口的自定义插件(动态库,在运行时加载)来扩展它。
test.hpp - 公共接口:
#if defined(MAGIC_PLUGIN)
# define MAGIC_EXPORT /* nothing */
#else
# define MAGIC_EXPORT __attribute__((visibility("default")))
#endif
MAGIC_EXPORT
void testCall();
test.cpp - 主程序:
#include <stdio.h>
#include <dlfcn.h>
#include "test.hpp"
// Declare a function to call from a loaded plugin
typedef void (* plugin_call_func)(void);
int main(int argc, char** argv) {
// Load library
const char* libraryName = "plugin.dylib";
void* library = dlopen(libraryName, RTLD_NOW);
if (library == nullptr) {
printf("Cannot open library\n");
return 1;
}
// Get function from loaded library
plugin_call_func pluginCall = reinterpret_cast<plugin_call_func>(
dlsym(library, "pluginCall"));
if (pluginCall == nullptr) {
printf("Cannot find the pluginCall function\n");
return 2;
}
// Execute loaded function
pluginCall();
// Forget function and close library
pluginCall = nullptr;
auto libraryCloseResult = dlclose(library);
if (libraryCloseResult != 0) {
printf("Cannot close library\n");
return 3;
}
return 0;
}
// Public function, should be called from a plugin
void testCall() {
printf("Test call\n");
}
plugin.cpp - 插件的来源:
#define MAGIC_PLUGIN
#include <stdio.h>
#include "test.hpp"
__attribute__((visibility("default")))
extern "C" void pluginCall(void) {
printf("Plugin call\n");
testCall();
}
首先,我编译主应用程序:
clang++ -std=c++20 -fvisibility=hidden -target arm64-apple-macos12 test.cpp -o test
nm --defined-only test 显示这些符号:
0000000100003ee4 T __Z8testCallv
0000000100000000 T __mh_execute_header
0000000100003dcc t _main
Mangled __Z8testCallv 是我需要的。到目前为止一切看起来都不错。但是后来我尝试将插件编译为动态库...
clang++ -std=c++20 -fvisibility=hidden -dynamiclib -g -current_version 0.1 -target arm64-apple-macos12 plugin.cpp -o plugin.dylib
并得到这个错误:
Undefined symbols for architecture arm64:
"testCall()", referenced from:
_pluginCall in plugin-38422c.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
好吧,这有点公平,我可以理解这一点,因为动态库不知道testCall 在某处实现。所以我想说它不用担心testCall的存在。
我试图研究如何做到这一点,查阅手册页,阅读大量堆栈溢出答案,我发现唯一可行的是将这些标志添加到链接器:
-Wl,-undefined,dynamic_lookup
它工作,库编译并且应用程序按预期工作。但我真的不想使用dynamic_lookup,因为它会将库中的每个未定义符号标记为已解决,这可能会导致一些不良后果。我只想告诉链接器主程序的公共符号的存在。
我错过了什么?有没有比dynamic_lookup更好的解决方案?
【问题讨论】:
-
加载后,我会亲自手动交出指向插件的函数指针表。这将依赖图保持为 DAG,通常更易于使用。
-
当你将插件编译为动态库时,你只是在编译
plugin.cpp..那么test.cpp呢? -
可以解决,如果您将
main与标志-rdynamic链接并在链接plugin.dylib时允许未定义的符号(或者如果您将main作为依赖项添加到plugin.dylib中--导致另一个问题。)最好的选择是遵循@Frank的建议。