Как заставить слабую связь работать с GCC?
Кажется, есть 3 способа сказать GCC о слабой связи символа:
__attribute__((weak_import))
__attribute__((weak))
#pragma weak symbol_name
Ни одна из этих работ для меня:
#pragma weak asdf
extern void asdf(void) __attribute__((weak_import, weak));
...
{
if(asdf != NULL) asdf();
}
Я всегда получаю ошибку ссылки, как это:
Неопределенные символы: "_asdf", ссылка с: _asdf$non_lazy_ptr в ccFA05kN.o ld: символ (ы) не найден collect2: ld вернул 1 статус выхода
Я использую GCC 4.0.1 на OS X 10.5.5. Что я делаю неправильно?
5 ответов
Я просто посмотрел на это и подумал, что некоторые другие могут быть заинтересованы в моих выводах.
Слабая связь со слабым_импортом действительно хорошо работает только с динамическими библиотеками. Вы можете заставить его работать со статическими ссылками (указав -undefined dynamic_lookup, как предложено выше), но это не такая горячая идея. Это означает, что неопределенные символы не будут обнаружены до времени выполнения. Это то, что я бы избегал в коде производства, лично.
Вот сеанс терминала Mac OS X, показывающий, как заставить его работать:
Вот фк
int f(int n)
{
return n * 7;
}
Вот что такое whatnof.c
#include <stdio.h>
#include <stdlib.h>
extern int f (int) __attribute__((weak_import));
int main() {
if(f == NULL)
printf("what, no f?\n");
else
printf("f(8) is %d\n", f(8));
exit(0);
}
Создайте динамическую библиотеку из fc:
$ cc -dynamiclib -o f.dylib f.c
Компилировать и связывать с динамической библиотекой, перечислять динамические библиотеки.
$ cc -o whatnof whatnof.c f.dylib
$ otool -L whatnof
whatnof:
f.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
Запустите whatnof, чтобы увидеть, что происходит:
$ whatnof
f(8) is 56
Теперь замените f.dylib пустой библиотекой (без символов):
$ mv f.dylib f.dylib.real
$ touch null.c
$ cc -dynamiclib -o f.dylib null.c
Запустите то же самое, чтобы увидеть, что происходит:
$ whatnof
what, no f?
Основная идея (или "сценарий использования") для слабого_импорта заключается в том, что он позволяет связывать набор динамических (общих) библиотек, а затем запускать тот же код для более ранних версий тех же библиотек. Вы можете проверить функции по NULL, чтобы увидеть, поддерживаются ли они в конкретной динамической библиотеке, с которой в данный момент выполняется код. Это, кажется, является частью базовой модели разработки, поддерживаемой XCode. Я надеюсь, что этот пример полезен; это помогло мне успокоиться об этой части дизайна XCode.
Добавлять -Wl,-flat_namespace,-undefined,dynamic_lookup
на строку компилятора gcc, которую вы используете для создания последней ссылки.
Пример минимального запуска Linux
main.c
#include <stdio.h>
int my_weak_var __attribute__((weak)) = 1;
int main(void) {
printf("%d\n", my_weak_var);
}
notmain.c
int my_weak_var = 2;
Скомпилируйте и запустите оба объекта:
gcc -c -std=c99 -Wall -Wextra -pedantic -o main.o main.c
gcc -c -std=c99 -Wall -Wextra -pedantic -o notmain.o notmain.c
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o notmain.o
./main.out
Выход:
2
Скомпилируйте и запустите без notmain.o
:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o
./main.out
Выход:
1
Итак, мы видим, что если дано на notmain.o
тогда неслабый символ имеет приоритет, как и ожидалось.
Мы можем проанализировать символы объектного файла ELF с помощью:
nm main.o notmain.o
который дает:
main.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
0000000000000000 V my_weak_var
U printf
notmain.o:
0000000000000000 D my_weak_var
а потом:
man nm
содержит:
Тип символа. По крайней мере, используются следующие типы; другие также зависят от формата объектного файла. Если строчные буквы, символ обычно является локальным; в верхнем регистре символ является глобальным (внешним). Однако есть несколько строчных символов, которые показаны для специальных глобальных символов ("u", "v" и "w").
"D"
"d" Символ находится в разделе инициализированных данных."В"
"V" Символ является слабым объектом. Когда слабый определенный символ связан с нормальным определенным символом, нормальный определенный символ используется без ошибок. Когда слабый неопределенный символ связан, а символ не определен, значение слабого символа становится равным нулю без ошибок. В некоторых системах верхний регистр указывает, что задано значение по умолчанию.
Если иметь дело с .a
статические библиотеки, однако, вам, возможно, придется использовать -Wl,--whole-archive
как объяснено в: Как сделать gcc-ссылку сильным символом в статической библиотеке, чтобы перезаписать слабый символ?
Слабые символы также можно оставить неопределенными, что в Binutils приводит к "поведению платформы", см. Поведение GCC для неразрешенных слабых функций.
Протестировано на Ubuntu 18.10, GCC 8.2.0.
Вам необходимо установить для переменной MACOSX_DEPLOYMENT_TARGET значение 10.2 или более позднюю. Смотрите документацию Apple и их технику по слабым ссылкам.
Из руководства gcc doc:
слабый
Слабый атрибут приводит к тому, что объявление генерируется как слабый символ, а не как глобальный. Это в первую очередь полезно при определении библиотечных функций, которые могут быть переопределены в пользовательском коде, хотя это также может быть использовано с не функциональными объявлениями. Слабые символы поддерживаются для целей ELF, а также для целей a.out при использовании ассемблера и компоновщика GNU.
Это означает, что объект имеет право перезаписывать слабый символ (определенный в другом объекте / библиотеке) без получения ошибок во время ссылки. Неясно, связываете ли вы библиотеку со слабым символом или нет. Кажется, что и вы не определили символ, и библиотека не связана должным образом.