2009-07-31 3 views

ответ

2

A .so аналогичен .dll на окнах. A .o в точности совпадает с .obj в Visual Studio.

65

Допустим, у вас есть следующий исходный файл C, вызовите его name.c

#include <stdio.h> 
#include <stdlib.h> 

void print_name(const char * name) 
{ 
    printf("My name is %s\n", name); 
} 

Когда вы скомпилировать его с cc name.c вы создаете name.o. В файле .o содержится скомпилированный код и данные для всех функций и переменных, определенных в name.c, а также индекс, связанный с их именами с фактическим кодом. Если вы посмотрите на этот показатель, скажем, с nm инструмента (доступен на Linux и многих других Unixes) вы увидите две записи:

00000000 T print_name 
     U printf 

Что это означает: есть два символа (имена функций и переменных, но не имена классов, структур или любых типов), хранящихся в .o. Первая, отмеченная T, фактически содержит ее определение в name.o. Другой, обозначенный U, является просто ссылкой . Код для print_name можно найти здесь, но код для printf не может быть найден. Когда ваша фактическая программа запускается, вам нужно будет найти все символы, которые являются ссылками, и найти их определения в других объектах, чтобы их можно было связать вместе в полную программу или полную библиотеку. Таким образом, объектный файл - это определения, найденные в исходном файле, преобразованные в двоичную форму и доступные для размещения в полной программе.

Вы можете связать файлы .o по одному, но вы этого не сделаете: их, как правило, много, и они являются деталями реализации. Вы бы предпочли, чтобы все они были собраны в пучки связанных объектов с хорошо распознанными именами. Эти пучки называются библиотеками и они бывают двух форм: статические и динамические.

статическая библиотека (в Unix) почти всегда с суффиксом .a (примеры включают libc.a, которая является основной библиотеки C, libm.a которая является математической библиотеки C) и так далее. Продолжая пример, вы создадите свою статическую библиотеку с помощью ar rc libname.a name.o. Если запустить nm на libname.a вы увидите это:

name.o: 
00000000 T print_name 
     U printf 

Как вы можете видеть это в первую очередь большая таблица объектных файлов с индексом найти все имена в нем. Подобно объектным файлам, он содержит оба символа, определяемые в каждом .o, и символы, на которые они ссылаются. Если вы должны были указать в еще один .o (например, date.o - print_date), вы увидите еще одну запись, подобную приведенной выше.

Если вы связали статическую библиотеку с исполняемым файлом, она введет всю библиотеку в исполняемый файл. Это похоже на соединение всех файлов .o. Как вы можете себе представить, это может сделать вашу программу очень большой, особенно если вы используете (как большинство современных приложений) множество библиотек.

динамической или совместно используемую библиотеку с суффиксом .so. Он, как и его статический аналог, представляет собой большую таблицу объектных файлов, ссылаясь на весь скомпилированный код. Вы бы построили его с помощью cc -shared libname.so name.o. Глядя на nm, это немного отличается от статической библиотеки. В моей системе он содержит около двух десятков символов только два из которых являются print_name и printf:

00001498 a _DYNAMIC 
00001574 a _GLOBAL_OFFSET_TABLE_ 
     w _Jv_RegisterClasses 
00001488 d __CTOR_END__ 
00001484 d __CTOR_LIST__ 
00001490 d __DTOR_END__ 
0000148c d __DTOR_LIST__ 
00000480 r __FRAME_END__ 
00001494 d __JCR_END__ 
00001494 d __JCR_LIST__ 
00001590 A __bss_start 
     w [email protected]@GLIBC_2.1.3 
00000420 t __do_global_ctors_aux 
00000360 t __do_global_dtors_aux 
00001588 d __dso_handle 
     w __gmon_start__ 
000003f7 t __i686.get_pc_thunk.bx 
00001590 A _edata 
00001594 A _end 
00000454 T _fini 
000002f8 T _init 
00001590 b completed.5843 
000003c0 t frame_dummy 
0000158c d p.5841 
000003fc T print_name 
     U [email protected]@GLIBC_2.0 

Разделяемая библиотека отличается от статической библиотеки в одном очень важном аспекте: не встраивать себя в своем окончательном исполняемый файл. Вместо этого исполняемый файл содержит ссылку на эту разделяемую библиотеку, которая разрешена, а не время соединения, но во время выполнения. Это имеет ряд преимуществ:

  • Ваш исполняемый файл намного меньше. Он содержит только код, явно связанный через объектные файлы. Внешние библиотеки - это ссылки, и их код не входит в двоичный файл.
  • Вы можете разделить (отсюда название) биты одной библиотеки между несколькими исполняемыми файлами.
  • Если вы внимательно относитесь к бинарной совместимости, обновите код в библиотеке между прогонами программы, и программа подберет новую библиотеку, не требуя ее изменения.

Есть некоторые недостатки:

  • Это занимает время, чтобы связать программу вместе. В общих библиотеках часть этого времени откладывается до каждого запуска исполняемого файла.
  • Процесс более сложный. Все дополнительные символы в общей библиотеке являются частью инфраструктуры, необходимой для того, чтобы связать библиотеку во время выполнения.
  • Вы подвергаетесь риску тонкой несовместимости между различными версиями библиотеки. В Windows это называется «DLL-ад».

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

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

+8

Следует отметить, что общие библиотеки являются обычным способом реализации плагинов. Плагин - это способ расширения функциональности приложения без его изменения. Приложение может загружать общую библиотеку, вызывая в dlopen() путь к библиотеке a аргумент, а затем dlsym(), чтобы найти конкретный символ в библиотеке (например, функция). Затем приложение вызывает функцию для выполнения функций из библиотеки. – dimba

+1

Хорошее знакомство с миром погрузки и связи :) +1 – xtofl

+0

Красивые объяснения :) +1 –

 Смежные вопросы

  • Нет связанных вопросов^_^