В этой главе мы сосредоточим свое внимание только на формате ELF (Executable and Linking Format — формат исполняемых и компонуемых файлов), поскольку именно он используется для создания исполняемых файлов и разделяемых библиотек в современных версиях Linux (а также во многих других реализациях UNIX).
ELF заменил собой старые форматы a.out и COFF.
41.4.1. Создание разделяемой библиотеки
Для построения разделяемой версии статической библиотеки, созданной ранее, нужно выполнить следующие шаги:
$ gcc — g — c — fPIC — Wall mod1.c mod2.c mod3.c
$ gcc — g — shared — o libfoo.so mod1.o mod2.o mod3.o
Первая команда создает три объектных модуля (мы объясним назначение параметра cc — fPIC в следующем разделе). Команда cc — shared создает на основе этих модулей разделяемую библиотеку.
Имена разделяемых библиотек принято обрамлять префиксом lib и суффиксом. so (от англ. shared object — «разделяемый объект»).
Для демонстрации того, что параметры командной строки, которые мы задействуем при создании разделяемых библиотек, не зависят от компилятора, в наших примерах вместо cc указана команда gcc. Другой компилятор в иной UNIX-системе, вероятно, потребует пересмотра этих параметров.
Стоит также отметить, что скомпилировать исходные файлы и создать разделяемую библиотеку можно с помощью всего одной команды:
$ gcc — g — fPIC — Wall mod1.c mod2.c mod3.c — shared — o libfoo.so
Но чтобы четко разделить этапы компиляции и сборки библиотеки, в примерах, приводимых в данной главе, для этого будут использоваться две отдельные команды.
В отличие от статических готовые разделяемые библиотеки не позволяют добавлять или удалять отдельные объектные модули. Как и в случае с обычными исполняемыми программами, объектные файлы внутри разделяемой библиотеки теряют свою идентичность.
41.4.2. Адресно-независимый код
Параметр cc — fPIC заставляет компилятор сгенерировать
Платформа Linux/x86-32 позволяет создавать исполняемые библиотеки на основе модулей, скомпилированных без параметра — fPIC. Однако в этом случае теряются некоторые преимущества разделяемых библиотек, так как страницы программного кода, содержащие адресно-независимые ссылки, не могут быть разделены между процессами. В ряде архитектур невозможно собрать разделяемую библиотеку без параметра — fPIC.
Чтобы определить, был ли имеющийся объектный файл скомпилирован с использованием данного параметра, можно проверить наличие имени _GLOBAL_OFFSET_TABLE_ в его таблице символов. Для этого подойдет любая из следующих двух команд:
$ nm mod1.o | grep _GLOBAL_OFFSET_TABLE_
$ readelf — s mod1.o | grep _GLOBAL_OFFSET_TABLE_
С другой стороны, если одна из нижеприведенных команд выведет что-либо на экран, это будет значить следующее: заданная разделяемая библиотека содержит как минимум один объектный модуль, скомпилированный без параметра — fPIC:
$ objdump — all-headers libfoo.so | grep TEXTREL
$ readelf — d libfoo.so | grep TEXTREL
Строка TEXTREL указывает на наличие объектного модуля, чей текстовый сегмент содержит ссылку, требующую перемещения кода на этапе выполнения.
Более подробно команды nm, readelf и objdump будут рассмотрены в разделе 41.5.
41.4.3. Использование статической библиотеки
Перед применением разделяемой библиотеки необходимо выполнить два шага, которые не требуются для работы со статическими библиотеками.
• Поскольку исполняемый файл больше не содержит копии нужных ему объектных модулей, он должен иметь возможность определять, какая разделяемая библиотека требуется для его работы. Для этого на этапе компоновки в исполняемый файл внедряется имя библиотеки (говоря в терминологии формата ELF, библиотечная зависимость записывается в метку DT_NEEDED исполняемого файла). Набор всех разделяемых библиотек, которые нужны программе, называют