Представьте, что вы разрабатываете некую софтину, и хотите разделить её на части. Одна часть по жизни должна оставаться постоянной, а другая часть (или много частей) могут заменяться или подгружаться по потребности. Типичный пример: ядро операционной системы и программы юзера, или графический редактор и его плагины.
Между частями софтины имеется определённый программный интерфейс: обычно набор процедур/функций, заданный на некоем языке, скажем Си. Постоянная часть реализует эти функции и "экспортирует" их для потребления переменными частями. То есть имеем некоторый include-файл, скажем "api.h".
Переменные части, или плагины, удобно компилировать в виде разделяемых библиотек в позиционно независимом коде, чтобы можно было подгрузить и выполнить в любом удобном месте.
Внимание, вопрос: как скомпилировать такой плагин? Ведь его не с чем линковать. У нас нет никакой библиотеки типа "api.so", которую можно было бы подсунуть линкеру.
Разберём на примере. Пусть основной код предоставляет плагину всего одну функцию: tell_user(string). Вот такой файл api.h:
Оказывается, есть способ. Компилятор clang умеет извлекать из Си-шных исходников описание интерфейса в формате YAML. А отдельная утилита llvm-ifs может делать из этого описания бинарник разделяемой библиотеки для линкера.
Делаем раз, получаем описание интерфейса:
Подробная статья про внутреннее устройство ELF-файла с динамическим связыванием: https://mcuoneclipse.com/2021/06/05/position-independent-code-with-gcc-for-arm-cortex-m/
Между частями софтины имеется определённый программный интерфейс: обычно набор процедур/функций, заданный на некоем языке, скажем Си. Постоянная часть реализует эти функции и "экспортирует" их для потребления переменными частями. То есть имеем некоторый include-файл, скажем "api.h".
Переменные части, или плагины, удобно компилировать в виде разделяемых библиотек в позиционно независимом коде, чтобы можно было подгрузить и выполнить в любом удобном месте.
Внимание, вопрос: как скомпилировать такой плагин? Ведь его не с чем линковать. У нас нет никакой библиотеки типа "api.so", которую можно было бы подсунуть линкеру.
Разберём на примере. Пусть основной код предоставляет плагину всего одну функцию: tell_user(string). Вот такой файл api.h:
Простейший плагин, выдающий юзеру сообщение:void tell_user(const char *);
Пытаемся компилировать, естественно, получаем ошибку:#include "api.h"
void _start()
{
tell_user("Hi!");
}
Линкеру нужно подсунуть некую разделяемую библиотеку api.so, чтобы он увидел там внешний символ tell_user и настроил связи на него. Как сделать такую библиотеку?$ cc -shared -fPIC plugin.c -o plugin.elf
plugin.c: undefined reference to `tell_user'
Оказывается, есть способ. Компилятор clang умеет извлекать из Си-шных исходников описание интерфейса в формате YAML. А отдельная утилита llvm-ifs может делать из этого описания бинарник разделяемой библиотеки для линкера.
Делаем раз, получаем описание интерфейса:
Делаем два, компилируем интерфейс в бинарник api.so:$ clang -c -emit-interface-stubs api.h
$ cat api.ifs
--- !ifs-v1
IfsVersion: 3.0
Target: x86_64-apple-macosx13.0.0
Symbols:
- { Name: "tell_user", Type: Func }
...
Теперь можем скомпилировать наш плагин:$ llvm-ifs api.ifs --output-elf=api.so
$ file api.so
api.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), no program header, stripped
Дело сделано, бинарник плагина готов. Теперь можно заняться реализацией собственно основной софтины, загружающей и выполняющей этот бинарник.$ cc -shared -fPIC plugin.c api.so -o plugin.elf
$ size plugin.elf
text data bss dec hex filename
677 344 80 1101 44d plugin.elf
Подробная статья про внутреннее устройство ELF-файла с динамическим связыванием: https://mcuoneclipse.com/2021/06/05/position-independent-code-with-gcc-for-arm-cortex-m/