Как сделать так, чтобы 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;
}

Make file:

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

Выход:

$ ./a.out
foo

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

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

3 ответа

Решение

Вообще говоря: если вы не ставите слабую реализацию в свой 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'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

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

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

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

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

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

Тогда вы увидите:

$ ./a.out
bar

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

Также посмотрите отличный пост Эли Бендерского о статических библиотеках и процессе их связывания "Порядок библиотек в статических ссылках" и этот ТАК вопрос.

Вы должны отделить слабую реализацию в другую библиотеку.

Объявляйте его только в main.

Другие вопросы по тегам