Разрешить циклические зависимости, связав одну и ту же библиотеку дважды? - PullRequest
32 голосов
/ 21 февраля 2012

У нас есть кодовая база, разбитая на статические библиотеки. К сожалению, библиотеки имеют циклические зависимости; например, libfoo.a зависит от libbar.a и наоборот.

Я знаю, что «правильный» способ справиться с этим - использовать опции компоновщика --start-group и --end-group, например:

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

Но в наших существующих файлах Makefile проблема обычно решается так:

g++ -o myApp -lfoo -lbar -lfoo

(Представьте, что это расширено до ~ 20 библиотек со сложной взаимозависимостью.)

Я проходил через наши Make-файлы, меняя вторую форму на первую, но теперь мои коллеги спрашивают меня, почему ... И помимо «потому что это чище» и смутное ощущение, что другая форма опасна, У меня нет хорошего ответа.

Итак, может ли связывание одной и той же библиотеки несколько раз когда-либо создать проблему? Например, может ли ссылка потерпеть неудачу с многократно определенными символами, если один и тот же .o будет введен дважды? Или есть какой-то риск, что мы могли бы получить две копии одного и того же статического объекта, создав тонкие ошибки?

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

Ответы [ 3 ]

6 голосов
/ 21 февраля 2012

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

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

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

4 голосов
/ 12 сентября 2018

проблема с

g++ -o myApp -lfoo -lbar -lfoo

заключается в том, что нет гарантии, что двух проходов через libfoo и одного прохода через libbar достаточно.

Подход с Wl,--start-group ... -Wl,--end-group лучше, потому что более устойчив.

Рассмотрим следующий сценарий (все символы находятся в разных объектных файлах):

  • myApp требуется символ fooA, определенный в libfoo.
  • Символ fooA нужен символ barB, определенный в libbar.
  • Символ barB нуждается в символе fooC, определенном в libfoo. Это круговая зависимость, которая может быть обработана с помощью -lfoo -lbar -lfoo.
  • Символу fooC нужен символ barD, определенный в libbar.

Чтобы выполнить сборку в приведенном выше случае, нам нужно передать -lfoo -lbar -lfoo -lbar компоновщику. Почему?

  1. Компоновщик видит libfoo впервые и использует определения символа fooA, но не fooC, потому что пока он не видит необходимости включать также fooC в двоичный файл. Однако компоновщик начинает искать определение barB, потому что его определение необходимо для работы fooA.
  2. Компоновщик видит -libbar, включает определение barB (но не barD) и начинает искать определение fooC.
  3. Определение fooC встречается в libfoo, когда оно обрабатывается во второй раз. Теперь становится очевидным, что также необходимо определение barD - но уже слишком поздно в командной строке libbar!

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

Таким образом, используя

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

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

Тем не менее, существует небольшое ухудшение производительности: в первом примере -lbar были отсканированы еще раз по сравнению с ручной командной строкой -lfoo -lbar -lfoo. Не уверен, стоит ли упоминать / обдумывать.

1 голос
/ 22 февраля 2012

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

Даже если по-прежнему сохраняются структурные причины для унаследованной структуры библиотеки, почти наверняка было бы приемлемо создать еще одну библиотеку из унаследованного устройства.Просто поместите все модули из 20 библиотек в новую библиотеку liballofthem.a.Тогда каждое отдельное приложение просто g++ -o myApp -lallofthem ...

...