Enlico
Enlico

Reputation: 28404

dlopen succeeds (or at least seems to) but then dlsym fails to retrieve a symbol from a shared library

In an attempt to undersand how lazily loaded dynamic libraries work, I've made up the following (unfortunately non-working) example.

dynamic.hpp - Header of the library

#pragma once

void foo();

dynamic.cpp - Implementation of the library

#include "dynamic.hpp"

#include <iostream>

void foo() {
    std::cout << "Hello world, dynamic library speaking" << std::endl;
}

main.cpp - main function that wants to use the library (edited from the snippet in this question)

#include <iostream>
#include <dlfcn.h>

#include "dynamic.hpp"

int main() {
    void * lib = dlopen("./libdynamic.so", RTLD_LAZY);
    if (!lib) {
        std::cerr << "Error (when loading the lib): " << dlerror() << std::endl;
    }
    dlerror();
    auto foo = dlsym(lib, "foo");
    auto error = dlerror();
    if (error) {
        std::cerr << "Error (when loading the symbol `foo`): " << error << std::endl;
    }
    dlerror();

    using Foo = void (*)();
    (Foo(foo)());
}

Compilation and linking¹

# compile main.cpp
g++ -g -O0 -c main.cpp
# compile dynamic.cpp into shared library
g++ -fPIC -Wall -g -O0 -pedantic -shared -std=c++20 dynamic.cpp -o libdynamic.so
# link
g++ -Wall -g -pedantic -L. -ldynamic main.o -o main

Run

LD_LIBRARY_PATH='.' ./main

Error

Error (when loading the symbol `foo`): ./libdynamic.so: undefined symbol: foo
Segmentation fault (core dumped)

As far as I can tell, the error above clearly shows that the library is correctly loaded, but it's the retrieval of the symbol which fails for some reason.


(¹) A few options are redundant or, at least, not necessary. I don't think this really affects what's happening, but if you think so, I can try again with the options you suggest.

Upvotes: 1

Views: 1060

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118340

auto foo = dlsym(lib, "foo");

Perform the following simple thought experiment: in C++ you can have overloaded functions:

void foo();

void foo(int bar);

So, if your shared library has these two functions, which one would you expect to get from a simple "dlsym(lib, "foo")" and why that one, exactly?

If you ponder and wrap your brain around this simple question you will reach the inescapable conclusion that you must be missing something fundamental. And you are: name mangling.

The actual symbol names used for functions in C++ code are "mangled". That is, if you use objdump and/or nm tools to dump the actual symbols in the shared libraries you will see a bunch of convoluted symbols, with "foo" hiding somewhere in the middle of them.

The mangling is used to encode the "signature" of a function: its name and the type of its parameters, so that different overloads of "foo" produce distinct and unique symbol names.

You need to feed the mangled name into dlsym in order to resolve the symbol.

Upvotes: 1

Related Questions