Comments 38
Перенаправление функций в разделяемых ELF-библиотеках
Перенаправление функций в Mach-O библиотеках
Нового сам ничего не узнал, но то что знал «причесалось» и разложилось по полочкам :)
It depends.
В Linux — да. В Darwin — нет, добавляется "_".
Конструктор Fred::Fred() помечен как weak потому что он неявно объявлен как inline, и компилятор будет генерировать для него код в кажой единице трансляции, где есть это определение, а значит линковщик должен убрать лишние копии этой функции.
Во-первых, вся информация о экспортируемых DLL символах все-таки содержится внутри. Соответственно, файл .LIB можно восстановить по файлу .DLL без особых проблем.
Если компоновщик ld позволяет просто подключить разделяемую библиотеку как входной файл, самостоятельно разбираясь, что библиотека является разделяемой и не требует статической компоновки, то компоновщик link.exe требует, чтобы все входные файлы были статическими библиотеками и никак иначе, и именно отсюда растут ноги у .LIB файла.
Кстати, тот же ld, портированный под Windows, спокойно есть .DLL файлы и не требует никаких либов, так что .LIB-файлы являются особенностью не операционной системы, а компоновщика.
PS В ситуации с циклическими зависимостями также существует альтернативное решение. Можно описать все импортируемые функции через __declspec(dllimport) и вообще не использовать .LIB-файлы.
При необходимости можно это сделать и самостоятельно. Если известен список экспортируемых функций, то надо перечислить прототипы этих функций в отдельном файле с модификатором __declspec(dllimport). После этого результат необходимо скомпилировать и сделать из этого всего статическую библиотеку. Вот и все, .LIB-файл готов.
Но один .LIB файл бессмыслен, к нему должен прилагаться заголовочный. Вот его получить уже труднее, поскольку используемые типы данных в DLL не описаны. С другой стороны, если заголовочный файл уже есть, то зачастую можно перед его включением написать что-то типа #define MYLIBAPI __declspec(dllimport), и получить требуемый в прошлом пункте файл с исходным кодом совершенно бесплатно. Или же можно вообще отказаться от использования .LIB-файла.
например, тут:
www.daniweb.com/software-development/cpp/threads/131117/how-to-create-a-.lib-from-a-.def
Важные вещи которые стоит запомнить для линукса:
1) Линкер однопроходный и обрабатывает строку линковки слева-направо. Поэтому при линковке важнен порядок объектных файлов и библиотек. Включить многопроходную линковку в пределах группы можно с помощью: --Wl,--start-group… -Wl,--end-group — внутри группы линкер станет многопроходным и возможно разрешение кросс-зависимостей;
2) При линковке динмаических библиотек линкер смотрит архитектуру. Невозможно слинковать в 1 бинарь разные архитектуры: x86_84 и i386 не слинкуется, одна из них будет отброшена. Проверяйте свой LD_LIBRARY_PATH. Допускается в пути иметь разные архитектуры, линкер сам выберет подходящую.
3) Множественное объявление символа (multiple definition of) скорее всего не ошибка, лечится -Wl,-z,muldefs.
Поиск потерявшегося символа — в какой библиотеке вполне можно делать и через Midnight-поиск, можно через nm |grep — как вам удобнее. Обычно если линковка сломана нужно несколько итераций линковка-поиск-исправление мейкфайла, дописываем найденные библиотеки справа. Допускается повторение библиотек в строке линковки, обычно при этом самый правый повтор можно убрать как избыточный (за исключением случая с кросс-сылками).
Fred theFred;
Fred theOtherFred(55);
…
theFred |00000000| B | OBJECT|00000008| |.bss
theOtherFred |00000008| B | OBJECT|00000008| |.bss
global constructors keyed to theFred
Вызов конструктора объявлен для theOtherFred, по в объектном файле указан theFred.
Парочка исправлений:
по средством — посредством
Упущено слово или более на месте многоточия в разделе «Статические библиотеки»:
которая…a.o, b.o, -lx и -ly.
Хорошая статься. Автор молодец, я прочитал с удовольствием.
Еще одно исправление: LD_LIBARRY_PATH наверно имелось ввиду LD_LIBRARY_PATH
По загрузке кода немного дополню, если не возражаете на примере powerpc архитектуры,
инструментарий GNU:
файлик crt0.S который пришлёпывается к программе для начальной инициализации
имеет код для зачистки .bss, sbss и т.п., а для того-чтобы секция не загружалась,
надо в атрибутах выставлять (NOLOAD), загрузчик это видит и пропускает её,
если например вы добавили свои секции и не хотите чтобы они загружались.
/* clean sbss section */
lis 5, .LCTOC1@h
ori 5, 5, .LCTOC1@l
lwz 6, .lsbss_start(5)
lwz 7, .lsbss_end(5)
cmplw 6, 7
beq mk_clean_bss
а потом в .init пробегает по секции .ctors, где хранятся адреса
глобальных конструкторов:
static fptr_t ctors_list[1] __attribute__((section(".ctors.begin"), __used__)) = { (fptr_t) -1 };
static fptr_t ctors_end[1] __attribute__((section(".ctors.end"), __used__)) = { (fptr_t) 0 };
void __do_global_ctors_aux ( void )
{
fptr_t *fp;
for( fp = ctors_list + 1; *fp != 0; fp++ )
(**fp)( );
}
ну и еще конечно функция __gxx_personality для компоновки исключений в секцию .eh_frame
А при адресации в .so, насколько я помню используется секция .got (Global Offset Table), было бы
неплохо если бы вы еще статью по этому поводу написали, было бы интересно её прочесть.
Успехов!
Нашел одну неточность, как по мне.
… статичные глобальные переменные также являются глобальными с той лишь разницей, что они доступны только в пределах одного файла, где они определены.
Статические глобальные переменные видны не в файле, а в единице трансляции. То есть если статическая переменная определена в *.h файле, то при включении этого файла в другой она будет видна и там.
Так как это одна из лучших статей, что я видел, то будет правильно убрать любую неточность:)
И стандарт С им не указ, ибо линкер в общем случае не привязан к компилятору — может например собирать Паскаль или Клиппер программы.
Статья хорошая, но возможно — требует уточнений.
Руководство новичка по эксплуатации компоновщика