0

My aim is to provide a new C++ shared library that will by called by Python using Pybind, to expose existing C++ functionality. The Python aspect is not super relevant here, but just for context.

The shared library essentially provides an interface, that then calls functionality in an existing project, called "core", provided by a static library (libcore.a). Core is NOT built with PIC.

For now, I am simply trying to get the basics working:

  • test_app - this is a C++ stand in for the Python client - the problem I will get to is demonstrated here
  • lib_interface - the .so I want to generate
  • lib_core - the .a representing all the existing functionality

It is important to me NOT to use PIC, but instead relocation (https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/).

This is all with CMAKE, on Ubuntu 20, gcc 9.4.0.

\CmakeLists.txt

cmake_minimum_required(VERSION 3.15)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-pthread -Wall -fopenmp -fuse-ld=lld")
project("test LANGUAGES CXX")

add_subdirectory(test_app)
add_subdirectory(lib_interface)
add_subdirectory(lib_core)

test_app\CmakeLists.txt

add_executable(test_app main.cpp)

target_link_libraries(test_app PUBLIC
        ${CMAKE_BINARY_DIR}/lib_interface/liblib_interface.so
        )

test_app\main.cpp

#include <iostream>
#include "../lib_interface/lib_interface.h"

int main()
{
    std::cout << "Test app start" << std::endl;
    lib_interface::foo();
    return 0;
}

lib_interface\CmakeLists.txt


add_library(lib_interface SHARED lib_interface.cpp)

set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "") # in order to remove -fPIC

target_link_libraries(lib_interface PRIVATE
        ${CMAKE_BINARY_DIR}/lib_core/liblib_core.a
        )

lib_interface\lib_interface.cpp

#include <iostream>
#include "lib_interface.h"
#include "../lib_core/lib_core.h"

void lib_interface::foo()
{
    std::cout << "lib_interface - foo"<< std::endl;

    std::cout << "calling lib_core"<< std::endl;
    lib_core::bla();
}

lib_core\CmakeLists.txt


add_library(lib_core STATIC lib_core.cpp)

lib_core\lib_core.cpp

#include <iostream>
#include "lib_core.h"

void lib_core::bla()
{
    std::cout << "lib_core - bla"<< std::endl;
}

Building as presented, I get this error

FAILED: lib_interface/liblib_interface.so 
: && /usr/bin/c++ -pthread -Wall -fopenmp -fuse-ld=lld -g   -shared -Wl,-soname,liblib_interface.so -o lib_interface/liblib_interface.so lib_interface/CMakeFiles/lib_interface.dir/lib_interface.cpp.o  lib_core/liblib_core.a && :
ld.lld: error: relocation R_X86_64_PC32 cannot be used against symbol std::cout; recompile with -fPIC

Adding -fPIC is not acceptable - this is one of those cases where the performance impact is a problem, and up-front loading time is best.

Going off of Possible to build a shared library with static link used library? , it seems that -static is needed.

Adding

set_target_properties(lib_interface PROPERTIES LINK_FLAGS "-static")

to lib_interface\CMakeList.txt, I get:

FAILED: lib_interface/liblib_interface.so 
: && /usr/bin/c++ -pthread -Wall -fopenmp -fuse-ld=lld -g  -static -shared -Wl,-soname,liblib_interface.so -o lib_interface/liblib_interface.so lib_interface/CMakeFiles/lib_interface.dir/lib_interface.cpp.o  lib_core/liblib_core.a && :
ld.lld: error: can't create dynamic relocation R_X86_64_32 against symbol: __TMC_END__ in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output

which seems more promising.

Adding

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-z,notext")

to lib_interface\CMakeList.txt, the error is now:

FAILED: lib_interface/liblib_interface.so 
: && /usr/bin/c++ -pthread -Wall -fopenmp -fuse-ld=lld -Wl,-z,notext -g  -static -shared -Wl,-soname,liblib_interface.so -o lib_interface/liblib_interface.so lib_interface/CMakeFiles/lib_interface.dir/lib_interface.cpp.o  lib_core/liblib_core.a && :
ld.lld: error: relocation R_X86_64_32 cannot be used against symbol __TMC_END__; recompile with -fPIC

Appreciate a pointer on where I'm going wrong.

Nik
  • 2,718
  • 23
  • 34
  • This answer implies that you can't build a non `-fPIC` shared library https://stackoverflow.com/a/19768349/3370124 – Richard Critten Dec 21 '22 at 12:39
  • I think shared libraries default to `-fPIC` these days, you need to explicitly set `-fno-PIC` – Alan Birtles Dec 21 '22 at 12:44
  • set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "") # in order to remove -fPIC seems to effectively remove the fPIC from the build. – Nik Dec 21 '22 at 12:52
  • For completeness, changing above set_target_properties(lib_interface PROPERTIES LINK_FLAGS "-static") to set_target_properties(lib_interface PROPERTIES LINK_FLAGS "-fno-PIC -static") yields same error messages – Nik Dec 21 '22 at 12:54
  • @richardCritten https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/ - seems to imply there are two options for shared libraries - PIC and relocation, unless I misunderstand? PIC the more common these days – Nik Dec 21 '22 at 12:56
  • _Adding -fPIC is not acceptable - this is one of those cases where the performance impact is a problem_ Have you measured the impact? And have you profiled it to see if there are 'hotspots' which you can perhaps eliminate at the source code level (by inlining the functions in question, for example)? – Paul Sanders Dec 21 '22 at 12:58
  • The code is highly tuned, relies heavily on the inlining, cares deeply about CPU caches not being blown away, operations are measured in mics, etc etc. So a reduction in perf of (at least) 30% is certainly going to very impactful. So before going anywhere near that horrible situation, the aim is to bottom out if PIC is really and truly required. The blog link I posted gives me the sense that it's not mandatory for shared libraries, only the popular option, since it is indeed the better option for most scenarios. – Nik Dec 21 '22 at 13:11

0 Answers0