Как использовать dlpreopening/предварительную загрузку libtool / libltdl?

#c #compilation #linker #static-linking #libtool

Вопрос:

Вот грубый пример кода, с которым я хотел бы использовать dlpreopening libtool libltdl:

https://github.com/EmmaJaneBonestell/dlopen-sample

Я хочу иметь возможность переписывать различные проекты, использующие функции libdl ( dlopen, dlsym и т. Д. ), Чтобы вместо этого использовать механизм предварительного открытия/предварительной загрузки libtool libtdl. Из документации libtool следует, что вместо этого он будет связывать объекты во время компиляции, что приведет к созданию действительно статических исполняемых файлов. Он был предназначен для систем, которые не поддерживают динамическую загрузку.

Если вы используете его для компиляции и связывания этих объектов, libtool запустит некоторые из своих сценариев и создаст необходимые структуры данных, а также, я полагаю, выполнит искажение и разборку, чтобы разрешить дублирование имен символов.

Даже после просмотра примеров в исходном коде libtool, прочтения всей документации и просмотра соответствующих тестов из набора тестов (например, тестов 118 и 120), я не смог этого сделать.

За пределами самого libtool, по сути, нигде нет упоминания об этой функции, которую я мог бы найти. Мне удалось увидеть, как он используется слишком сложным для меня способом в Graphviz, но на этот счет также мало документации.

Я действительно не очень большой программист, скорее мастер. Мне достаточно просто использовать libltdl в качестве оболочки для стандартного dlopen/dlsym.

Я думаю, что моя главная проблема заключается в том, чтобы выяснить, как я могу вернуть правильный дескриптор для предварительно открытого объекта, хотя я, возможно, делаю другие вещи неправильно.

В документации говорится, что lt_dlopen может работать с предварительно загруженными статическими модулями, но, похоже, он не принимает никаких возможных ссылок на него, которые я мог бы себе представить.

Я бы также предпочел даже не вызывать libtool, но c’est la vie.

Я не потрудился включить что-либо из того, что я пробовал с точки зрения кода (на примере), потому что я перепробовал такой беспорядок вещей, что я чувствую, что на самом деле было бы просто запутанно показывать это.

Текущая документация по libtool: https://www.gnu.org/software/libtool/manual/libtool.html

Ответ №1:

По-видимому, libtool требует .файл la должен присутствовать и его rpath должен быть правильно объявлен, даже если он не будет использоваться ни для чего в этом действительно статическом двоичном файле.

Документация Libtool достаточно ясно показывает, что ваши файлы «модуля» должны содержать следующий макрос препроцессора для всех экспортируемых символов.

 #define SYMBOL SOURCEFILENAME_LTX_SYMBOL
 

В моем случае это приводит к тому, что бром.c и хлор.c имеют следующее, соответственно.

 #define chemical_name bromine_LTX_chemical_name
#define chemical_name chlorine_LTX_chemical_name
 

Все еще предполагая хранилище кода в моем вопросе, следующие команды являются примером того, как использовать libtool для автоматического предварительного открытия двоичного файла.

 libtool --mode=compile gcc -static -fPIC -fno-plt -c bromine.c

libtool --mode=compile gcc -static -fPIC -fno-plt -c chlorine.c

libtool --mode=link gcc -all-static -fPIC -fno-plt -o bromine.la -rpath $PWD -module -no-undefined -avoid-version bromine.lo

libtool --mode=link gcc -all-static -fPIC -fno-plt -o chlorine.la -rpath $PWD -module -no-undefined -avoid-version chlorine.lo
 

Однако для меня гораздо проще пропустить каждый шаг libtool и просто объявить необходимый массив структур внутри самого main.c. Объявите переопределенные функции/символы как внешнее значение int, независимо от их типа, и создайте массив lt_dlsymlist с именем lt__PROGRAM__LTX_предварительно загруженные_символы. Первым элементом будет сама программа, а последним будет {0, (void *) 0}. Между ними в одной строке структуры будет объявлено базовое имя предварительно открытого объекта/статического архива с суффиксом файла «.a».

Он будет иметь суффикс .a независимо от того, является ли это файлом .a,. o,. c или любым другим файлом. Следующей структурой(ами) будут все экспортированные из нее символы, которые вы хотите использовать, затем, если хотите, другое имя файла, его символы и так далее. В именах символов будет использоваться (void *) amp;СИМВОЛ вместо (void *) 0, как и во всех других полях.

 extern int bromine_LTX_chemical_name();
extern int chlorine_LTX_chemical_name();

const lt_dlsymlist lt__PROGRAM__LTX_preloaded_symbols[] ={ 
  {"@PROGRAM@", (void *) 0},
  {"chlorine.a", (void *) 0},
  {"chlorine_LTX_chemical_name", (void *) amp;chlorine_LTX_chemical_name},
  {"bromine.a", (void *) 0},
  {"bromine_LTX_chemical_name", (void *) amp;bromine_LTX_chemical_name},
  {0, (void *) 0}
};
 

Наконец, lt_dlopen не отражает объявления lt_dlsymlist-в идеале вы будете вызывать только базовое имя файла без его расширения. например

 handle = lt_dlopen("chlorine");
 

НЕ

 handle = lt_dlopen("chlorine.a")
 

Тем не менее, я считаю, что это будет работать независимо от расширения, которое вы предоставляете, поскольку функция lt_dlopen удаляет расширение.

Выполнение этого вышеописанным способом может сократить вашу компиляцию до одной строки и вообще избежать libtool. Например

 gcc -static -Wl,-static -fPIC -fno-plt -Wl,-z,now,--no-undefined -o main main.c bromine.c chlorine.c -lltdl -ldl
 

Я также проверил, что это создает действительно статический двоичный файл, связавшись с отладочной сборкой libltdl со всем, но предварительно удаленным из нее (без libdl и т. Д.).

 $./main HOBr HOCl

loaders: lt_preopen
try_dlopen (bromine, (null))
tryall_dlopen (bromine.a, lt_preopen)
Calling lt_preopen->module_open (bromine.a)
  Result: Success
try_dlopen (chlorine, (null))
tryall_dlopen (chlorine.a, lt_preopen)
Calling lt_preopen->module_open (chlorine.a)
  Result: Success
The IUPAC name of HOBr is hypobromous acid.
The IUPAC name of HOCl is hypochlorous acid.
 

Комментарии:

1. Вау, этот ответ очень своевременен. Я гуглил это несколько дней назад и не смог найти никаких хороших ссылок. Не могли бы вы, пожалуйста, обновить связанное репо предложенным решением? Я бы хотел посмотреть, как все это работает вместе.

2. Я попытался последовать вашему примеру, но похоже, что libltdl не видит символов lt__PROGRAM__LTX_preloaded_ — они не становятся частью предварительно загруженных списков_символов.

3. Да, я немедленно обновлю репо соответствующим образом, возможно, в подпапке. Я не собираюсь показывать, как это сделать с помощью libtool-просто как это сделать самостоятельно связывание libltdl.

4. Да, сейчас я рад, что это работает в любом случае. Я пробовал как -dlpreopen с помощью libtool, так и вручную, как показано выше, но каждый раз одна и та же проблема — символы связываются, но они не отображаются в предварительно загруженных списках имен, поэтому libltdl продолжает пытаться вернуться к dlopen.

5. На самом деле… Я думаю, что вижу, что я пропустил в версии, отличной от libtool, я, вероятно, позвонил LTDL_SET_PRELOADED_SYMBOLS слишком поздно в процессе.