Подтвердить что ты не робот

Как заставить gcc ссылку сильного символа в статической библиотеке переписать слабый символ?

Моя проблема может быть сведена к следующему:

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

Выход

$ ./a.out
foo

Таким образом, слабая полоса символов в main.c не перезаписывается сильным символом в bar.c из-за того, что bar.c связан с main.c в статической библиотеке libbar.a.

Как я могу сказать gcc сделать сильный символ в libbar.a, чтобы перезаписать слабый символ в main.c?

4b9b3361

Ответ 1

Вообще говоря: если вы не поместите слабую реализацию в свой main, компоновщик решит ее наконец во время выполнения. Но если вы реализуете его в main.c, вы сможете переопределить его с помощью сильной привязки (bar.c) при связывании этого статического элемента.

Пожалуйста, прочитайте http://www.bottomupcs.com/libraries_and_the_linker.html - он содержит много интересного в этой теме.

Я сам сделал тест:

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;
}

Мой файл 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 implementation
    LD_LIBRARY_PATH=. ./main6                                   # => main 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 implementation
    LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main8              # => main 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 implementation
    LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main10             # => main 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

Взгляните на main1 && main2... если вы не поместите слабую реализацию в main.c, а сохраните слабую в библиотеке и сильную в другой lib., вы сможете переопределить слабый, если сильная lib определяет сильная реализация bar().

Ответ 2

Я озадачен ответом max.haredoom(и что он был принят). Ответ касается разделяемых библиотек и динамической компоновки, тогда как вопрос был явно о поведении статического связывания с использованием статических библиотек. Я считаю, что это вводит в заблуждение.

При связывании статических библиотек ld по умолчанию не заботится о слабых/сильных символах : он просто решает символ undefined на первый встреченный символ (поэтому порядок статических библиотек в командной строке важен).

Однако это поведение по умолчанию можно изменить с помощью параметра --whole-archive. Если вы переписываете свой последний шаг в Makefile следующим образом:

gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive

Затем вы увидите:

$ ./a.out
bar

В двух словах --whole-archive заставляет компоновщик сканировать все его символы (в том числе уже разрешенные). Если есть сильный символ, который уже разрешен слабым символом (как в нашем случае), сильный символ будет перекрывать слабый.

Также см. замечательную статью о статических библиотеках и процесс их компоновки "Заказ библиотеки в статической привязке" Эли Бендерски и этот вопрос SO.