Вкратце: делает llvm
/clang
поддерживать «слабый» атрибут?
Я изучаю некоторые источники библиотеки Arduino (HardwareSerial.cpp, чтобы быть более подробным), и я нашел некоторые интересные атрибуты weak
что я никогда не использовал раньше:
#if defined(HAVE_HWSERIAL0)
void serialEvent() __attribute__((weak));
bool Serial0_available() __attribute__((weak));
#endif
Я нашел это интересным, и я прочитал, что компоновщик должен установить его в NULL, если он не определен.
Однако в моих тестах с clang
Я не могу использовать это.
lib.cpp:
#include "lib.h"#include <stdio.h>
void my_weak_func() __attribute__((weak));
void lib_func() {
printf("lib_func()\n");
if (my_weak_func)
my_weak_func();
}
lib.h:
#ifndef LIB_FUNC
#define LIB_FUNC
void lib_func();
#endif
main.cpp:
#include "lib.h"#include <stdio.h>
#ifdef DEFINE_WEAK
void my_weak_func() {
printf("my_weak_func()\n");
}
#endif
int main() {
lib_func();
printf("finished\n");
return 0;
}
если я использую g++ lib.cpp main.cpp -o main -DDEFINE_WEAK
я могу использовать это:
MBA-Anton:Weak_issue asmirnov$ ./main
lib_func()
my_weak_func()
finished
но если я использую g++ lib.cpp main.cpp -o main
я не могу связать приложение:
Undefined symbols for architecture x86_64:
"my_weak_func()", referenced from:
lib_func() in lib-ceb555.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Чтобы быть более подробно о Clang:
MBA-Anton:Weak_issue asmirnov$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.3.0
Thread model: posix
Что я должен делать? Является weak
атрибут поддерживается llvm / clang?
PS. Я уже пытался переписать lib.cpp в пути Apple описывает и все та же ошибка компоновщика:
#include "lib.h"#include <stdio.h>
extern void my_weak_func() __attribute__((weak_import));
void lib_func() {
printf("lib_func()\n");
if (my_weak_func != NULL)
my_weak_func();
}
Кажется, что (насколько я могу судить) описание слабых ссылок в Apple вводит в заблуждение. Я добился успеха, помечая функцию как слабый / слабый_импорт, если определение действительно доступно во время ссылки. Это противоположно обычному поведению Linux, когда слабо связанный символ не нужно определять во время ссылки.
Например, следующие скомпилированы на Ubuntu 14.04 с gcc 4.8.2, но не на OS X 10.9.5 с clang
/* test.c */
int weakfunc() __attribute__((weak));
int main()
{
if (weakfunc) return weakfunc();
else return -1;
}
Самый простой обходной путь, который я нашел, — это явно указать компоновщику, чтобы оставленный символ не был определен. Например, clang test.c -Wl,-U,_myfunc
, Обратите внимание, что имя символа будет отличаться между C и C ++. В C (по крайней мере, для меня, я предполагаю, что это соответствует), к имени символа добавляется знак подчеркивания, как показано здесь. В C ++ имя искажено, так что вы получите что-то вроде __Z8weakfuncv
(не обязательно последовательный — я получаю только одно начальное подчеркивание на искаженном имени на моей коробке Ubuntu).
Следуя этому подходу, если функция определена во время выполнения (например, через библиотеку, предварительно загруженную путем установки DYLD_INSERT_LIBRARIES
переменная окружения или если версия зависимости разделяемой библиотеки отличается во время выполнения от той, что была во время сборки), символ будет разрешен, и функция будет вызываться по желанию. Если символ не определен во время выполнения, то проверка функции завершается неудачно, и мы продолжаем возвращать -1 по желанию.
Несколько более сложное решение — связать с фиктивной библиотекой, которая обеспечивает реализацию рассматриваемой функции. Например, если вы компилируете следующее как libdummy.dylib в том же каталоге:
int weakfunc()
{
return 1;
}
Вы можете слабо ссылаться на него
clang test.c -weak_library ./libdummy.dylib -flat_namespace
Символ определяется во время соединения, поэтому компоновщик удовлетворен и будет отмечен как слабо связанный в полученном двоичном файле. Связав libdummy.dylib с -weak_library
а не со стандартом -l/-L
При связывании сама зависимость библиотеки слабая, поэтому исполняемый файл все равно будет работать, даже если libdummy.dylib недоступен во время выполнения.
-flat_namespace
Аргумент говорит компоновщику использовать «плоское» пространство имен, а не «двухуровневое» пространство имен, которое, по-видимому, является стандартным в OS X. В двухуровневом пространстве имен каждый связанный символ помечается библиотекой, из которой он получен, так что без этого компоновщик будет принимать только версию weakfunc из библиотеки libdummy.dylib. Обратите внимание, что в первом случае пометка символа как неопределенного, этот символ трактуется как плоское пространство имен, поскольку компоновщик понятия не имеет, в какой библиотеке он может находиться во время выполнения.
По замыслу это не удается, потому что у компоновщика недостаточно информации. В частности, он не работает из-за комбинации двух настроек компоновщика по умолчанию:
-two_levelnamespace
-two_levelnamespace
указывает компоновщику связывать внешние символы как по имени, так и по пути установки библиотеки. При использовании компоновщик связывает символы с библиотеками на основе того, где он находит их во время компоновки, учитывая набор библиотек, которые он передал. Если компоновщик не найдет символ, он не будет знать, из какой библиотеки он пришел.
Вы можете отключить двухуровневое пространство имен с помощью -flat_namespace
, но в целом, я думаю, что это хорошая практика, чтобы оставить это.
Ld.so в Linux не поддерживает двухуровневое пространство имен, так что это не проблема. Предполагается, что каждый неопределенный символ имеет определение в некоторой библиотеке, которая будет обнаружена во время выполнения.
-undefined error
-undefined
Параметр определяет, как обрабатывать символы, которые не имеют определения, видимые во время соединения, и по умолчанию это ошибка. Другой разумный вариант dynamic_lookup
, который говорит динамическому компоновщику выяснить, где находится символ самостоятельно.
Изменение любой из этих настроек решит вашу проблему, но она сложная. Вы также можете указать компоновщику использовать динамический поиск для определенных символов и оставить значение по умолчанию error
мимоходом -U _my_weak_func
в ld
, или же -Wl,-U,_my_weak_func
в Clang (который говорит ему передать его компоновщику). _
префикс имени символа необходим.
Вы можете создать файл tbd и использовать его вместо динамических библиотек, чтобы сообщить компоновщику, где именно будет найден слабый символ, если он будет реализован, вместо того, чтобы заставлять слабые функции использовать динамический поиск. Apple использует файлы tbd для своих библиотек и фреймворков, что позволяет работать слабым ссылкам. Однако этот процесс несколько утомителен, поскольку Apple не предлагает инструментов для автоматического создания tbd-файлов для библиотек. Вам нужно будет передать файл следующего формата в виде библиотеки компилятору:
--- !tapi-tbd-v3
archs: [ $ARCH ]
uuids: [ '$ARCH: $UUID' ]
platform: $ARCH
install-name: $INSTALL_PATH
current-version: $CURRENT_VERSION
objc-constraint: none
exports:
- archs: [ $ARCH ]
symbols: [ _my_weak_func ]
...
Куда:
$ARCH
это имя архитектуры объекта, который вы хотите построить (например, «x86_64», без кавычек)$UUID
может быть запрошен с otool -l $path_to_your_lib | grep -A 2 LC_UUID
$INSTALL_PATH
и $ CURRENT_VERSION можно запросить с otool -l $path_to_your_lib | grep -A 4 LC_ID_DYLIB
Это позволит компоновщику узнать, какая библиотека должна содержать ваш слабый символ.