g ++: В каком порядке должны быть связаны статические и динамические библиотеки? - PullRequest
24 голосов
/ 29 января 2009

Допустим, у нас есть основной исполняемый файл с именем "my_app", и он использует несколько других библиотек: 3 библиотеки связаны статически, а 3 другие связаны динамически. В каком порядке они должны быть связаны с "my_app"?

Но в каком порядке они должны быть связаны?

Допустим, мы получили libSA (как в Static A), который зависит от libSB, и libSC, который зависит от libSB:

libSA -> libSB -> libSC

и три динамические библиотеки: libDA -> libDB -> libDC (libDA является основным, libDC является самым высоким)

в каком порядке они должны быть связаны? основной первый или последний?

g++ ... -g libSA libSB libSC -lDA -lDB -lDC -o my_app

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

Ответы [ 5 ]

26 голосов
/ 29 января 2009

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

Ситуация с динамическими библиотеками более запутанная, есть два аспекта:

  1. Общая библиотека работает точно так же, как статическая библиотека (за исключением общих сегментов, если они присутствуют), что означает, что вы можете просто сделать то же самое - просто связать свою общую библиотеку, как только вы получите объектные файлы. Это означает, что, например, символы из libDA будут отображаться как неопределенные в libDB

  2. Вы можете указать библиотеки для ссылки в командной строке при связывании общих объектов. Это имеет тот же эффект, что и 1., но помечает libDB как нужную libDA.

Разница в том, что если вы используете первый способ, вы должны указать все три библиотеки (-lDA, -lDB, -lDC) в командной строке при связывании исполняемого файла. Если вы используете последний, вы просто указываете -lDC, и он автоматически вытягивает другие во время соединения. Обратите внимание, что время ссылки - непосредственно перед запуском вашей программы (что означает, что вы можете получить разные версии символов, даже из разных библиотек).

Это все относится к UNIX; Windows DLL работает совсем по-другому.

Изменить после уточнения вопроса:

Цитата из ld справочного руководства.

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

Смотрите опцию `- (', чтобы узнать способ форсирования компоновщик для поиска в архивах несколько раз.

Вы можете перечислить один и тот же архив несколько раз в командной строке.

Этот тип поиска в архиве стандарт для линкеров Unix. Однако если вы используете `ld 'в AIX, обратите внимание, что это отличается от поведения компоновщик AIX.

Это значит:

Любая статическая библиотека или объект, зависящий от другой библиотеки, должны быть помещены перед ним в командной строке. Если статические библиотеки зависят друг от друга по кругу, вы можете, например,. используйте параметр командной строки -( или поместите библиотеки в командную строку дважды (-lDA -lDB -lDA). Порядок динамических библиотек не имеет значения.

24 голосов
/ 29 января 2009

Этот вопрос лучше всего решить на тривиальном примере. В самом деле! Потратьте 2 минуты, напишите простой пример и попробуйте! Вы чему-то научитесь, и это быстрее, чем спрашивать.

Например, заданные файлы:

a1.cc

#include <stdio.h>
void a1() { printf("a1\n"); }

a2.cc

#include <stdio.h>
extern void a1();
void a2() { printf("a2\n");  a1(); }

a3.cc

#include <stdio.h>
extern void a2();
void a3() { printf("a3\n"); a2(); }

aa.cc

extern void a3();
int main()
{
  a3();
}

Продолжительность:

g++ -Wall -g -c a1.cc
g++ -Wall -g -c a2.cc
g++ -Wall -g -c a3.cc
ar -r liba1.a a1.o
ar -r liba2.a a2.o
ar -r liba3.a a3.o
g++ -Wall -g aa.cc -o aa -la1 -la2 -la3 -L.

Показывает:

./liba3.a(a3.o)(.text+0x14): In function `a3()':
/tmp/z/a3.C:4: undefined reference to `a2()'

Принимая во внимание:

g++ -Wall -g -c a1.C
g++ -Wall -g -c a2.C
g++ -Wall -g -c a3.C
ar -r liba1.a a1.o
ar -r liba2.a a2.o
ar -r liba3.a a3.o
g++ -Wall -g aa.C -o aa -la3 -la2 -la1 -L.

преуспевает. (Изменяется только порядок параметров -la3 -la2 -la1.)

PS:

nm --demangle liba*.a

liba1.a:
a1.o:
                 U __gxx_personality_v0
                 U printf
0000000000000000 T a1()

liba2.a:
a2.o:
                 U __gxx_personality_v0
                 U printf
                 U a1()
0000000000000000 T a2()

liba3.a:
a3.o:
                 U __gxx_personality_v0
                 U printf
                 U a2()
0000000000000000 T a3()

От человек нм :

  • Если строчные буквы, символ является локальным; в верхнем регистре символ является глобальным (внешним).

  • "T" Символ находится в разделе текста (кода).

  • "U" Символ не определен.

2 голосов
/ 30 января 2009

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

g++ ... -la1 -la2 -la3 -la1 -la2 -la3 ...
0 голосов
/ 29 января 2009

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

0 голосов
/ 29 января 2009

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

Начните с самого простого, которое не имеет (или только вне вашего проекта) зависимостей.

...