Been fighting with this on and off for 48 hours now; I’m still getting undefined reference errors when attempting to link a dynamic library with its dependency – despite all exports existing, and the library being found successfully.
Scenario:
- libmemory (C++) – exports functions with
extern "C"
- libstring (C) – exports functions, imports from libmemory
libmemory builds successfully:
$ g++ -shared -fPIC -o ./builds/libmemory.so ...$(OBJECTS)...
libstring compiles successfully, but fails to link:
$ 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
Verifying libmemory exports its symbols, and the library itself is found by using -v
to 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
Is there something horribly simple I’m missing? All the other related questions to this have simple answers such as link library order, and the paths used – but I’ve already verified they’re in place and working as expected.
Tinkering with -fvisibility
led to no changes either.
The same result exists whether using clang or gcc.
Linux 3.16.0-38-generic
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
Advertisement
Answer
So, I was stripping out the final parts of the amalgamation, and uncovered the issue.
My import/export is modelled off of this: https://gcc.gnu.org/wiki/Visibility
My equivalent implementation ends up looking like this:
# if GCC_IS_V4_OR_LATER # define DLLEXPORT __attribute__((visibility("default"))) # define DLLIMPORT __attribute__((visibility("hidden"))) # else # define DLLEXPORT # define DLLIMPORT # endif
The DLLIMPORT (visibility hidden) is causing the problem; I replace it with a blank definition and it’s all ok. Yes, I did also have the equivalent for clang, which is why that also failed in the same way.
My takeaway from this is that the C code only ever saw these would-be-symbols as hidden, and therefore couldn’t import them no matter how hard it tried, and however much they actually existed!