GCC по-разному относится к объекту и статической библиотеке относительно неопределенных символов - PullRequest
0 голосов
/ 21 ноября 2018

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

Исходный код:

$ cat main.c
int main() { return 0; } 
$ cat src.c
int outerUnusedFunc() {
    return innerUndefinedFunc();
}
int innerUndefinedFunc();

Создание * .o и * .a из него, сравнение с использованием "nm":

$ gcc -c -o main.o main.c
$ gcc -c -o src.o src.c
$ ar r src.a src.o
ar: creating src.a
$ nm src.o
                 U innerUndefinedFunc
0000000000000000 T outerUnusedFunc
$ nm src.a

src.o:
                 U innerUndefinedFunc
0000000000000000 T outerUnusedFunc

(Здесь мы ясно видим, что и * .o, и * .a содержат список одинаковых символов)

А теперь ...

$ ld -o exe main.o src.o
src.o: In function `outerUnusedFunc':
src.c:(.text+0xa): undefined reference to `innerUndefinedFunc'
$ echo $?
1
$ ld -o exe main.o src.a
$ echo $?
0

В чем причина того, что GCC относится к этому по-разному?

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Если вы прочитаете тег static-libraries вики , это объяснит, почему никакие объектные файлы из src.a не связаны с вашей программой, и, следовательно, почему не имеет значения, на какие неопределенные символы ссылаются в них.

Разница между объектным файлом foo.o и статической библиотекой libfoo.a в качестве входных данных компоновщика заключается в том, что объектный файл всегда связан с вашей программой, безусловно, тогда кактот же объектный файл в библиотеке статических библиотек, libfoo.a(foo.o), извлекается из libfoo.a и связывается с программой только в том случае, если компоновщик нуждается в для продолжения связывания, как объяснено тегом wiki.1015 *

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

Наблюдаемое вами поведение - это поведение компоновщик , вызываете ли вы его через интерфейс GCC.

Предоставление компоновщика foo.o говорит ему: Я хочу это в программе .Предоставление компоновщика libfoo.a говорит ему: Вот некоторые объектные файлы, которые могут вам понадобиться или не понадобиться .

0 голосов
/ 21 ноября 2018

Во втором случае - со статической библиотекой - в командной строке написано "собрать exe из main.o и добавить все необходимые вещи из src.a".ld просто игнорирует библиотеку, потому что для main.o не требуется никаких внешних символов (на outerUnusedFunc нет ссылки из main.o).

Но в первом случае командная строка говорит "build exe из main.o и src.o".ld должен поместить src.o содержимое в выходной файл.Следовательно, он обязывает анализировать модуль src.o, добавлять outerUnusedFunc в выходной файл и разрешать все символы для outerUnusedFunc, несмотря на то, что он не используется.

Вы можете включить сборку мусора для секций кода

gcc --function-sections -Wl,--gc-sections -o exe main.c src.c

В этом случае outerUnusedFunc (как и все другие функции) будет размещен в отдельном разделе.ld увидит, что этот раздел не используется (символы не указаны).Он удалит весь раздел из выходного файла, так что innerUndefinedFunc не будет ссылаться и символ не будет разрешен - тот же результат, что и в случае библиотеки.

С другой стороны, вы можете вручную ссылаться на outerUnusedFunc как "undefined", так что ld должен найти его в библиотеке и добавить в выходной файл.

ld -o exe main.o -u outerUnusedFunc src.a

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

...