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

Я пытаюсь понять больше о связывании и общей библиотеке.

В конечном счете, мне интересно, можно ли добавить метод в общую библиотеку. Например, предположим, что у вас есть исходный файл a.c и библиотека lib.so (без исходного файла). Далее предположим для простоты, что a.c объявляет единственный метод, имя которого отсутствует в lib.so. Я подумал, может быть, было бы возможно во время компоновки связать ao с lib.so, дав указание создать newLib.so и заставить компоновщика экспортировать все методы/переменные в lib.so, чтобы newLib.so теперь в основном lib.so с добавленным методом из a.so.

В более общем случае, если у вас есть исходный файл, зависящий от общей библиотеки, можно ли создать один выходной файл (библиотеку или исполняемый файл), который больше не зависит от общей библиотеки? (То есть все соответствующие методы/переменные из библиотеки были бы экспортированы/связаны/встроены в новый исполняемый файл, что сделало бы зависимость недействительной). Если это невозможно, то что технически препятствует этому?

Здесь был задан похожий вопрос: Объединить несколько общих библиотек .so. Один из ответов включает следующий текст: «Если у вас есть доступ к исходным или объектным файлам для обеих библиотек, можно просто скомпилировать/связать из них комбинированный SO.: без объяснения технических деталей. Была ли это ошибка или нет? это держать?Если да, то как это сделать?


person TuTor    schedule 15.05.2020    source источник


Ответы (1)


Если у вас есть разделяемая библиотека 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