将 C/C++ 代码与共享库混合使用会产生 "undefined symbol"
Mixing C/C++ code yields "undefined symbol" with shared library
这个问题困扰了我一个星期了,所以我想是时候向你们寻求帮助了。简而言之,故事如下:
我们正在内部使用 Qt/C++ 开发嵌入式服务器。它是一个非常简单的服务器,可以处理客户端请求并通过调用 dlopen()/dlsym() 加载适当的函数来执行特定于供应商的操作。这意味着供应商会简单地向我们提供一个 C 语言的 .so 文件,其中包含我们定义的功能(对我们来说是透明的)。这将用 C 编写,因为它需要做很多低级的事情,而我们的服务器是在 Qt 中,因为我们计划最终为它提供一个前端。
这是一些伪代码:
在我们的 main.cpp 文件中(这是用 C 语言编写的,但使用 Qt mkspec 中定义的 g++ 编译器,使用 -ldl 和 -rdynamic 编译以导出所有符号):
- dlopen() vendor.so 文件(尝试使用 RTLD_NOW 和 RTLD_LAZY)
- dlsym() vendor.so init() 方法(这将调用供应商的 init 方法,它将通过 setter 方法设置此 "vendor plugin" 的 name/properties在我们的代码中,调用此 plugin_set_name(args...))
在共享头文件 (shared.h) 中(两个代码库都会使用它;我们的会有完整的结构定义,供应商只会有 setters/getters 的原型):
- extern "C" int plugin_set_name(args...)
在供应商 main.c 文件中(使用 gcc、-fPIC 和 -shared 编译)
- 上面提到的init()函数的实现
所以本质上,发生的事情是 C++ 代码将使用 dl 调用从 C .so 库加载 init() 函数,然后 C .so 库将调用定义在C++ 代码(在本例中为 plugin_set_name)。这可能吗?两者都没有相互链接,因为它们是相互独立编译的,并且使用不同的编译器(gcc vs g++)。
我得到的错误是在运行时:"undefined symbol: plugin_set_name"(因此它可以很好地找到并进入库的 init() 方法)。当我使用 gcc 和直接的 C 代码来完成所有事情时,它可以完美地工作,所以我知道它不是代码,而是混合了 C/C++ 的东西。我还了解使用 extern "C" 来防止名称重整,并使用 nm / readelf 确定没有任何类型的重整。有任何想法吗?解决此问题的最佳方法是什么?
不知何故,这在今天神奇地起作用了。我无法解释。我只是在共享 header 周围有 extern "C" 声明,所以在 shared.h 中:
#ifdef __cplusplus
extern "C" {
#endif
plugin_set_name(args...)
other_shared_functions
#ifdef __cplusplus
}
#endif
不过我一直都有这个。在任何一种情况下,它现在都可以使用用 C 编译的供应商插件和用 Qt 和 C++ 编译的服务器。我认为问题是所有外部元素的放置位置以及 g++ 链接标志(rdynamic 至关重要)的组合。谢谢。只是把它放在这里以防其他人遇到同样的问题。
这个问题困扰了我一个星期了,所以我想是时候向你们寻求帮助了。简而言之,故事如下:
我们正在内部使用 Qt/C++ 开发嵌入式服务器。它是一个非常简单的服务器,可以处理客户端请求并通过调用 dlopen()/dlsym() 加载适当的函数来执行特定于供应商的操作。这意味着供应商会简单地向我们提供一个 C 语言的 .so 文件,其中包含我们定义的功能(对我们来说是透明的)。这将用 C 编写,因为它需要做很多低级的事情,而我们的服务器是在 Qt 中,因为我们计划最终为它提供一个前端。
这是一些伪代码:
在我们的 main.cpp 文件中(这是用 C 语言编写的,但使用 Qt mkspec 中定义的 g++ 编译器,使用 -ldl 和 -rdynamic 编译以导出所有符号):
- dlopen() vendor.so 文件(尝试使用 RTLD_NOW 和 RTLD_LAZY)
- dlsym() vendor.so init() 方法(这将调用供应商的 init 方法,它将通过 setter 方法设置此 "vendor plugin" 的 name/properties在我们的代码中,调用此 plugin_set_name(args...))
在共享头文件 (shared.h) 中(两个代码库都会使用它;我们的会有完整的结构定义,供应商只会有 setters/getters 的原型):
- extern "C" int plugin_set_name(args...)
在供应商 main.c 文件中(使用 gcc、-fPIC 和 -shared 编译)
- 上面提到的init()函数的实现
所以本质上,发生的事情是 C++ 代码将使用 dl 调用从 C .so 库加载 init() 函数,然后 C .so 库将调用定义在C++ 代码(在本例中为 plugin_set_name)。这可能吗?两者都没有相互链接,因为它们是相互独立编译的,并且使用不同的编译器(gcc vs g++)。
我得到的错误是在运行时:"undefined symbol: plugin_set_name"(因此它可以很好地找到并进入库的 init() 方法)。当我使用 gcc 和直接的 C 代码来完成所有事情时,它可以完美地工作,所以我知道它不是代码,而是混合了 C/C++ 的东西。我还了解使用 extern "C" 来防止名称重整,并使用 nm / readelf 确定没有任何类型的重整。有任何想法吗?解决此问题的最佳方法是什么?
不知何故,这在今天神奇地起作用了。我无法解释。我只是在共享 header 周围有 extern "C" 声明,所以在 shared.h 中:
#ifdef __cplusplus
extern "C" {
#endif
plugin_set_name(args...)
other_shared_functions
#ifdef __cplusplus
}
#endif
不过我一直都有这个。在任何一种情况下,它现在都可以使用用 C 编译的供应商插件和用 Qt 和 C++ 编译的服务器。我认为问题是所有外部元素的放置位置以及 g++ 链接标志(rdynamic 至关重要)的组合。谢谢。只是把它放在这里以防其他人遇到同样的问题。