cmake, can't link static library into shared one - c++

I'm writing adapter (shared library) to some fpga API. I've got libsomelib.a and its API - somelibAPI.h. Here is a minimal example of my adapter:
somelib_adapter.h:
#include <string>
namespace details {
#include "somelibAPI.h"
}
class somelib_adapter {
public:
std::string foo();
};
somelib_adapter.cpp:
#include "somelib_adapter.h"
using namespace details;
std::string somelib_adapter::foo() {
char result[64];
somelibAPI_call(result);
return std::string(result);
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
project(untitled1)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES somelib_adapter.cpp somelib_adapter.h)
FIND_LIBRARY(SOMELIB_LIBRARIES
NAMES libsomelib.a
PATHS "${SRC_CPP_DIRECTORY}")
add_library(untitled1 SHARED ${SOURCE_FILES})
set(untitled1 -Wl,--whole-archive ${SOMELIB_LIBRARIES} -Wl,--no-whole-archive)
target_link_libraries(untitled1 some_other_shared_lib_used_by_somelib)
cmake finds libsomelib.a but when I'm trying to nm libuntitled1.so | c++filt it does not contain symbols location of somelibAPI_call. What's more, there is an undefined reference error with executable I made to test it. What may be wrong?
EDIT: libsomelib.a is compiled with -fPIC
EDIT2: I see I misunderstood some example. Now Ive got target_link_libraries(untitled1 -Wl,--whole-archive ${SOMELIB_LIBRARIES} -Wl,--no-whole-archive some_other_shared_lib_used_by_somelib) but there is another problem: /usr/bin/ld: ../libsomelib.a(somelibAPI.o): relocation R_X86_64_32 against .rodata.str1.1 can not be used when making a shared object; recompile with -fPIC

This is, in general, not possible, unless you recompile the static library.
The problem here is that dynamic libraries have to be generated with position independent code, so that they can be loaded dynamically into the address space of an existing process. That is, the compiler must generate the code in a specific way so that it is suitable for execution from a shared library.
Since we do not have this requirement for static libraries, a compiler is free to create position-dependent code there (which is exactly what happened in your case), which prohibits it from being linked into a shared library later. So for this to work, you need to change compilation of the static library to make it aware that it will be linked into a shared library later on. Only by recompiling the static library with the correct options can you make this work.
The exact way to set the correct build options of course depends on the build system used for building that static library. For example, for a static library built with CMake, you could change its build script so that it sets the POSITION_INDEPENDENT_CODE target property on the static library target.

Related

Transitive Symbol visiblity in C++ [duplicate]

I'm working on a project which has a "util" library containing stuff like logging, assertion handling etc. This is compiled into a static library with -fPIC added. I also have a plugin system, where the plugins are shared libraries loaded at runtime via dlopen. Those plugins and the main executable both use the static util library.
The problem: Now I'm getting AddressSanitizer: odr-violation errors when using ASAN. The issue is size=40 'vtable for StdStreamWriter' reported twice where StdStreamWriter is an implementation of an interface used internally in the static library.
I tried really hard to reproduce this in a MWE:
Create a static library exposing some function
Inside that use an interface and implementation in a std::shared_ptr
create a shared library linking against that
create an executable linking against the static lib and dlopen the shared library
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(proj)
set(sanitizer_flags "-fsanitize=address,undefined -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${sanitizer_flags}")
add_library(foo STATIC foo.cpp)
target_compile_features(foo PUBLIC cxx_std_14)
set_target_properties(foo PROPERTIES CXX_EXTENSIONS OFF POSITION_INDEPENDENT_CODE ON)
add_library(lib SHARED lib.cpp)
target_link_libraries(lib foo)
add_executable(main main.cpp)
target_link_libraries(main foo dl)
However the issue does not occur in the MWE no matter how hard I try.
I traced the difference down to differing results in nm -C liblib.so | grep vtable:
The MWE (no ODR error) shows V vtable for Impl
The acutal program shows D vtable for StdStreamWriter
I guess the error stems from the difference D vs V which results in the vtables not being merged.
Where does this difference come from? When is this decided? I stripped down the link command for the shared library to the bare essentials (clang++-8 -shared -fsanitize=address,undefined -o <..> <all *.o and *.so>) but still get the D vtable instead of V vtable.
What else can I try to fix this?
This is most likely caused by a known issue in Clang's implementation of Asan which causes it to detect false ODR violations for static data with vague linkage (normally class vtables or typeinfos).
As a workaround, try compiling with -mllvm -asan-use-private-alias=1 and maybe doing export ASAN_OPTIONS=use_odr_indicator=1 before running your code.
If this fixes your problem, please post a comment in the aforementioned issue to increase the chance that it's fixed once and for all in upstream.

