Search code examples
c++linuxlinkerdynamic-librarydynamic-loading

unresolved symbol __real_malloc while dynamically loading a library which is compiled with --wrap=malloc


As mentioned in other SO answers I am using the wrapping mechanism of GNU ld to intercept calls to malloc on Linux (see here for an example). The used linker flag is -Wl,--wrap=malloc and the respective void __wrap_malloc(size_t) is also defined. This works fine in a test application where all compilation unit are linked into the same binary.

Now, I need to modify a dynamically linked library which is loaded into a main program via dlopen(). Linking the library succeeds, but loading it into the main program fails with undefined symbol: __real_malloc.

Running nm on the library shows that __wrap_malloc is defined but __real_malloc is not. But, according to man ld and this SO answer, malloc should get replaced with __wrap_malloc and __real_malloc should point to malloc when using this technique.

In the test application, I see that __real_malloc is undefined in the compiled object files but is resolved after getting linked into the executable.

So, why is the symbol resolved in the test application but not in the dynamic library? In both cases a final link step is performed which should resolve this symbol. Or is it required to add another library during the link step of the dynamic library in order to get __real_malloc resolved?

Just in case, it is not possible to modify the target application which loads the dynamic library via dlopen.


Solution

  • It should work and only requires slight changes to the code in the question you linked.

    testapp.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>
    
    typedef void f_t(void);
    
    int main()
    {
        void* h = dlopen("./malloc_wrapper.so", RTLD_NOW);
        if (h == NULL)
        {
            puts(dlerror());
            exit(1);
        }
    
        f_t* f = (f_t*)dlsym(h, "test");
    
        if (f == NULL)
        {
            puts(dlerror());
            exit(1);
        }
    
        (*f)();
    
        return 0;
    }
    

    malloc_wrapper.c

    #include <stdlib.h> /* added */
    #include <stdio.h>
    void *__real_malloc (size_t);
    
    /* This function wraps the real malloc */
    void *__wrap_malloc(size_t size)
    {
        void *lptr = __real_malloc(size);
        printf("Malloc: %lu bytes @%p\n", size, lptr);
        return lptr;
    }
    
    void test(void) /* added */
    {
        free(malloc(1024));
    }
    

    Compile and run.

    gcc -Wl,-wrap,malloc -shared -fpic malloc_wrapper.c -o malloc_wrapper.so
    gcc testapp.c -o testapp -ldl
    ./testapp
    Malloc: 1024 bytes @0x1d44680
    

    Compiling malloc_wrapper.so like this reproduces the error you describe:

    gcc -shared -fpic malloc_wrapper.c -o malloc_wrapper.so
    ./testapp
    ./malloc_wrapper.so: undefined symbol: __real_malloc
    

    Perhaps you were using wrap when compiling and linking the executable instead of the shared object?