0

I am trying to use an external third-party shared library file in my project using the CMake build system, where I have access to just the .so file and its relevant headers. As a minimal example, pretend this external library (called hello) is defined as follows:

hello.h

#pragma once

void hello(void);

hello.c

#include "hello.h"
#include <stdio.h>

void hello(void)
{
    printf("Hello world!\n");
}

The example library could have been built externally with (specifics here don't matter):

g++ -shared -o libhello.so -fPIC hello.c

This shared library is confirmed to have a hello function in its external table using nm -gDC libhello.so:

                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000001119 T hello()
                 w __cxa_finalize
                 w __gmon_start__
                 U puts

Given access to libhello.so and hello.h (the real external library, the Chromium Embedded Framework or CEF is obviously more complicated than this with multiple directories), I naively copy the relevant headers and shared library into my project so that I can use this hello function.

This is my resulting project folder structure:

├── CMakeLists.txt
├── libs
│   └── hello
│       ├── include
│       │   └── hello.h
│       └── libhello.so
└── src
    └── main.cpp

The main.cpp file is obviously nothing special:

#include "hello.h"

int main(void)
{
    hello();
    return 0;
}

The important part, the CMakeLists.txt file, is written like this:

cmake_minimum_required(VERSION 3.0)

project(test)

add_library(HELLO SHARED IMPORTED)
set_target_properties(
    HELLO
    PROPERTIES
    IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/libs/hello/libhello.so"
    INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/libs/hello/include/"
)

add_executable(index src/main.cpp)
target_link_libraries(index HELLO)

Building and attempting to run client program is performed as follows from the root of this directory:

mkdir build
cd ./build
cmake ..
make
./index

I am met with the following error message:

Scanning dependencies of target index
[ 50%] Building CXX object CMakeFiles/index.dir/src/main.cpp.o
[100%] Linking CXX executable index
/usr/bin/ld: CMakeFiles/index.dir/src/main.cpp.o: in function `main':
main.cpp:(.text+0x9): undefined reference to `hello()'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/index.dir/build.make:85: index] Error 1
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/index.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

How do I resolve this linking error in my project? If platform-specifics matter, I am on Ubuntu 20.04 WSL2 with cmake --version of 3.16.3.

UPDATE #1:

Attempting to use the more idiomatic target_include_directories yields the same error (new CMakeLists.txt as follows):

cmake_minimum_required(VERSION 3.0)

project(test)

add_library(HELLO SHARED IMPORTED)
set_target_properties(
    HELLO
    PROPERTIES
    IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/libs/hello/libhello.so"
    #INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/libs/hello/include/"
)

add_executable(index src/main.cpp)
target_include_directories(index PUBLIC ${CMAKE_SOURCE_DIR}/libs/hello/include/)
target_link_libraries(index HELLO)

UPDATE #2:

Running the compilation make step with make VERBOSE=1 yields the following additional diagnostic information:

make VERBOSE=1
/usr/bin/cmake -S/home/anish/repos/hello_test -B/home/anish/repos/hello_test/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/anish/repos/hello_test/build/CMakeFiles /home/anish/repos/hello_test/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/anish/repos/hello_test/build'
make -f CMakeFiles/index.dir/build.make CMakeFiles/index.dir/depend
make[2]: Entering directory '/home/anish/repos/hello_test/build'
cd /home/anish/repos/hello_test/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/anish/repos/hello_test /home/anish/repos/hello_test /home/anish/repos/hello_test/build /home/anish/repos/hello_test/build /home/anish/repos/hello_test/build/CMakeFiles/index.dir/DependInfo.cmake --color=
Dependee "/home/anish/repos/hello_test/build/CMakeFiles/index.dir/DependInfo.cmake" is newer than depender "/home/anish/repos/hello_test/build/CMakeFiles/index.dir/depend.internal".
Dependee "/home/anish/repos/hello_test/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/anish/repos/hello_test/build/CMakeFiles/index.dir/depend.internal".
Scanning dependencies of target index
make[2]: Leaving directory '/home/anish/repos/hello_test/build'
make -f CMakeFiles/index.dir/build.make CMakeFiles/index.dir/build
make[2]: Entering directory '/home/anish/repos/hello_test/build'
[ 50%] Building CXX object CMakeFiles/index.dir/src/main.cpp.o
/usr/bin/c++   -I/home/anish/repos/hello_test/libs/hello/include   -o CMakeFiles/index.dir/src/main.cpp.o -c /home/anish/repos/hello_test/src/main.cpp
[100%] Linking CXX executable index
/usr/bin/cmake -E cmake_link_script CMakeFiles/index.dir/link.txt --verbose=1
/usr/bin/c++    -rdynamic CMakeFiles/index.dir/src/main.cpp.o  -o index  -Wl,-rpath,/home/anish/repos/hello_test/libs/hello ../libs/hello/libhello.so 
/usr/bin/ld: CMakeFiles/index.dir/src/main.cpp.o: in function `main':
main.cpp:(.text+0x9): undefined reference to `hello()'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/index.dir/build.make:85: index] Error 1
make[2]: Leaving directory '/home/anish/repos/hello_test/build'
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/index.dir/all] Error 2
make[1]: Leaving directory '/home/anish/repos/hello_test/build'
make: *** [Makefile:84: all] Error 2
CodeSurgeon
  • 2,435
  • 2
  • 15
  • 36
  • @john I thought I was letting CMake know the location of these headers with `INTERFACE_INCLUDE_DIRECTORIES`. Should I be using `target_include_directories` as well or is there another way to do that? – CodeSurgeon Mar 15 '23 at 07:15
  • `target_include_libraries` is usually the cleaner / more idiomatic(?) way. Plus, you won't need to worry about forgetting to use `APPEND` like you did here (lucky for you there was nothing to lose forgetting `APPEND` here) – starball Mar 15 '23 at 07:18
  • @user I updated the question to try to use `target_include_directories` (I could not find `target_include_libraries`), but I still ran into the same error. – CodeSurgeon Mar 15 '23 at 07:28
  • 1
    off sorry for my huge typo. yes that won't solve the problem since this is a link error and not a compile error. – starball Mar 15 '23 at 07:29
  • 1
    Run `make` with additional option `VERBOSE=1`. That way you will see the exact command line being executed for linking. – Tsyvarev Mar 15 '23 at 07:35
  • @Tsyvarev Neat, thanks for the additional diagnostic tool! I have added the output of `make VERBOSE=1` to the question. – CodeSurgeon Mar 15 '23 at 07:45
  • 2
    Assuming you have only system compilers on WSL, `/usr/bin/c++` is essentially the same as `g++`, so compiler's incompatibility is not the case. Are you sure that `${CMAKE_SOURCE_DIR}/libs/hello/libhello.so` is exactly the file, which `nm` output is given in the question post? Note, that your library's source file has `.c` extension, which normally implies C code, but you compile it with C++ compiler. Would you compile that source with C compiler, the error will be expected: https://stackoverflow.com/a/12574420/3440745 – Tsyvarev Mar 15 '23 at 08:04
  • @Tsyvarev g++ and /usr/bin/c++ are the same on my system. After I renamed the hello.c and hello.h to hello.cpp and hello.hpp (using `#include ` in that code to ensure it was c++ and not c code) and rebuilt the .so file, it worked. Thank you so much! Unfortunately, this still does not fix it for the library I need to link with (google's chromium embedded framework, when I use the shared library and headers provided in the `stable standard distribution` provided by https://cef-builds.spotifycdn.com/index.html#linux64). Time to make another question... – CodeSurgeon Mar 15 '23 at 08:37
  • @Tsyvarev Perhaps that other library's .so file is built with a different compiler (since I did not build `libcef.so` from the real external library on my computer but relied on that automated build I linked in the prior comment. Is there anyway to see what compiler was used to build a given .so file? – CodeSurgeon Mar 15 '23 at 08:46
  • Found this link: `https://unix.stackexchange.com/questions/719/can-we-get-compiler-information-from-an-elf-binary`. Running the objdump command listed there on the real external .so shared library, I get a later g++ version `(GCC: (Debian 10. 0010 322e312d 36292031 302e322e 31203230 2.1-6) 10.2.1 20)` than my computer has (`g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0`). Would that cause these types of errors with undefined references as well? – CodeSurgeon Mar 15 '23 at 08:51
  • Try wrapping your `#include` in `extern "C" {}` – Osyotr Mar 15 '23 at 08:56

0 Answers0