Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMake: Errors when linking to shared library #248

Open
abhaybd opened this issue Feb 7, 2024 · 7 comments
Open

CMake: Errors when linking to shared library #248

abhaybd opened this issue Feb 7, 2024 · 7 comments

Comments

@abhaybd
Copy link

abhaybd commented Feb 7, 2024

I'm trying to use Loguru as a dependency for my shared library, but it's crashing with link-time errors. I have found a workaround that solves the issue, but this should probably be fixed, or at least documented.

Simple example:

main.cpp:

#include <loguru.hpp>

void foo();

int main() {
    LOG_F(INFO, "Hello, world!");
    foo();
}

library.cpp:

#include <loguru.hpp>

void foo() {
    LOG_F(INFO, "Foo was called!");
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)

project(loguru_cmake_example CXX)

include(FetchContent)
FetchContent_Declare(LoguruGitRepo
	GIT_REPOSITORY "https://github.com/emilk/loguru" # can be a filesystem path
	GIT_TAG        "master"
)
FetchContent_MakeAvailable(LoguruGitRepo) # defines target 'loguru::loguru'

add_library(lib SHARED library.cpp)
target_link_libraries(lib PRIVATE loguru::loguru)

add_executable(main main.cpp)
target_link_libraries(main PRIVATE loguru::loguru lib)

After configuring with CMake (version 3.16.3), running make -j $(nproc) yields:

/usr/bin/ld: _deps/logurugitrepo-build/libloguru.a(loguru.cpp.o): relocation R_X86_64_PC32 against symbol `_ZN6loguru19g_flush_interval_msE' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/lib.dir/build.make:85: liblib.so] Error 1
make[1]: *** [CMakeFiles/Makefile2:126: CMakeFiles/lib.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

Workaround

This error can be fixed by adding set(BUILD_SHARED_LIBS ON) before the FetchContent_MakeAvailable(...) call. After adding this line, I get a new error:

/usr/bin/ld: _deps/logurugitrepo-build/libloguru.so.2.1.0: undefined reference to `dladdr'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/main.dir/build.make:86: main] Error 1
make[1]: *** [CMakeFiles/Makefile2:99: CMakeFiles/main.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

I found that I can fix this new error by adding set(LOGURU_STACKTRACES ON) before the FetchContent_MakeAvailable(...) call.

After adding these lines, the new CMakeLists.txt looks like:

cmake_minimum_required(VERSION 3.10)

project(loguru_cmake_example CXX)

include(FetchContent)
FetchContent_Declare(LoguruGitRepo
	GIT_REPOSITORY "https://github.com/emilk/loguru" # can be a filesystem path
	GIT_TAG        "master"
)
set(BUILD_SHARED_LIBS ON)
set(LOGURU_STACKTRACES ON)
FetchContent_MakeAvailable(LoguruGitRepo) # defines target 'loguru::loguru'

add_library(lib SHARED library.cpp)
target_link_libraries(lib PRIVATE loguru::loguru)

add_executable(main main.cpp)
target_link_libraries(main PRIVATE loguru::loguru lib)

And compilation now works. Note that this issue only happens when linking a shared library against loguru, static libraries and executables work fine.

How can this issue be fixed without requiring users to implement these workarounds, or at least document that these statements are necessary for shared libraries?

@skaravos
Copy link
Contributor

I'll look into this over the weekend, its probably only a small fix required in the CMakeLists.txt file.

@jalendw
Copy link

jalendw commented Oct 16, 2024

+1 on this issue. I fixed it by adding this to the top level CMakeLists.txt in my own loguru fork, but I would prefer to be able to use upstream loguru without my own patches:

if(UNIX AND NOT APPLE)
       set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()

@skaravos
Copy link
Contributor

Sorry I never followed up on this, when I checked back in June I was unable to reproduce the error with my specific build environment so responding to the issue slipped my mind.

Can I ask what OS and compiler you're using? @jalendw

@skaravos
Copy link
Contributor

Okay nevermind, I setup an ubuntu 16 docker environment and was able to reproduce the undefined reference to 'dladdr' issue.

The problem is that the loguru.cpp file implicitly enables stacktraces by default when __GLIBC__ is defined, see here

loguru/loguru.cpp

Lines 88 to 92 in 4adaa18

#ifdef __GLIBC__
#ifndef LOGURU_STACKTRACES
#define LOGURU_STACKTRACES 1
#endif
#else

Enabling stacktraces requires the loguru library to be linked to libdl but the CMakeLists.txt file doesn't know it has to do that unless the user defines the cmake variable LOGURU_STACKTRACES=ON.

I'll need to think about this a bit more to decide on the best fix.

@jalendw
Copy link

jalendw commented Oct 17, 2024

Thanks @skaravos !

FWIW, I saw this on both Fedora40 and Ubuntu24.04, with GCC.

@skaravos
Copy link
Contributor

skaravos commented Nov 4, 2024

@abhaybd @jalendw

Looking into this a bit more tonight, I noticed that I glossed over the fact that there were actually two distinct problems that you both were having.

  1. undefined reference to dladdr

and

  1. recompile with -fPIC

I already addressed the dladdr problem in my other comments above, as it relates to the LOGURU_STACKTRACE variable. That one is something that does still need to be addressed in loguru's own CMakeLists.txt.

However, I never explained the -fPIC problem and why you were running into that.

That errors occurs when you try to compile a STATIC library without -fPIC, and then try to link that static library to a SHARED library.

So there were two solutions to that problem:

  1. You can enable -fPIC on the loguru target by setting the CMAKE_POSITION_INDEPENDENT_CODE variable to TRUE before calling FetchContent_MakeAvailable(LoguruGitRepo)

    • This will cause CMake to compile the loguru library with the -fPIC (or equivalent) flag.

      include(FetchContent)
      FetchContent_Declare(LoguruGitRepo
        GIT_REPOSITORY "https://github.com/emilk/loguru" # can be a filesystem path
        GIT_TAG        "master"
      )
      set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)  # loguru will be compiled with -fPIC
      FetchContent_MakeAvailable(LoguruGitRepo)  # defines target 'loguru::loguru'
    • If you're not using FetchContent and instead are building loguru independently on the command line, you would define that variable during your cmake configure command:

      cmake -S . -B _build -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE # ...
  2. You could compile loguru as a SHARED library instead of a STATIC library, by setting the BUILD_SHARED_LIBS variable to TRUE

    include(FetchContent)
    FetchContent_Declare(LoguruGitRepo
      GIT_REPOSITORY "https://github.com/emilk/loguru" # can be a filesystem path
      GIT_TAG        "master"
    )
    set(BUILD_SHARED_LIBS TRUE)  # loguru will be compiled as a shared library
    FetchContent_MakeAvailable(LoguruGitRepo)  # defines target 'loguru::loguru'

