Почему статические и динамически подключаемые библиотеки отличаются?

Если оба они содержат скомпилированный код, почему мы не можем загружать «статические» файлы во время выполнения и почему мы не можем связываться с динамическими библиотеками во время компиляции? Почему необходимо, чтобы отдельные форматы содержали «автономный» код? Что нужно хранить в любом из них, что гарантирует разницу?


person Tamás Szelei    schedule 30.05.2011    source источник
comment
-fPIC здесь немного подсказка   -  person alternative    schedule 31.05.2011


Ответы (6)


Статические библиотеки — это настоящие библиотеки в том смысле, что они содержат библиотеку объектных файлов. Каждый объектный файл часто создается из одного исходного файла и содержит машинный код, а также информацию о данных, необходимых для кода. На этапе компоновки компоновщик выберет необходимые объектные файлы и объединит их в исполняемый файл.

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

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

Подвести итог:

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

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

person Martin Liversage    schedule 30.05.2011
comment
Ре <я>. Это как бы объясняет, почему вы не можете запускать статические библиотеки, не так ли...? - person Pacerier; 28.02.2017
comment
@Pacerier - это важное различие между компилятором и компоновщиком. Чтобы скомпилированный код мог вызывать функции в других файлах, ему нужны адреса памяти для перехода (точнее, вызова). Однако при выполнении компилятор не знает этих адресов памяти; вместо этого он использует символы. На самом деле замена символов адресами — это работа компоновщика. См. этот вопрос для получения дополнительной информации. TL;DR: вы можете запускать статические библиотеки, но только после запуска компоновщика. Динамические библиотеки уже сделали это. - person Tyrannosaur; 24.05.2017

Код в объектном файле не связан. Он содержит неявные ссылки на внешние функции, которые еще не разрешены.

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

В конечном счете, нет причин, по которым это должно быть так. Теоретически вы можете написать загрузчик, который будет делать все это каждый раз, когда вы загружаете файл. По сути, это просто оптимизация: компоновщик выполняет относительно медленную часть работы. Ссылки на библиотеки DLL остались, но они разрешены до такой степени, что загрузчик довольно быстро находит и загружает целевой файл (при необходимости) и разрешает указанные функции. Когда компоновщик выполняет свою работу, он делает гораздо больше, просматривая длинные списки определений, чтобы найти те, которые вам нужны, что немного медленнее.

person Jerry Coffin    schedule 30.05.2011

Примечание. Следующий ответ не не зависит от платформы, а специфичен для систем на основе ELF и некоторых других подобных. Кто-то еще может заполнить данные для других систем.

Что такое статическая библиотека?

Статическая библиотека — это набор *.o файлов в архиве. Каждый файл может содержать ссылки на неопределенные символы, которые должны быть разрешены компоновщиком, например, в вашей библиотеке может быть ссылка на printf. Библиотека не предоставляет каких-либо указаний о том, где будет найдена printf, ожидается, что компоновщик найдет ее в одной из других библиотек, на которые его попросили связать.

Предположим, ваша библиотека содержит следующий код:

read_png.o
write_png.o
read_jpg.o
write_jpg.o
resize_image.o
handle_error.o

Если приложение использует только read_png и write_png, то другие фрагменты кода не будут загружены в исполняемый файл (кроме handle_error, который вызывается из read_png и write_png).

Мы не можем загрузить статическую библиотеку во время выполнения, потому что:

  • Компоновщик не знает, где найти внешние объекты, например, printf.

  • Это было бы медленно. Динамические библиотеки оптимизированы для быстрой загрузки.

  • Статические библиотеки не имеют концепции пространств имен. Я не могу определить свой собственный handle_error, потому что это будет противоречить определению библиотеки.

Что такое динамическая библиотека?

Динамическая библиотека в системах ELF представляет собой объект того же типа, что и исполняемый файл. Он также экспортирует больше символов, исполняемый файл должен экспортировать только _start. Динамические библиотеки оптимизированы таким образом, что все это может быть отображено непосредственно в памяти.

Если у вас есть вызов printf в вашей динамической библиотеке, помимо требований к статическим библиотекам существуют некоторые дополнительные требования:

  • Вы должны указать, какая библиотека имеет printf.

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

Мы не хотим использовать динамические библиотеки для статической компоновки, потому что:

  • Мы не можем связать только часть динамической библиотеки. Даже если наш исполняемый файл никогда не вызывает read_jpg, он включается, потому что динамические библиотеки работают по принципу «все или ничего».

  • Дополнительные накладные расходы на вызовы функций расточительны, даже если они невелики.

Сводка

Компиляция выглядит примерно так:

Source  ==compile==>  Object  ==link==>  Executable / Shared Library

Статическая библиотека — это архив, полный объектов, которые еще не были связаны. Осталось много работы.

Общая библиотека — это связанный конечный продукт, готовый к загрузке в память.

Статические библиотеки были изобретены первыми. Если бы оба были изобретены одновременно, возможно, они были бы гораздо более похожими.

person Community    schedule 30.05.2011
comment
Вы действительно это имели в виду : We can't load a static library at runtime because: The linker doesn't know where to find external objects, e.g., printf.... Или там опечатка? Вместо этого вы хотели сказать loader, а не linker? - person Nawaz; 19.11.2011
comment
@Nawaz: Нет, компоновщик правильный. Динамический компоновщик, ld.so в Linux или dyld в OS X, отвечает за разрешение символов во время выполнения. (И нет, я не имел в виду время загрузки.) - person Dietrich Epp; 20.11.2011

Нет фундаментальной причины, по которой вы не могли бы использовать файлы статической библиотеки (.a) в качестве динамических библиотек, загружая и связывая их во время запуска программы или даже динамически во время выполнения. Однако они не были специально скомпилированы и подготовлены для запуска с отображением памяти по произвольному адресу (даже выравнивание не обязательно правильное!), поэтому коду загрузчика пришлось бы выделять память, читать нужные объекты из .a файлов в этот памяти и выполнять в ней основные модификации. И, конечно же, ни одна из этих воспоминаний не может быть передана. Так что, наверное, это очень плохая идея...

person R.. GitHub STOP HELPING ICE    schedule 31.05.2011

Статическая библиотека == tarball, тогда как динамическая == непрерывное изображение частично связано

Статическая библиотека — это просто архив1 из .o or.obj` файлов. Когда исполняемый файл компонуется, ссылочные модули (и те, на которые они ссылаются, а затем те, на которые они ссылаются, а затем ... и т. д.) копируются из статической библиотеки и прикрепляются к концу основной программы. Все это выгружается в память как единый «объект» ОС.

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

У статических библиотек есть плюсы и минусы.

Нет ада DLL (он же ад зависимостей)
Гораздо меньше памяти для небольшого времени выполнения смеси процессов
Гораздо больший объем памяти для больших смесей процессов во время выполнения разрозненных программ
Нельзя совместно использовать память библиотечного кода между процессами, за исключением случаев, когда они запускают одну и ту же программу
Большой набор программ (например, посадочные места для Linux/Windows/Mac) занимают много места, так как printf et al дублируются снова и снова в каждом изображении
Трудно, если вообще возможно, исправить ошибки безопасности, возникающие в библиотеках
Трудно, если не невозможно, обновить библиотеку в одиночку
Трудно, если вообще возможно, обновить библиотеку в одиночку и сломать вашу программу


1. На самом деле они не в формате tar(1), но связаны между собой.

person DigitalRoss    schedule 31.05.2011

Разница в следующем:

  • Статические библиотеки, скомпилированные и связанные
  • Динамические библиотеки подключены

Ссылки

person Community    schedule 09.01.2015