1

First thing first, here's my minimum reproducible program:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.17)
project(untitled4)

set(CMAKE_CXX_STANDARD 17)

add_library(lib1 SHARED lib1.cpp)

add_executable(untitled4 main.cpp)
target_link_libraries(untitled4 PRIVATE dl)

main.cpp:

#include "iostream"
#include <dlfcn.h>
int Test() {
  return 123456;
}
int main() {
  auto* handler = dlopen("/home/liu/source/untitled4/cmake-build-debug/liblib1.so", RTLD_LAZY|RTLD_GLOBAL);
  if (!handler) {
    std::cerr << dlerror();
    exit(1);
  }
}

and lib1.cpp:

#include "iostream"
extern int Test();
class Foo {
 public:
  Foo() {
    std::cout << Test() << std::endl;
  }
};
Foo foo;

now let me explain:

As you can see I defined a function called Test in main.cpp, and I want to the shared library liblib1.so call it when it is loaded.

But when I run the main() function, there's an error log said:

/home/liu/source/untitled4/cmake-build-debug/liblib1.so: undefined symbol: _Z4Testv

I check the symbol by using nm untitled4 |grep Test and the symbol seems exist:

0000000000000b87 t _GLOBAL__sub_I__Z4Testv
0000000000000aea T _Z4Testv

So what did I do wrong? How to fix this?

An important thing to be notice is that in the real case, the build of lib1 and the build of main.cpp are totally separated, the two build don't know each other. But I can make them into one build (very difficult) if this can fix the problem(if there's no other way).

P.S. I tried using extern "C" to wrap around the Test() in both files, but not working, seems not the C/C++ function naming problem.

psionic12
  • 185
  • 1
  • 11
  • I wonder how `lib1` library is successfully built when it uses `Test` function but doesn't link with definition if this function. Have you somehow suppressed "undefined reference" error when **build** the library? Note, that CMake has a special kind of library intended for `dlopen` usage: `MODULE` (instead of `SHARED`). – Tsyvarev Nov 25 '20 at 13:10
  • @Tsyvarev sounds reasonable, I will investigate – psionic12 Nov 25 '20 at 13:47
  • @Tsyvarev With the info I got from you I made a small example and it works without problems (no warnings either with `-Wall -Wextra -pedantic -pedantic-errors`), but I'm building it manually, – Ted Lyngmo Nov 25 '20 at 14:55
  • Error "undefined reference" is a **linker** error. It has nothing common with `-Wall` and other options you have specified, because these options are for **compiler**. Not sure what do you mean by "I'm building it manually". Do you mean "without CMake"? Or what? – Tsyvarev Nov 25 '20 at 15:00
  • Note, that declaring a function with **empty** argument list - `int Test()` - is a legacy (**old way**). While in C++ it is synonym for `int Test(void)`, in C their meaning is different. It is better to always specify `void` instead of empty argument list: `int Test(void)`. See that question for more info: https://stackoverflow.com/questions/51032/is-there-a-difference-between-foovoid-and-foo-in-c-or-c. – Tsyvarev Nov 25 '20 at 15:04
  • @Tsyvarev Yes, I built it using `g++` commands from the command line. I didn't mean that the `-Warnings` was for the linker. I just meant that the code was compiled without warnings. Linking was done with `g++ -rdynamic -o main main.o -ldl`. No suppression. – Ted Lyngmo Nov 25 '20 at 15:12
  • In the my first comment I told about creation/linking of the **library** (`lib1.cpp`), not the **executable** (`main.cpp`). The executable is obviously could be built successfully: it is not linked with the library and doesn't use its symbols directly. – Tsyvarev Nov 25 '20 at 15:15
  • @Tsyvarev Sorry, that I built with `g++ -Wall -Wextra -pedantic -pedantic-errors -fPIC -c -o lib1.o lib1.cpp` and `g++ -shared -o liblib1.so lib1.o`. – Ted Lyngmo Nov 25 '20 at 15:17
  • @Tsyvarev I think the answer is that OP misses `-rdynamic` while linking the final executable. I get the same error if I exclude that flag. – Ted Lyngmo Nov 25 '20 at 15:20
  • 1
    @TedLyngmo: Oops, sorry, I confused your comments with the asker ones. Yes, the C++ code itself looks correct. CMake project should (from my understanding) also work with `SHARED` replaced by `MODULE`. As for `-rdynamic`, it should be added by CMake automatically. – Tsyvarev Nov 25 '20 at 15:25
  • @Tsyvarev Re: `-rdynamic` Yeah, it doesn't look like a flag that one should need to add explicitly. Perhaps using `MODULE` instead as you say is the solution. One thing that _does_ look like a potential problem is the global `Foo foo;`. It looks like [Static Initialization Order Fiasco](https://en.cppreference.com/w/cpp/language/siof) waiting to happen, but that's a "future" problem. Not the one OP asks about. – Ted Lyngmo Nov 25 '20 at 15:27
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/225107/discussion-between-ted-lyngmo-and-tsyvarev). – Ted Lyngmo Nov 25 '20 at 15:56
  • 1
    I tested with `-rdynamic` and it works, as to use `MODULE`, unfortunately it will not work without `-rdynamic`, neither does `SHARED`, which I will do a further investigate to figure out why, as to the Order Fiasco, my real case use lazy initialization, won't be a problem. – psionic12 Nov 25 '20 at 16:44

1 Answers1

2

With the addition of the linker option -rdynamic your code does not fail with "undefined symbol".

You can set that with set_target_properties(untitled4 PROPERTIES ENABLE_EXPORTS 1) or target_link_options(untitled4 BEFORE PRIVATE "-rdynamic").

Example:

cmake_minimum_required(VERSION 3.17)
project(untitled4)

set(CMAKE_CXX_STANDARD 17)

add_library(lib1 SHARED lib1.cpp)

add_executable(untitled4 main.cpp)
set_target_properties(untitled4 PROPERTIES ENABLE_EXPORTS 1)

target_link_libraries(untitled4 PRIVATE dl)
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • @TedLyngmo: It is normal for a library, which is loaded by an application via `dlopen`, to have access to all functions, defined in that application. Linkage to that functions is performed by `dlopen` itself, no manual intervention is needed. – Tsyvarev Nov 25 '20 at 14:29
  • @Tsyvarev I added a `CMake` workaround instead. Perhaps it can be made in a better way, but I'm a `CMake` newbie. – Ted Lyngmo Nov 25 '20 at 16:17
  • I searched that the "CMake" style of doing this is `set_target_properties(untitled4 PROPERTIES ENABLE_EXPORTS 1)`, probably better than use compiler flags, which is more portable. – psionic12 Nov 26 '20 at 07:43
  • @psionic12 Nice! I updated it with that alternative. – Ted Lyngmo Nov 26 '20 at 08:26