PVOID - Разное

Разное

Команда nm


  Команда nm может сообщать список символов указанной библиотеки. Она работает как со статическими, так и с общими библиотеками. Для указанной библиотеки nm(1) может перечислить определенные символы, значение каждого символа и его тип. Также она может указать, где символ был определен в исходном коде (по имени файла и номеру строки), если эта информация доступна в библиотеке (смотрите опцию -l).

 Тип символа требует немного больше объяснений. Тип отображается как буква; нижний регистр обозначает, что символ локальный, в то время как верхний регистр обозначает, что символ глобальный (внешний). Обычные типы символов включают T (нормальное определение в секции кода), D (секция инициализированных данных), B (секция неинициализированных данных), U (не определен; символ используется библиотекой, но не определен в ней), и W (слабый; если другая библиотека также определяет этот символ, ее определение переопределяет это).

 Если вы знаете имя функции, но вы не можете вспомнить, в какой библиотеке она была определена, вы можете использовать опцию «-o» (которая добавляет имя файла на каждой строке) вместе с grep, чтобы найти имя библиотеки. Из Bourne shell, вы можете найти все библиотеки в /lib, /usr/lib, непосредственных подкаталогах /usr/lib и /usr/local/lib для «cos» следующим образом:

nm -o /lib/* /usr/lib/* /usr/lib/*/* \
      /usr/local/lib/* 2> /dev/null | grep 'cos$'

 Более подробную информацию о nm можно найти в документации «info» для nm, локально установленной как info:binutils#nm.

 

 Конструктор и деструктор библиотеки


 Библиотеки должны экспортировать процедуры инициализации и очистки, используя атрибуты функции gcc __attribute__((constructor)) и __attribute__((destructor)). смотрите страницы info для gcc для дополнительной информации об этом. Процедура конструктора выполняется перед возвратом из dlopen (или перед стартом main(), если библиотека загружена при запуске программы). Процедура деструктора выполняется перед выходом из dlclose (или после exit() или завершения main(), если библиотека загружена при запуске программы). Прототипы этих на языке Си:

void __attribute__ ((constructor)) my_init(void);
void __attribute__ ((destructor)) my_fini(void);

 Общие библиотеки не должны компилироваться с аргументами gcc «-nostartfiles» или «-nostdlib». Если используются эти аргументы, процедуры конструктора/деструктора не выполнятся (если не приняты специальные меры).

 

 Специальные функции _init и _fini (УСТАРЕВШИЕ/ОПАСНЫЕ)


 Исторически были две специальные функции, _init и _fini, которые могут быть использованы для конструкторами и деструкторами. Однако, они являются устаревшими, и их использование может привести к непредсказуемым результатам. Ваши библиотеки не должны использовать их; вместо этого используйте атрибуты функции constructor и destructor, описанные выше.

 Если вам неоходимо работать со старыми системами или кодом, который должен использует _init или _fini, вот как они работали. Две специальные функции были определены для инициализации и очистки модуля: _init и _fini. Если функция «_init» экспортируется в бибилотеку, то она вызывается, когда библиотека открывается впервые (через dlopen() или просто как общая библиотека). В программе на языке Си, это означает только то, что вы определяете некоторую функцию с именем _init. Также есть соответствующая функция с именем _fini, которая вызывается каждый раз, когда клиент прекращает работать с библиотекой (через вызов dlclose(), который устанавливает счетчик ссылок в ноль, или при нормальном выходе из программы). Прототипы этих функций на языке Си таковы:

void _init(void);
void _fini(void);

 В этом случае, при компиляции файла в «.o» файл в gcc, обязательно добавьте опцию «-nostartfiles». Это удерживает компилятор Си от компоновки начальных системных библиотек с файлом .so . Иначе, вы получите ошибку «multiple-definition» (многократно определенный символ). Обратите внимание, что это полностью отличается от компиляции модулей с использованием рекомендованных атрибутов функций. Благодарю Джима Мишеля и Тима Джентри за предложение добавить обсуждение _init и _fini, а также помощь в его создании.

 

 Общие библиотеки могут быть скриптами


 Стоит отметить, что загрузчик GNU позволяет общим библиотекам быть текстовыми файлами с использованием специализированного языка сценариев вместо обычного формата библиотеки. Это полезно для непрямого объединения других библиотек. Например, вот листинг /usr/lib/ libc.so на одной из моих систем:

/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

 Для дополнительной информации об этом, смотрите документацию texinfo на скрипты компоновщика ld (ld command language). Общая информация находится в info:ld#Options и info:ld#Commands.

 

 Контроль версий символов и скрипты версий


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

 Решением этой проблемы является контроль версий символов в сочетании со скриптами версий. С контролем версий, пользователь может получить предупреждение, когда он запускает свою программу, если используемые библиотеки слишком старые. Вы можете узнать больше об этом из описания сценариев версий руководства ld https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_25.html.

 

 GNU libtool


 Если вы строите приложение, которое должно быть портировано на множество систем, вы можете рассмотреть использование GNU libtool для сборки и установки библиотек. GNU libtool — это общий скрипт поддержки библиотек. Libtool скрывает сложности использования общих библиотек за единообразным переносимым интерфейсом. Libtool предоставляет переносимые интерфейсы для создания объектных файлов, компоновки библиотек (статических и общих), компоновки исполняемых файлов, отладки исполняемых файлов, установки библиотек, установки исполняемых файлов. GNU libtool также включает libtdl, переносимую обертку для динамически загружаемых программ. Для дополнительной информации, смотрите документацию на http://www.gnu.org/software/libtool/manual.html.

 

Удаление символов для освобождения пространства


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

 Лучшим подходом является нормальная генерация объектных файлов, выполнение отладки и тестирования (отладка и тестирование намного проще с ними). После того, как вы тщательно протестировали программу, используйте strip(1) для удаления символов. Команда strip(1) дает вам хороший контроль над тем, какие символы исключить; смотрите документацию на strip(1) для дополнительной информации.

 Другим подходом является использование опций компоновщика GNU ld «-S» и «-s»; «-S» исключает отладочную информацию о символах (но не все символы) из выходного файла, тогда как «-s» исключает всю информацию о символах из выходного файла. Вы можете использовать эти опции из gcc таким образом: «-Wl,-S» и «-Wl,-s». Если вы всегда удаляете символы и этих опций достаточно, не стесняйтесь, но это не самый гибкий подход.

 

 Очень маленькие исполняемые файлы


 Вы можете найти статью Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux полезной. Она описывает, как сделать действительно крошечный исполняемый файл. Честно говоря, вы не должны использовать большинство этих трюков в нормальных условиях, но они весьма поучительны, показывая, как на самом деле работает ELF.

 

 C++ против C


 Стоит отметить, что если вы пишете программу на C++, и вы вызываете функцию из библиотеки C, в вашем коде на C++ вам необходимо объявить функцию C как extern "C". В противном случае компоновщик не сможет найти функцию C. Внутренне компиляторы C++ «искажают» имена функций C++ (например, с целью типизации), и им необходимо сообщить, что данная функция должна быть вызвана как функция C (и поэтому не имеет искаженного имени).

 Если вы пишете библиотеку, которая может быть вызвана из C или C++, рекомендуется включать команды extern "C" непосредственно в ваши заголовочные файлы, так что вы делаете это автоматически для ваших пользователей. В сочетании с обычным #ifndef в начале файла, чтобы пропустить повторное включение заголовочных файлов, это означает, что обычный заголовочный файл, используемый C или C++, для некоторого заголовочного файла foobar.h будет выглядеть так:

/* Объясните здесь, что делает foobar */

