Что такое *.so.*.* libs

Когда я делаю ls -l в /usr/lib Я вижу много людей с "sameName.so.*.*" расширение.

  1. Каково значение этих расширений?
  2. Почему создаются мягкие ссылки? в чем их польза?

Один пример очень поможет в понимании.

2 ответа

Решение

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

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

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

Управление версиями общих объектов - один из способов избежать этого. Другим было бы вообще не делиться объектами, но у этого также есть свои плюсы и минусы, которые я не буду здесь рассматривать.

В качестве примера, скажем, у вас есть версия 1 xyz.so, У вас есть файл и символическая ссылка на этот файл:

pax> ls -al xyz*
-rw-r--r--  1 pax paxgroup    12345 Nov 18  2009 xyz.so.1
lrwxrwxrwx  1 pax paxgroup        0 Nov 18  2009 xyz.so -> xyz.so.1

Теперь, когда вы создаете исполняемый файл exe1, связывая это с xyz.so, он будет следовать по символической ссылке, чтобы хранить xyz.so.1 в исполняемый файл, как вещь, которую он должен загрузить во время выполнения.

Таким образом, когда вы обновляете разделяемую библиотеку таким образом:

pax> ls -al xyz*
-rw-r--r--  1 pax paxgroup    12345 Nov 18  2009 xyz.so.1
-rw-r--r--  1 pax paxgroup    67890 Nov 18  2009 xyz.so.2
lrwxrwxrwx  1 pax paxgroup        0 Nov 18  2009 xyz.so -> xyz.so.2

ваш оригинальный исполняемый файл exe1 будет по- прежнему загружать версию 1 общего объекта.

Тем не менее, любые исполняемые файлы, которые вы создаете сейчас (например, exe2) будет связан с версией 2 общего объекта.


Фактические детали реализации могут несколько отличаться (я основываю свой ответ на более ранних версиях UNIX, и Linux, кажется, делает управление версиями немного более разумно, чем просто следуя символическим ссылкам). У IBM developerWorks есть хорошая статья о том, как это делается здесь.

Когда вы создаете общий объект, вы даете ему реальное имя и soname, Они используются для установки общего объекта (который создает как объект, так и ссылку на него).

Таким образом, вы можете в конечном итоге с ситуацией:

pax> ls -al xyz*
-rw-r--r--  1 pax paxgroup    12345 Nov 18  2009 xyz.so.1.5
lrwxrwxrwx  1 pax paxgroup        0 Nov 18  2009 xyz.so.1 -> xyz.so.1.5
lrwxrwxrwx  1 pax paxgroup        0 Nov 18  2009 xyz.so -> xyz.so.1

с xyz.so.1.5 имея SONAME из xyz.so.1,

Когда компоновщик ссылки в xyz.so, следует по ссылкам вплоть до xyz.so.1.5 и использует его SONAME из xyz.so.1 хранить в исполняемом файле. Затем, когда вы запускаете исполняемый файл, он пытается загрузить xyz.so.1 который будет указывать на конкретный xyz.so.1.N (не обязательно версия 1.5).

Чтобы вы могли установить xyz.so.1.6 и обновить xyz.so.1 вместо этого указывать ссылку на него, и уже связанные исполняемые файлы будут использовать его вместо этого.

Одним из преимуществ этого многоуровневого метода является то, что вы можете иметь несколько потенциально несовместимых библиотек с одним и тем же именем (xyz.so.1.*, xyz.so.2.*) но в каждой основной версии вы можете свободно обновлять их, поскольку они должны быть совместимыми.

Когда вы связываете новые исполняемые файлы:

  • Те, кто связывается с xyz.so получит последнюю минорную версию последней мажорной версии.
  • Другие ссылки с xyz.so.1 получит последнюю минорную версию конкретной мажорной версии.
  • Остальные связываются с xyz.so.1.2 получит конкретную минорную версию конкретной мажорной версии.

Это схема управления версиями для разделяемых библиотек. Каждая библиотека должна иметь 3 имени:

  1. Настоящее имя: фактическое имя библиотеки, libfoo.so.1.2.3
  2. "SONAME": имя, записанное в исполняемом файле, и динамический компоновщик имени, libfoo.so.1.2, Это имя фактически записано внутри самого двоичного файла библиотеки и будет записано в исполняемый файл во время компоновки. Обычно это символическая ссылка на настоящее имя библиотеки (обычно последняя версия).
  3. Имя компоновщика: имя, которое вы даете компоновщику при сборке вашей программы. Обычно ссылки на последние SONAME.

пример

Скажи у тебя libfoo версия 1 установлена: libfoo.so -> libfoo.so.1.0 -> libfoo.so.1.0.0, Вы строите свою программу bar с -lfoo, теперь ссылки на libfoo и загрузит libfoo.so.1.0 во время выполнения из-за SONAME. Затем вы обновляетесь до исправленной, но бинарно-совместимой libfoo.so.1.0.1 заменив настоящий двоичный файл. bar еще ссылки на libfoo.so.1.0 и не нуждается в восстановлении.

Теперь представьте, что вы хотите построить новую программу baz это использует несовместимые изменения в libfoo v1.1. Вы устанавливаете новую версию, и ваша система теперь имеет две версии, установленные параллельно:

  1. libfoo.so.1.0 -> libfoo.so.1.0.1
  2. libfoo.so -> libfoo.so.1.1 -> libfoo.so.1.1.0

Примечание. Имя компоновщика обновлено до последней версии (это версия, соответствующая заголовкам, установленным вами в /usr/include).

Вы строите bazи это ссылки на libfoo.so и нагрузки libfoo.so.1.1 во время выполнения. Не то bar по-прежнему работает против libfoo.so.1.0 и не нуждается в обновлении.

Другие вопросы по тегам