Shared libraries and linking on Linux (elf)
-
29-10-2019 - |
Question
I have read the thread on Creating Library with backward compatible ABI that uses Boost and I'm now trying to understand how I should link my shared libraries to maintain a stable ABI, and avoid problems with interfering symbols.
I have created the following simple test project:
cat <<EOF > a.c
#define ABI __attribute__((visibility("default")))
int common();
int ABI a() { return common() + 1; }
EOF
cat <<EOF > b.c
#define ABI __attribute__((visibility("default")))
int common();
int ABI b() { return common() + 2; }
EOF
cat <<EOF > common_v1.c
int common() { return 1; }
EOF
cat <<EOF > common_v2.c
int common() { return 2; }
EOF
cat <<EOF > test.c
#include <assert.h>
int a();
int b();
int main(int argc, const char *argv[])
{
assert( a() + b() == 6 );
return 0;
}
EOF
cat <<EOF > CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(TEST)
add_library(common_v1 STATIC common_v1.c)
add_library(common_v2 STATIC common_v2.c)
SET_SOURCE_FILES_PROPERTIES( a.c b.c COMPILE_FLAGS -fvisibility=hidden )
add_library(a SHARED a.c)
target_link_libraries(a common_v1)
add_library(b SHARED b.c)
target_link_libraries(b common_v2)
add_executable(test test.c)
target_link_libraries(test a b)
EOF
The libraries common_v1 and common_v2 should emulate an external dependency of the libraries a and b (like Boost). Because common_v1 and common_v2 is considered as external libraries, I would prefer not to change their build system (and there by not change the flags they are compiled with).
The project above, compiles fine, but it does not work! When the test application is executed it jumps into the assert statement.
This makes me believe the same definition of common is used in both liba and libb. Why is this the case, and what am I doing wrong?
Solution
You can fix your test program by using --retain-symbols-file
option of ld when creating your a
and b
libraries and retain only a()
and b()
symbols, so that common()
symbol will not be exported by these libraries (and therefore one library will not try to use common()
symbol from another):
--retain-symbols-file filename Retain only the symbols listed in the file filename, discarding all others. filename is simply a flat file, with one symbol name per line.
Also you can use --version-script
option:
--version-script=version-scriptfile Specify the name of a version script to the linker.
where the version-scriptfile
is the following:
FOO { global: a; b; # symbols to be exported local: *; # hide others };
Related threads: