Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux Loader Lock / loader int __attribute__((constructor))

Tags:

c

linux

.so

I am currently developing a program module called test.so. Inside this test.so, there is code that loads and uses an external library called wow.so.

To test this module, I created a new test project. The execution flow is as follows: Test program (main) → test.so → internally loads wow.so

Because test.so needs to automatically execute some code upon being loaded, I used a constructor function like this: static void __attribute__((constructor)) my_init() { ... }

However, this constructor function is not working properly.

After some research, I found that attempting to load another .so file using dlopen() within a constructor function can fail. This appears to be due to loader lock or the initialization order of dynamic libraries on Linux.

Furthermore, I do not have the source code or header files for wow.so; I only have the compiled .so file. Also, I must solve this issue by modifying only test.so — I cannot modify any other part.

like image 508
정한호 Avatar asked Sep 02 '25 04:09

정한호


1 Answers

This behavior is related to glibc's dynamic loader. When libraries are loaded recursively, if a constructor function attempts to call dlopen() to load another shared library, it can lead to deadlocks or undefined behavior because dlopen() tries to acquire the loader lock which is already held during constructor execution.

Several bug reports have been filed but it is not considered a bug in glibc.
Red Hat Bugzilla Bug #661676
Debian glibc Bug #786397
Sourceware Bugzilla Bug #15686

The same behaviour also occurs in windows, due to the similarity of Windows loader lock and glibc's loader lock. MSDN - DLL Best Practices

Constructor functions (Glibc) or DllMain function (Windows) are executed before the main() function. It is recommended that constructors perform simple initializations. You shouldn’t perform operations that might block or wait on external resources inside constructor functions. Calling functions like dlopen() (in glibc) or LoadLibrary() (in windows) in constructors can lead to deadlocks or undefined behavior.

In glibc, dlopen() is marked as AS-Unsafe (asynchronous-signal-unsafe) and AC-Unsafe (asynchronous-cancel-unsafe) because it requires holding internal loader locks. Therefore, dlopen() should only be called in normal, sequential program code.
Glibc - Unsafe Features

On Windows, the situation is similar. Microsoft's documentation warns about the limitations of what can safely be done inside DllMain() (same as glibc’s constructor functions).
MSDN - DllMain

There are two valid options to safely load additional libraries and perform other similar operations:

1. Define an Explicit Initialization Function
Just like many other libraries (e.g. libcurl, libusb etc.) that require you to call an init() function explicitly in your main program, you can do the same for initialization.

static void *wow_handle = NULL;
int test_init(void) {
    if (!wow_handle) {
        wow_handle = dlopen("./libwow.so", RTLD_NOW);
        if (!wow_handle) {
            fprintf(stderr, "[libtest.so] dlopen failed: %s\n", dlerror());
            return -1;
        }
        return 0;
    }
    return 0;
}

2. Lazy Load or Lazy Perform on first use
You can delay loading the library or delay operations until the first time a dependent function is called. This avoids early initialization but requires checking on each use.

static void *wow_handle = NULL;
typedef void (*wow_func)(void);
static wow_func wow_func = NULL;

void test_function_requires_wow(void) {
    if (!wow_handle) {
        wow_handle = dlopen("./libwow.so", RTLD_NOW);
        if (!wow_handle) {
            fprintf(stderr, "[libtest.so] dlopen failed: %s\n", dlerror());
            return;
        }
        wow_func = (wow_func)dlsym(wow_handle, "wow_func");
        if (!wow_func) {
            fprintf(stderr, "[libtest.so] dlsym failed: %s\n", dlerror());
            return;
        }
    }
    wow_func();
}
like image 85
SHG Avatar answered Sep 04 '25 18:09

SHG