Если у вас есть разделяемая библиотека libfoo.so
, единственными способами, которыми вы можете использовать ее в компоновке чего-либо еще, являются:
Свяжите программу, которая динамически зависит от нее, например.
$ gcc -o prog bar.o ... -lfoo
Или свяжите другую общую библиотеку, которая динамически зависит от нее, например.
$ gcc -shared -o libbar.so bar.o ... -lfoo
В любом случае продукт связи, prog
или libbar.so
, приобретает динамическую зависимость от libfoo.so
. Это означает, что prog|libfoo.so
имеет информацию, вписанную в него компоновщиком, который инструктирует загрузчик ОС во время выполнения найти libfoo.so
, загрузить его в адресное пространство текущего процесса и связать ссылки программы на экспортируемые символы libfoo
с адресами их определений. .
Так что libfoo.so
должен продолжать существовать так же, как и prog|libbar.so
. Невозможно связать libfoo.so
с prog|libbar.so
таким образом, чтобы libfoo.so
физически сливался с prog|libbar.so
и больше не был зависимостью во время выполнения.
Неважно, есть ли у вас исходный код других входных файлов компоновки — bar.o ...
— которые зависят от libfoo.so
. Единственный тип связи, которую вы можете сделать с разделяемой библиотекой, — это динамическая связь.
Это полностью противоречит привязке статической библиотеки.
Вы задаетесь вопросом об утверждении в этом этом ответе, где говорится:
Если у вас есть доступ к исходным или объектным файлам для обеих библиотек, можно легко скомпилировать/связать из них комбинированный SO.
Автор просто замечает, что если у меня есть исходные файлы
foo_a.c foo_b.c... bar_a.c bar_b.c
которые я компилирую в соответствующие объектные файлы:
foo_a.o foo_b.o... bar_a.o bar_b.o...
или, если у меня просто есть эти объектные файлы. Затем, а также - или вместо - связать их в две общие библиотеки:
$ gcc -shared -o libfoo.so foo_a.o foo_b.o...
$ gcc -shared -o libbar.so bar_a.o bar_b.o...
Я мог бы связать их в один:
$ gcc -shared -o libfoobar.so foo_a.o foo_b.o... bar_a.o bar_b.o...
который не будет зависеть от libfoo.so
или libbar.so
, даже если они существуют.
И хотя это может быть простым, оно также может быть ложным. Если есть какой-либо символ name
, который глобально определен в любом из foo_a.o foo_b.o...
, а также глобально определен в любом из bar_a.o bar_b.o...
, то он не будет иметь значения для связи ни libfoo.so
, ни libbar.so
(и его не нужно динамически экспортировать ни одним из них). Но связь libfoobar.so
не сработает для множественного определения name
.
Если мы создадим разделяемую библиотеку libbar.so
, которая зависит от libfoo.so
и сама связана с libfoo.so
:
$ gcc -shared -o libbar.so bar.o ... -lfoo
и затем мы хотим связать программу с libbar.so
, мы можем сделать это таким образом, что нам не нужно упоминать ее зависимость libfoo.so
:
$ gcc -o prog main.o ... -lbar -Wl,-rpath=<path/to/libfoo.so>
См. этот ответ, чтобы узнать об этом. Но это не меняет того факта, что libbar.so
во время выполнения зависит от libfoo.so
.
Если это невозможно, то что технически препятствует этому?
Что технически препятствует связыванию разделяемой библиотеки с какой-либо программой или разделяемой библиотекой targ
таким образом, чтобы физически объединять ее в targ
, так это то, что разделяемая библиотека (например, программа) не относится к тому типу вещей, которые компоновщик знает, как физически объединить в свой вывод. файл.
Входные файлы, которые компоновщик может физически объединить в targ
, должны иметь структурные свойства, которые помогут компоновщику выполнить это слияние. Такова структура объектных файлов. Они состоят из именованных входных разделов объектного кода или данных, помеченных различными атрибутами. Грубо говоря, компоновщик делит объектные файлы на разделы и распределяет их по выходным разделам выходного файла в соответствии с их атрибутами, а также вносит двоичные изменения в объединенный результат, чтобы разрешать ссылки на статические символы или включать загрузчик ОС для разрешения динамических во время выполнения.
Это необратимый процесс. Компоновщик не может использовать программу или разделяемую библиотеку и реконструировать объектные файлы, из которых она была создана, чтобы снова объединить их во что-то другое.
Но это действительно не по делу. Когда входные файлы физически объединяются в targ
, это называется статической компоновкой. Когда входные файлы просто внешне ссылаются в targ
, чтобы заставить загрузчик ОС сопоставить их с процессом, который он запустил для targ
, это называется динамической компоновкой. Техническое развитие дало нам решение файлового формата для каждой из этих потребностей: объектные файлы для статической компоновки, разделяемые библиотеки для динамической компоновки. Ни один из них не может быть использован для целей другого.
person
Mike Kinghan
schedule
15.05.2020