How to make an extern method defined in a shared library but declared in other target using cmake?

The problem is as followed: you have two files, one is for a library one is for the executable and you want to call a method defined in the library but declared in the executable:
lib.cpp:
extern void Method();
void DoStuff() {
Method();
}
main.cpp:
#include <iostream>
void Method() {
std::cout << "Do Stuff" << std::endl;
}
int main() {
Method();
return 0;
}
And now using cmake I just basically want to tell the library that the declaration of the DoStuff method is in the executable target.
Using this simple CMakeLists.txt leads to an undefined symbol linker error:
Undefined symbols for architecture x86_64:
"Method()", referenced from:
DoStuff() in lib.cpp.o
CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(extern)
set(CMAKE_CXX_STANDARD 14)
add_library(myLib SHARED lib.cpp)
add_executable(exe main.cpp)
target_link_libraries(exe myLib)
The only way I found was to go through a temporary static library and link my shared library with that static library which I think is quite nasty because I soon I add more classes to the exectuable I get duplicate symbols (which totally makes sense since they are duplicate)
add_library(tmpLib STATIC main.cpp)
target_link_libraries(myLib tmpLib)
I've browsing the interwebs for two nights now looking for a way to do that and I'm still left with absolutely no clue.
The error happens during linking object files to create library file. Try OBJECT keyword in add_library instead of SHARED. This will create object files, but it won't link them. Then, you can use target_link_libraries the same way to link these object files to your executable. Since the linker will run once to create your executable and your executable includes the definition of Method, the linker shouldn't complain.
Also, extern specifier isn't necessary there, since functions have external linkage by default.
Depending on your linker, there might be an obscure way of telling it to ignore unresolved symbols, but this is highly unconventional, counter-intuitive, unexpected and confusing for other users/developers and your future self, therefore you should avoid it at all costs in real code. See this question for details.
Note: Your code works fine as-is on Debian 10 x64 with g++ 8.3.0, GNU ld 2.32.1.
After many more hours of searching I finally found the solution.
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,dynamic_lookup")
is all it takes ! It tells the linker to find undefined symbol by dynamic_lookup for shared libraries. Note that is for macos linker only. For other gcc variants use should use -Wl,allow-shlib-undefined. More info about this on this SO Post

ASAN detects ODR violation of vtable of class which is shared with dynamically loaded library

I'm working on a project which has a "util" library containing stuff like logging, assertion handling etc. This is compiled into a static library with -fPIC added. I also have a plugin system, where the plugins are shared libraries loaded at runtime via dlopen. Those plugins and the main executable both use the static util library.
The problem: Now I'm getting AddressSanitizer: odr-violation errors when using ASAN. The issue is size=40 'vtable for StdStreamWriter' reported twice where StdStreamWriter is an implementation of an interface used internally in the static library.
I tried really hard to reproduce this in a MWE:
Create a static library exposing some function
Inside that use an interface and implementation in a std::shared_ptr
create a shared library linking against that
create an executable linking against the static lib and dlopen the shared library
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(proj)
set(sanitizer_flags "-fsanitize=address,undefined -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${sanitizer_flags}")
add_library(foo STATIC foo.cpp)
target_compile_features(foo PUBLIC cxx_std_14)
set_target_properties(foo PROPERTIES CXX_EXTENSIONS OFF POSITION_INDEPENDENT_CODE ON)
add_library(lib SHARED lib.cpp)
target_link_libraries(lib foo)
add_executable(main main.cpp)
target_link_libraries(main foo dl)
However the issue does not occur in the MWE no matter how hard I try.
I traced the difference down to differing results in nm -C liblib.so | grep vtable:
The MWE (no ODR error) shows V vtable for Impl
The acutal program shows D vtable for StdStreamWriter
I guess the error stems from the difference D vs V which results in the vtables not being merged.
Where does this difference come from? When is this decided? I stripped down the link command for the shared library to the bare essentials (clang++-8 -shared -fsanitize=address,undefined -o <..> <all *.o and *.so>) but still get the D vtable instead of V vtable.
What else can I try to fix this?
This is most likely caused by a known issue in Clang's implementation of Asan which causes it to detect false ODR violations for static data with vague linkage (normally class vtables or typeinfos).
As a workaround, try compiling with -mllvm -asan-use-private-alias=1 and maybe doing export ASAN_OPTIONS=use_odr_indicator=1 before running your code.
If this fixes your problem, please post a comment in the aforementioned issue to increase the chance that it's fixed once and for all in upstream.

Using --whole-archive linker option with CMake and libraries with other library dependencies

