Как заставить слабую связь работать с 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

GitHub вверх по течению.

Итак, мы видим, что если дано на 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.

Это означает, что объект имеет право перезаписывать слабый символ (определенный в другом объекте / библиотеке) без получения ошибок во время ссылки. Неясно, связываете ли вы библиотеку со слабым символом или нет. Кажется, что и вы не определили символ, и библиотека не связана должным образом.

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