Ошибка ссылки g ++ - почему обходной путь работает - PullRequest
2 голосов
/ 07 ноября 2011


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

Вот структура файла, связанная с моей проблемой:

project
  |-package_a
    |--a.cpp
    |--...
  |-package_b
    |--b.cpp
    |--c.cpp
    |--...
  |-package_others


Все * .o в package_a будут упакованы в a.a и * .o в package_b будет упакован в б.а

"g++ -o exec -Bstatic b.a a.a ..." используется для генерации двоичного файла.

В package_b / b.cpp я добавил функцию foo ().
И в package_a / a.cpp я использовал эту функцию.

Но здесь я получу ошибку ссылки, говорящую о неопределенной ссылке на foo () в a.o
Я могу проверить (по objdump), что foo () уже в b.o.

Изменив команду link на "g++ -o exec -Bstatic a.a b.a ...", двоичный файл может быть успешно собран. Теперь я понимаю, что компоновщик заботится о порядке в списке ссылок. Но, пожалуйста, поймите, что это большой проект. У меня нет разрешения на изменение конфигурации проекта, поэтому необходимо сохранить исходный порядок ссылок.

Затем я добавил фиктивную функцию bar () в package_b / c.cpp, которая ничего не делает но просто вызов foo (), тогда будет запущен оригинальный "g++ -o exec -Bstatic b.a a.a ..." без ошибок связи.

Может кто-нибудь показать мне немного света, почему просто добавление фиктивной функции в тот же пакет будет работать в этом случае?

Я использую g ++ 4.4.4 и linux 2.6.18-194.el5

Любой комментарий будет оценен

Ответы [ 4 ]

4 голосов
/ 07 ноября 2011

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

Вы можете изменить это поведение либо

  • , включая один из архивов дважды, как в

    g++ -o exec a.a b.a a.a
    
  • с использованием конструкции -(

    g++ -o exec -( a.a b.a -)
    

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

Не повезло ... Может быть, менеджер или кто-то еще просто не хочет, чтобы вы использовали функции в b из a.

Затем я добавил фиктивную функцию bar () в package_b / c.cpp, которая ничего не делает, кроме простого вызова foo (), после чего оригинальный "g ++ -o exec -Bstatic ba aa ..." будет работать без какой-либо ссылки ошибка.

Может быть, уже упоминалась другая функция package_b/c.cpp, и компоновщик взял bar() с ней (потому что они находятся в одном файле), и эта ссылка foo(), которая впоследствии была включена в вывод, тоже , Это удалось, потому что foo было в b.a тоже.

3 голосов
/ 07 ноября 2011

Вы можете прочитать , как работают линкеры .Кстати, флаг -Bstatic не нужен, поскольку архивы объектных файлов .a. связаны только статически (как если бы в командной строке вместо .a был указан список объектных файлов, содержащихся в .a).

Кроме того, вы всегда можете обернуть список архивов, чтобы связать их с опциями --start-group / --end-group, чтобы компоновщик сканировал список архивов несколько раз, так что упорядочение архивов не требуется (как в MS VC ++):

g++ -o exec -Wl,--start-group a.a b.a -Wl,--end-group

См. man ld:

   -( archives -)
   --start-group archives --end-group
       The archives should be a list of archive files.  They may be either
       explicit file names, or -l options.

       The specified archives are searched repeatedly until no new
       undefined references are created.  Normally, an archive is searched
       only once in the order that it is specified on the command line.
       If a symbol in that archive is needed to resolve an undefined
       symbol referred to by an object in an archive that appears later on
       the command line, the linker would not be able to resolve that
       reference.  By grouping the archives, they all be searched
       repeatedly until all possible references are resolved.

       Using this option has a significant performance cost.  It is best
       to use it only when there are unavoidable circular references
       between two or more archives.
2 голосов
/ 07 ноября 2011

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

Здесь есть подробное объяснение здесь .

0 голосов
/ 07 ноября 2011

Когда вы используете функцию из статической библиотеки, вы должны сначала в командной строке поместить файл, из которого используется функция, затем библиотеку, в которой определена функция.В противном случае, если вы поместите определение первым, gcc (или, более конкретно, ld) отбрасывает «неиспользуемую» функцию.Вот как работает gcc, извините.

...