#ifndef FOOBAR_H
#define FOOBAR_H

#ifdef __cplusplus
extern "C" {
#endif

 ... заголовок foobar расположите здесь ...

#ifdef  __cplusplus
}
#endif
#endif

 

 Ускорение инициализации C++


 Разработчики KDE могли заметить, что запуск больших GUI приложений на C++ может занять много времени, частично из-за необходимости сделать множество релокаций. Существует несколько решений этой проблемы. Смотрите «Making C++ ready for the desktop (by Waldo Bastian)» для дополнительной информации.

 

 Оптимизация времени компоновки


 GCC 4.5 добавил новую опцию оптимизации, -flto. Когда исходные файлы скомпилированы и скомпонованы с использованием -flto, GCC применяет оптимизации, как если бы весь исходный код находился в одном файле, что позволяет ему производить намного более агрессивные оптимизации. Вы должны также использовать уровень оптимизации (-O). Это делает устаревшим старую опцию -combine, но вы можете захотеть серьезно рассотреть объединение её с -fwhole-program. Если вы хотите сделать это, вам необходимо использовать -flto на каждом шаге процесса сборки, и вы должны использовать точно такие же оптимизации и машинозависимые опции каждый раз. Статья «What’s new in GCC 4.5?» in Linux Weekly News (LWN) содержит больше информации об этом.

 

 Linux Standard Base (LSB)


 Целью проекта Linux Standard Base (LSB) является разработка и продвижение набора стандартов, которые повысят совместимость между дистрибутивами Linux и позволят приложениям работать в любой совместимой системе Linux. Домашняя страница проекта находится по адресу http://www.linuxbase.org.

 Хорошая статья, которая рассказывет как разрабатывать LSB-совместимые приложения, была опубликована в октябре 2002, Developing LSB-certified applications: Five steps to binary-compatible Linux applications, Джорджем Крафтом IV (Старший инженер-программист технологического центра Linux, IBM). Конечно, вам нужно написать код, который обращается к стандартизированному уровню переносимости, только если вы хотите, чтобы ваш код был переносимым. В дополнение, LSB предоставляет некоторые инструменты, так что разработчики приложений на C/C++ могут проверить соответствие LSB; эти инструменты используют некоторые возможности компоновщика и специальные библиотеки, чтобы делать эти проверки. Очевидно, что вам нужно установить инструменты для выполнения этих проверок; вы можете получить их с вебсайта LSB. Затем просто используйте компилятор «lsbcc», как ваш компилятор C/C++ (lsbcc внутренне создает окружение для компоновщика, которое будет выдавать предупреждения, если определенные правила LSB не соблюдаются):