Both of those options will resolve your position-independent-code issue, and it looks like you both stumbled upon one of the two solutions and solved your own problems which is great.

Loguru's CMakeLists.txt file doesn't explicitly define whether it should be compiled as a static/shared library with/without position independent code, because doing so could cause performance penalties or force usage constaints on the end user where it isn't needed.

Instead it respects the cmake built-in variables CMAKE_POSITION_INDEPENDENT_CODE and BUILD_SHARED_LIBS for controlling its behaviour.

skaravos added a commit to skaravos/loguru that referenced this issue Nov 4, 2024
Changes:
- Fixes issue [emilk#248](emilk#248)
- CMake now performs a quick `try_run()` to inspect whether the
  currently detected C++ compiler is using glibc, and if so sets the
  LOGURU_STACKTRACES variable to TRUE by default to match the default
  value of the LOGURU_STACKTRACES macro inside `loguru.cpp`.

Notes:
- This is required because the cmake file is currently setup to only
  link loguru to the `dl` library if the cmake variable
  LOGURU_STACKTRACES is TRUE.
  Unfortunately, the `loguru.cpp` file implicitly enables the
  stacktraces feature under certain conditions (glibc on non-windows)
  and the CMake file wasn't previously written to account for that.
  This meant that the `dl` library wasn't being linked even though
  stacktraces were enabled (in the .cpp file), causing a linking error.
  - `undefined reference to `dladdr'`
@jalendw
Copy link

jalendw commented Nov 4, 2024

Thanks @skaravos , this seems to work without patching the loguru source:

+if(UNIX AND NOT APPLE)
+    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+endif()
 add_subdirectory(vendor/loguru)
+if(UNIX AND NOT APPLE)
+    unset(CMAKE_POSITION_INDEPENDENT_CODE)
+endif()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants