尽管找到库并导出符号,仍出现ld未定义引用的问题。

4

我已经花费了48个小时在这个问题上,但仍然无法解决,在尝试将动态库与其依赖项链接时,我仍然会收到未定义的引用错误,尽管所有导出项都存在,并且成功找到库。

场景:

  • libmemory(C++)-使用extern"C"导出函数
  • libstring(C)-导出函数,从libmemory导入

libmemory成功构建:

$ g++ -shared -fPIC -o ./builds/libmemory.so ...$(OBJECTS)...

libstring编译成功,但链接失败:

$ gcc -shared -fPIC -o ./builds/libstring.so ...$(OBJECTS)... -L./builds -lmemory
./temp/libstring/string.o: In function `STR_duplicate':
string.c:(.text+0x1cb): undefined reference to `MEM_priv_alloc'
./temp/libstring/string.o: In function `STR_duplicate_replace':
string.c:(.text+0x2a0): undefined reference to `MEM_priv_free'
string.c:(.text+0x2bf): undefined reference to `MEM_priv_alloc'
/usr/bin/ld: ./builds/libstring.so: hidden symbol `MEM_priv_free' isn't defined
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status

验证libmemory导出其符号,并且使用-v选项在gcc中可以找到该库:

...
attempt to open ./builds/libmemory.so succeeded
-lmemory (./builds/libmemory.so)
...

$ nm -gC ./builds/libmemory.so | grep MEM_
0000000000009178 T MEM_exit
0000000000009343 T MEM_init
00000000000093e9 T MEM_print_leaks
00000000000095be T MEM_priv_alloc
000000000000971d T MEM_priv_free
00000000000099c1 T MEM_priv_realloc
0000000000009d26 T MEM_set_callback_leak
0000000000009d3f T MEM_set_callback_noleak

$ objdump -T ./builds/libmemory.so | grep MEM_
0000000000009d3f g    DF .text  0000000000000019  Base        MEM_set_callback_noleak
00000000000093e9 g    DF .text  00000000000001d5  Base        MEM_print_leaks
0000000000009d26 g    DF .text  0000000000000019  Base        MEM_set_callback_leak
00000000000099c1 g    DF .text  0000000000000365  Base        MEM_priv_realloc
0000000000009343 g    DF .text  00000000000000a6  Base        MEM_init
00000000000095be g    DF .text  000000000000015f  Base        MEM_priv_alloc
000000000000971d g    DF .text  00000000000002a4  Base        MEM_priv_free
0000000000009178 g    DF .text  00000000000000a7  Base        MEM_exit

$ readelf -Ws ./builds/libmemory.so | grep MEM_
    49: 0000000000009d3f    25 FUNC    GLOBAL DEFAULT   11 MEM_set_callback_noleak
    95: 00000000000093e9   469 FUNC    GLOBAL DEFAULT   11 MEM_print_leaks
    99: 0000000000009d26    25 FUNC    GLOBAL DEFAULT   11 MEM_set_callback_leak
   118: 00000000000099c1   869 FUNC    GLOBAL DEFAULT   11 MEM_priv_realloc
   126: 0000000000009343   166 FUNC    GLOBAL DEFAULT   11 MEM_init
   145: 00000000000095be   351 FUNC    GLOBAL DEFAULT   11 MEM_priv_alloc
   192: 000000000000971d   676 FUNC    GLOBAL DEFAULT   11 MEM_priv_free
   272: 0000000000009178   167 FUNC    GLOBAL DEFAULT   11 MEM_exit
   103: 0000000000009343   166 FUNC    GLOBAL DEFAULT   11 MEM_init
   108: 0000000000009178   167 FUNC    GLOBAL DEFAULT   11 MEM_exit
   148: 0000000000009d3f    25 FUNC    GLOBAL DEFAULT   11 MEM_set_callback_noleak
   202: 00000000000095be   351 FUNC    GLOBAL DEFAULT   11 MEM_priv_alloc
   267: 000000000000971d   676 FUNC    GLOBAL DEFAULT   11 MEM_priv_free
   342: 0000000000009d26    25 FUNC    GLOBAL DEFAULT   11 MEM_set_callback_leak
   346: 00000000000099c1   869 FUNC    GLOBAL DEFAULT   11 MEM_priv_realloc
   366: 00000000000093e9   469 FUNC    GLOBAL DEFAULT   11 MEM_print_leaks

有什么非常简单的东西我错过了吗?所有其他相关的问题都有简单的答案,比如链接库顺序和使用的路径-但是我已经验证它们已经到位并按预期工作。
调整可见性(-fvisibility)也没有改变任何东西。
使用clang或gcc都会得到相同的结果。
Linux 3.16.0-38-generic gcc版本4.8.4(Ubuntu 4.8.4-2ubuntu1〜14.04.3)

一个缺失的-soname是否与你的问题有关? - Adrian Colomitchi
@AdrianColomitchi 尝试为两者添加适当的-soname参数,但没有任何区别。 - ZXcvbnM
@Leon,-DC选项的输出与-gC选项的输出相同,令人遗憾。 - ZXcvbnM
1
你能准备一个最小、完整和可验证的示例吗? - Leon
我现在正在工作,但今晚回来后会把它剥离出来。毫无疑问,当我开始时可能会找到解决方案...! - ZXcvbnM
2个回答

0
你应该将 MEM_priv_alloc() 函数标记为 extern "C" 或者将函数体包装在 extern "C" { /* function implementation */ } 中(根据描述已经完成)。 并且在头文件中使用 __cplusplusextern "C" 的组合。
#ifdef __cplusplus
   #define EXTERNC extern "C"
#else
   #define EXTERNC
#endif

EXTERNC int MEM_priv_alloc (void);

请同样仔细检查 MEM_priv_alloc 函数的原型。例如,MEM_priv_alloc 不应该是 inline 的(不幸的是我不知道这个的物理学,然而 inline 示例对我来说失败了)


以下是我使用的简化示例:
文件:
$ ls
main.c  Makefile  mem.cpp  mem.h  strings.c  strings.h

mem.cpp

#include <stdio.h>
#include "mem.h"
extern "C" int MEM_priv_alloc (void)
{
   return 0;
}

mem.h

#ifdef __cplusplus
   #define EXTERNC extern "C"
#else
   #define EXTERNC
#endif
EXTERNC int MEM_priv_alloc (void);

strings.c:

#include <stdio.h>
#include "mem.h"
int STR_duplicate_replace (void)
{
   MEM_priv_alloc();
}

strings.h:

int STR_duplicate_replace (void);

main.c

#include <stdio.h>
#include "string.h"
int main (void)
{
   STR_duplicate_replace ();
   return 0;
}

Makefile:

prog: libmemory.so libstrings.so
        gcc -o $@ -L. -lmemory -lstrings main.c

libmemory.so: mem.cpp
        g++ -shared -fPIC -o $@ $^

libstrings.so: strings.c
        gcc -shared -fPIC -o $@ $^

以及构建日志:

g++ -shared -fPIC -o libmemory.so mem.cpp
gcc -shared -fPIC -o libstrings.so strings.c
gcc -o prog -L. -lmemory -lstrings main.c

请参阅更多详细信息: 在C代码中使用C++库如何混合使用C和C ++


嗯,我怀疑问题更复杂。这段代码(还有其他我还没处理的库)在msvc2015上已经完全构建为Windows版本,并且如果我禁用内存调试,它是可以工作的(所以MEM_priv*函数从未被使用或导出)。我会将其精简至最小并在今晚进一步查找。 - ZXcvbnM

0

所以,我正在剥离整合的最后部分,并揭示了这个问题。

我的导入/导出是基于这个模型:https://gcc.gnu.org/wiki/Visibility

我的等效实现最终看起来像这样:

#     if GCC_IS_V4_OR_LATER
#             define DLLEXPORT        __attribute__((visibility("default")))
#             define DLLIMPORT        __attribute__((visibility("hidden")))
#     else
#             define DLLEXPORT
#             define DLLIMPORT
#     endif

DLLIMPORT(可见性隐藏)是问题的原因;我用空白定义替换它,一切都好了。是的,我也有clang的等效物,这也导致了同样的失败。

我的结论是,C代码只能将这些可能的符号视为隐藏的,因此无论它如何尝试,无论它们实际上存在多少,都无法导入它们!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接