I've got a project that used to be a giant set of source files that all got compiled and then linked as one executable. As a first step in making the project more modular, I am breaking up the build into several smaller chunks and making them static libraries. There's a hierarchy, so Exe1 will link against static libs Lib2A and Lib2B. Lib2A will depend on static Lib3A, lib3B, lib3C, etc. The numbers here show their layer in the hierarchy.
The problem is that I need to use --whole-archive when linking or else some symbols from the underlying libraries are not found.
When I add the below for the linking of Exe1:
target_link_libraries(Exe1 -Wl,--whole-archive Lib2A Lib2B -Wl,--no-whole-archive)
I end up with an actual link stage command like:
g++ -o Exe1 -Wl,--whole-archive libLib2A.a libLib2B.a -Wl,--no-whole-archive libLib3A.a libLib3B.a libLib3C.a
Inevitably, symbols from some of the layer 3 static libraries get lost and I get missing symbol errors.
I expected that because Lib2A has Lib3* libraries as dependencies, that they would also be "inside" the --whole-archive part of the linker command, but they show up outside.
I've tried many different combinations (e.g. putting the --whole-archive stuff at lower layers), but haven't come across an approach that works using CMake. What am I doing wrong?
Thanks
For 3.12 and newer versions of CMake, I would use object libraries.
The workaround I found for versions earlier than that was to create an intermediate static library that used some property magic to place all linkage dependencies inside the --whole-archive section. For me, the top-level static library was called 'source'. It contained actually nothing itself, but had linkage dependencies on a bunch of other static libraries. I created 'source-combined' as follows:
add_library(source-combined STATIC "")
set_target_properties(source-combined PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(source-combined PUBLIC
-Wl,--whole-archive
$<TARGET_PROPERTY:source,INTERFACE_LINK_LIBRARIES>
-Wl,--no-whole-archive
)
Now when I create an executable or a shared library by linking against this souce-combined library, I get the --whole-archive and --no-whole-archive as bookends around the entire set of static libraries that were the link dependencies of 'source'. It took forever to stumble across this technique, so I'm sharing it.
The following worked for me. Consider two libraries:
my_platform
my_clib
We want the whole archive of my_clib, and my_platform links to it.
add_library(my_platform INTERFACE) # this could also be a regular library
add_library(my_clib STATIC)
target_sources(my_clib
PRIVATE
gcc_newlib_nano.c
gcc_newlib_nano_cpp.cc
)
# Link my_clib and any other libs
target_link_libraries(my_platform
INTERFACE
my_clib
)
# Ensure the whole archive is linked
target_link_options(my_platform
INTERFACE
-Wl,--whole-archive ${CMAKE_CURRENT_BINARY_DIR}/libmy_clib.a -Wl,--no-whole-archive
)
As an alternative to the above answer, I needed to get something quick and dirty to see if the effort to add whole archive target flags (or convert the code base to object libraries...) was the right solution. By following the CMake Source Code for the default link command, I modified my project's command to:
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--start-group -Wl,--whole-archive <LINK_LIBRARIES> -Wl,--no-whole-archive -Wl,--end-group")
It worked a treat! While not the greatest solution, it will at least get some results quickly.
If you need to use the linker option --whole-archive, then you definably should use object libraries:
# Lib2A/CMakeLists.txt
add_library(Lib2A OBJECT ${Lib2A_SRC})
# Lib2B/CMakeLists.txt
add_library(Lib2B OBJECT ${Lib2B_SRC})
It is portable and does not require use the linker option --whole-archive.

CMake: Shared library that uses static library

I am trying to create a shared library (really a Python module) that links against a static library. Both libraries are part of the same project and built using cmake.
Now, the shared library is built like this:
add_library(MyLibPython SHARED ${PYTHON_WRAPPERS_SRC})
set_target_properties(MyLibPython PROPERTIES PREFIX "")
target_link_libraries(MyLibPython MyLibStatic ${LIBS})
This builds without error, but when I try to import the Python module, I get:
ImportError:
lib/python/MyLibPython.so: undefined symbol: _Zone_of_my_MyLibStatic_functions
I also have a number of executables (unit tests) that are built in a similar way, and they work perfectly.
I should add, this is using gcc on Linux.
Check your linker command line. Is it passing something like -Wl,--as-needed? If so, it might not be including everything required by the static library.
I don't think your technique is portable in general. Can you get a shared library to link against? I think that there are some platforms where everything that goes into a shared library needs to be compiled as PIC.
Anyway, to link an entire archive with GNU ld (look up man ld):
gcc -o foo foo.o bar.o baz.o -Wl,--whole-archive libfoo.a -Wl,--no-whole-archive [rest-of-linker-args]