$ CC=lsbcc make myapplication
  (или)
$ CC=lsbcc ./configure; make myapplication 

 Затем вы можете использовать программу lsbappchk, чтобы убедиться, что программа использует только функции, стандартизированные LSB.

$ lsbappchk myapplication

 Также вам необходимо следовать инструкциям по созданию пакетов LSB (т.е. использовать RPM v3, использовать LSB-совместимые имена пакетов, а вспомогательное ПО должно по умолчанию устанавливаться в /opt). Смотрите статью и сайт LSB для получения дополнительной информации.

 

 Слияние библиотек в большие общие библиотеки


 Что, если вы хотите сначала создать маленькие библиотеки, а затем позже объединить их в большие библиотеки? В этом случае, вы можете найти полезной опцию компоновщика «--whole-archive», которая может быть использована для принудительного извлечения файлов .a и компоновки их в файл .so .

 Вот пример использования --whole-archive:

gcc -shared -Wl,-soname,libmylib.$(VER) -o libmylib.so $(OBJECTS) \
-Wl,--whole-archive $(LIBS_TO_LINK) -Wl,--no-whole-archive \
$(REGULAR_LIBS)

 Как отмечает документация ld, обязательно используйте опцию --no-whole-archive в конце, или gcc также попытается объединить стандартные библиотеки. Спасибо Кендаллу Беннетту за то, что он предложил этот способ.

 

 Другая информация


 How to Write Shared Libraries Ульриха Дреппера из Red Hat (28 августа 2004) содержит дополнительную информацию, которая может быть полезной.


Этот раздел является переводом руководства Program Library HOWTO


  <<< Назад Содержание Вперед >>>