Линкер 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)

Смотрите этот вопрос для получения дополнительной информации о порядке ссылки.

Другие вопросы по тегам