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