Связь между объектным файлом и общим объектным файлом

какова связь между файлом общего объекта (.so) и файлом объекта (.o)?

не могли бы вы объяснить на примере?


person ASHOK    schedule 31.07.2009    source источник


Ответы (2)


Допустим, у вас есть следующий исходный файл на 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 и многих других Unix), вы заметите две записи:

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 __cxa_finalize@@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 printf@@GLIBC_2.0

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

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

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

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

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

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

person quark    schedule 31.07.2009
comment
Стоит отметить, что общие библиотеки - это распространенный способ реализации плагинов. Плагин - это способ расширить функциональность приложения, не меняя его. Приложение может загрузить разделяемую библиотеку, вызвав dlopen () с указанием пути к библиотеке в качестве аргумента, за которым следует dlsym (), чтобы найти определенный символ в библиотеке (например, функцию). Затем приложение вызывает функцию для выполнения функций из библиотеки. - person dimba; 31.07.2009
comment
Приятное введение в мир загрузки и линковки :) +1 - person xtofl; 31.07.2009
comment
Ваш ответ содержит удивительное количество неточностей. Не могли бы вы пометить это как вики сообщества, чтобы их можно было исправить? - person Employed Russian; 04.08.2009
comment
@quark: Вы упомянули, что связывание со статической библиотекой похоже на связывание всех файлов .o. Будет ли gcc на самом деле преобразовывать все файлы .o в окончательный исполняемый файл или выберет только те, которые действительно упоминаются? Я использовал это (tenouk.com/Bufferoverflowc/Bufferoverflow1c.html) как ссылку. - person dma_k; 02.10.2010
comment
@ Занятые русские: Не могли бы вы упомянуть эти неточности? - person dma_k; 02.10.2010
comment
@dma_k Вот несколько ложных утверждений: когда ваша фактическая программа запускается, ей нужно будет найти все символы, которые являются ссылками, и найти их определения в других объектных файлах, чтобы их связать вместе в полную программу или полную библиотеку - это не На самом деле это не происходит во время выполнения, это происходит во время статической ссылки. Если вы подключаете статическую библиотеку к исполняемому файлу, она встраивает всю библиотеку в исполняемый файл - просто ложь. - person Employed Russian; 03.10.2010
comment
@ Занятые русские: Спасибо. Я согласен с нашим вторым замечанием, которое также обсуждается здесь: stackoverflow.com/questions/1804606/ - person dma_k; 04.10.2010
comment
Если я попытаюсь скомпилировать с использованием cc name.c, ld не удастся: /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o: In function '_start': (.text+0x20): undefined reference to 'main' - person Drew Noakes; 31.01.2013

.So аналогичен .dll в Windows. .O - это то же самое, что .obj в Visual Studio.

person Jim Buck    schedule 31.07.2009