cgccldweak-linking

How to make gcc link strong symbol in static library to overwrite weak symbol?


My problem can be summarised in the following:

bar.c:

#include <stdio.h>

void bar() {
    printf("bar\n");
}

main.c:

#include <stdio.h>

void __attribute__((weak)) bar() {
    printf("foo\n");
}

int main() {
    bar();
    return 0;
}

Makefile:

all:
    gcc -c bar.c
    ar -rc libbar.a bar.o
    gcc main.c -L. -lbar

Output:

$ ./a.out
foo

So the weak symbol bar in main.c is not overwritten by the strong symbol in bar.c due to bar.c being linked to main.c in a static library libbar.a.

How can I tell gcc to make the strong symbol in libbar.a to overwritten the weak symbol in main.c?


Solution

  • Generally speaking: if you don't put a weak implementation into your main, the linker will resolve it at last at runtime. But if you implement it in main.c, you will only be able to override it with a strong bound (bar.c) when linking this static.

    Please read https://bottomupcs.com/ch09s05.html - it contains a lot of interesting stuff on this topic.

    I've made a test myself:

    bar.c

    #include <stdio.h>
    
    void bar()
    {
            puts("bar.c: i'm the strong bar()");
    }
    

    baz.c

    #include <stdio.h>
    
    void __attribute__((weak)) bar() 
    {
            puts("baz.c: i'm the weak bar()");
    }
    

    main.c

    #include <stdio.h>
    
    #ifdef V2
            void __attribute__((weak)) bar()
            {
                    puts("main: i'm the build in weak bar()");
            }
    #else
            void __attribute__((weak)) bar();
    #endif
    
    int main()
    {
        bar();
        return 0;
    }
    

    My Makefile:

    all:
        gcc -c -o bar.o bar.c
        gcc -shared -fPIC -o libbar.so bar.o
        gcc -c -o baz.o baz.c
        gcc -shared -fPIC -o libbaz.so baz.o
        gcc -o main1 main.c -L. -lbar -lbaz
        gcc -o main2 main.c -L. -lbaz -lbar
        LD_LIBRARY_PATH=. ./main1                                   # => bar.c
        LD_LIBRARY_PATH=. ./main2                                   # => baz.c
        LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main1              # => baz.c (!!)
        LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main2              # => baz.c
        gcc -o main3 main.c bar.o baz.o
        gcc -o main4 main.c baz.o bar.o
        ./main3                                                     # => bar.c
        ./main4                                                     # => bar.c
        gcc -DV2 -o main5 main.c -L. -lbar -lbaz
        gcc -DV2 -o main6 main.c -L. -lbaz -lbar
        LD_LIBRARY_PATH=. ./main5                                   # => main's implementation
        LD_LIBRARY_PATH=. ./main6                                   # => main's implementation
        gcc -DV2 -o main7 main.c -L. -lbar -lbaz
        gcc -DV2 -o main8 main.c -L. -lbaz -lbar
        LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main7              # => main's implementation
        LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main8              # => main's implementation
        gcc -DV2 -o main9  main.c -L. -lbar -lbaz
        gcc -DV2 -o main10 main.c -L. -lbaz -lbar
        LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main9              # => main's implementation
        LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main10             # => main's implementation
        gcc -c bar.c
        gcc -c baz.c
        gcc -o main11 main.c bar.o baz.o
        gcc -o main12 main.c baz.o bar.o
        ./main11                                                    # => bar.c
        ./main12                                                    # => bar.c
        gcc -o main13 -DV2 main.c bar.o baz.o
        gcc -o main14 -DV2 main.c baz.o bar.o
        ./main13                                                    # => bar.c
        ./main14                                                    # => bar.c
    

    Take a look at main1 && main2... if you don't put any weak implementation into main.c but keep the weak one in a library and the strong one in another lib., you'll be able to override the weak one if the strong lib defines a strong implementation of bar().