Линкер GCC игнорирует символы в библиотеке.a, которые, как я подтвердил, присутствуют
У меня действительно сложная проблема в GCC.
Я получаю следующую ошибку:
gcc -Wall -Werror -L/Users/red_angel/chorebox_sys/lib -o products/chbc2c -lchorebox ofiles/main.o
Undefined symbols for architecture x86_64:
"_chbclib_flushout", referenced from:
_main in main.o
"_chorebox_argc", referenced from:
_chorebox_command_line in libchorebox.a(chorebox_command_line.o)
"_chorebox_argv", referenced from:
_chorebox_command_line in libchorebox.a(chorebox_command_line.o)
"_chorebox_env", referenced from:
_chorebox_command_line in libchorebox.a(chorebox_command_line.o)
"_mn_command_options", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [products/chbc2c] Error 1
Что не так с этой ошибкой? Я подтвердил, что _chorebox_argc
Символ действительно присутствует в "libchorebox.a".
Я подтвердил это, выполнив команду:
nm /Users/red_angel/chorebox_sys/lib/libchorebox.a | cat -n | chodo -_chorebox_argc flip
Поскольку команда "chodo" - это команда, которую я написал, вы, возможно, не знакомы - я объясню, что она делает. Он читает из стандартного ввода и пересылает в стандартный вывод каждую строку, соответствующую шаблону поиска. В этом случае (короче говоря) он выводит каждую строку, содержащую "_chorebox_argc
Строка
Я получаю следующий вывод:
3 0000000000000004 C _chorebox_argc
55 U _chorebox_argc
Чтобы более подробно рассмотреть соответствующую часть файла, я набираю ту же команду, только на этот раз опуская команду "chodo" в конце переданной серии команд --- и тем самым скопирую / вставлю вам соответствующую часть этого файла:
1
2 /Users/red_angel/chorebox_sys/lib/libchorebox.a(vars.o):
3 0000000000000004 C _chorebox_argc
4 0000000000000008 C _chorebox_argv
5 0000000000000008 C _chorebox_env
6
7 /Users/red_angel/chorebox_sys/lib/libchorebox.a(chorebox_mlc.o):
8 00000000000000c8 s EH_frame0
9 0000000000000075 s L_.str
10 U ___stderrp
11 U _chorebox_argv
12 0000000000000000 T _chorebox_mlc
13 00000000000000e0 S _chorebox_mlc.eh
14 U _exit
15 U _fflush
16 U _fprintf
17 U _malloc
18
19 /Users/red_angel/chorebox_sys/lib/libchorebox.a(chorebox_apend_string.o):
20 0000000000000078 s EH_frame0
21 0000000000000000 T _chorebox_apend_string
22 0000000000000090 S _chorebox_apend_string.eh
23 U _chorebox_join_string
24 U _free
25
Само собой разумеется, что символ определенно присутствует в файле "libchorebox.a" ----- так почему компоновщик GCC жалуется, что он не найден?
2 ответа
После некоторого обсуждения в чате мы обнаружили, что проблема заключается в "общих" определениях. Ниже приведена упрощенная версия кода, вызывающая проблемы. Система Mac OS X (Маверикс и Йосемити).
Только с общими определениями
vars.h
extern int chorebox_argc;
extern char **chorebox_argv;
extern char **chorebox_envp;
vars.c
#include "vars.h"
int chorebox_argc;
char **chorebox_argv;
char **chorebox_envp;
main.c
#include "vars.h"
int main(int argc, char **argv, char **envp)
{
chorebox_argc = argc;
chorebox_argv = argv;
chorebox_envp = envp;
return argc;
}
Компиляция 1
$ gcc -c vars.c
$ nm vars.o
0000000000000004 C _chorebox_argc
0000000000000008 C _chorebox_argv
0000000000000008 C _chorebox_envp
$ ar rv libvars.a vars.o
ar: creating archive libvars.a
a - vars.o
$ ranlib libvars.a
warning: ranlib: warning for library: libvars.a the table of contents is
empty (no object file members in the library define global symbols)
$ gcc -c main.c
$ gcc -o program main.o vars.o
$ gcc -o program main.o -L. -lvars
Undefined symbols for architecture x86_64:
"_chorebox_argc", referenced from:
_main in main.o
"_chorebox_argv", referenced from:
_main in main.o
"_chorebox_envp", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status
$
Обратите внимание на C в выводе из nm
, Это указывает на "общее" определение переменных. Само по себе недостаточно превращать их в определения переменных - посмотрите на сообщение ranlib
,
Я не уверен, является ли это новым поведением в Mac OS X или нет. Однако кажется, что наличие исходного файла, который определяет только неинициализированные переменные, недостаточно, когда переменные определены в библиотеке, хотя этого достаточно, когда объектный файл связан напрямую.
С определениями переменных
vardefs.c
#include "vars.h"
int chorebox_argc = 0;
char **chorebox_argv = 0;
char **chorebox_envp = 0;
Это имеет явно инициализированные версии переменных. Значения инициализатора такие же, как значения по умолчанию, но явная инициализация имеет все значение.
Компиляция 2
$ rm libvars.a
$ gcc -c vardefs.c
$ ar rv libvars.a vardefs.o
ar: creating archive libvars.a
a - vardefs.o
$ gcc -o program main.o -L. -lvars
$
Явно инициализированные переменные извлекаются из библиотеки без проблем.
С одним определением переменной
vars.h
extern int chorebox_argc;
extern char **chorebox_argv;
extern char **chorebox_envp;
extern int make_believe;
vars.c
#include "vars.h"
int chorebox_argc;
char **chorebox_argv;
char **chorebox_envp;
int make_believe = 59;
main.c
#include "vars.h"
int main(int argc, char **argv, char **envp)
{
chorebox_argc = argc;
chorebox_argv = argv;
chorebox_envp = envp;
make_believe = 1;
return argc;
}
Компиляция 3
$ gcc -c vars.c
$ ar rv libvars.a vars.o
ar: creating archive libvars.a
a - vars.o
$ nm vars.o
0000000000000004 C _chorebox_argc
0000000000000008 C _chorebox_argv
0000000000000008 C _chorebox_envp
0000000000000000 D _make_believe
$ gcc -c main.c
$ gcc -o program main.o -L. -lvars
$
Обратите внимание, что добавление инициализированных make_believe
Достаточно извлечь объектный файл из библиотеки, и тогда общих определений для других переменных будет достаточно для удовлетворения компоновщика.
занятия
Хотя порядок связывания был частью проблемы, это была не вся проблема.
Предоставление неинициализированных глобальных переменных в библиотеке не всегда работает, особенно если в том же исходном файле нет других определений.
Как я отмечал в чате, обычно не очень удобно предоставлять прямой доступ к глобальным переменным. Было бы лучше предоставить функциональные интерфейсы для доступа (получения и установки) переменных.
Положить -l
вариант после файла, который нуждается в этом (ofiles/main.o
)
Смотрите этот вопрос для получения дополнительной информации о порядке ссылки.