Компоновщик 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 жалуется, что он не найден?


person Sophia_ES    schedule 11.02.2015    source источник
comment
ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation): можете ли вы заставить $LD указывать на GNU ld, чтобы у нас не было проблем с совместимостью с clang?   -  person Marcus Müller    schedule 12.02.2015
comment
Ну ---- библиотека была собрана на том же компьютере с той же версией GCC --- так что наличие несовместимости само по себе вызывает недоумение. Кроме того, я не уверен, что понимаю, что вы говорите.   -  person Sophia_ES    schedule 12.02.2015
comment
@MarcusMüller ---- как заставить $LD указывать на GNU ld? Я так понимаю, что /usr/bin/ld не GNU ld? И если да --- как мне узнать, где находится GNU ld?   -  person Sophia_ES    schedule 12.02.2015
comment
Хорошо @MarcusMüller --- я попробовал ваше предложение (насколько я мог понять) -- и это все равно не помогло: вот pastebin: pastebin.com/2msWRvEp   -  person Sophia_ES    schedule 12.02.2015
comment
возможный дубликат порядка компоновщика - GCC   -  person Jonathan Leffler    schedule 12.02.2015
comment
@JonathanLeffler ---- были испробованы все варианты порядка ссылок - безрезультатно. Также при сборке библиотеки я менял порядок файлов компонентов --- тоже безрезультатно. Если ваша гипотеза верна, то один из этих подходов мог решить проблему, но ни один из них не помог.   -  person Sophia_ES    schedule 12.02.2015
comment
@JonathanLeffler --- кстати ---- Я уже спрашивал (когда Маркус впервые указал, в чем проблема), почему GCC генерирует вещи, которые несовместимы сами с собой. Если кто-то не может предложить объяснение, почему GCC ведет себя таким непоследовательным образом в той же учетной записи на той же машине с той же версией GCC - и что можно сделать с этим это - это не ответ.   -  person Sophia_ES    schedule 12.02.2015
comment
Я могу сказать GCC создать код, который несовместим с другим кодом, созданным той же копией GCC. Все, что для этого требуется, это (аб)использование -m32 и -m64 в разных частях процесса компиляции. Вы на Mac?   -  person Jonathan Leffler    schedule 12.02.2015
comment
@JonathanLeffler --- я мог бы добавить --- как в проекте, который создает библиотеку, и в проекте, который пытается использовать ее (поскольку я написал весь код в оба проекта) -- в никаких точках нет каких-либо параметров, переданных в GCC, которые должны каким-либо образом изменить процессор или размер адреса, который будет использовать компилятор. В обоих местах он должен использовать систему по умолчанию (и это в одной и той же учетной записи, на той же машине, с одной и той же версией GCC) --- поэтому несоответствие, которое вы предлагаете, просто не имеет смысла.   -  person Sophia_ES    schedule 12.02.2015
comment
Однажды вы ответите на мой вопрос: вы установили, являются ли объектные файлы в libchorebox.a 32-битными или 64-битными файлами? Если они 64-битные, я ошибаюсь. Если они 32-битные, то я на правильном пути, независимо от того, что вы или я думаем, что должно происходить. Мы должны работать над доказательствами, а не верой. Я согласен, что это «не должно происходить»; однако вопрос в том, «происходит ли это» — и пока мы не узнаем на него ответ, вы бессильны.   -  person Jonathan Leffler    schedule 12.02.2015
comment
Давайте продолжим обсуждение в чате.   -  person Sophia_ES    schedule 12.02.2015


Ответы (2)


После некоторого обсуждения в чате мы обнаружили, что проблема заключается в «общих» определениях. Далее следует упрощенная версия кода, вызывающего проблемы. Система Mac OS X (Mavericks и Yosemite).

Только с общими определениями

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 достаточно, чтобы извлечь объектный файл из библиотеки, а общих определений для других переменных будет достаточно, чтобы удовлетворить компоновщика.

Уроки

Хотя порядок связывания был частью проблемы, это не была вся проблема.

Предоставление неинициализированных глобальных переменных в библиотеке не всегда работает, особенно если в том же исходном файле нет других определений.

Как я уже отмечал в чате, обычно не рекомендуется предоставлять прямой доступ к глобальным переменным. Было бы лучше предоставить функциональные интерфейсы для доступа (получения и установки) переменных.

person Jonathan Leffler    schedule 12.02.2015
comment
Большое спасибо. Это также может быть решением проблемы с плоской ошибкой пространства имен, если кто-то использует библиотеку python cffi. Ошибки там не слишком информативны, и я слишком долго осознал, что никогда не делал архив. - person balbok; 29.02.2020

Поместите параметр -l после файла, которому он нужен (ofiles/main.o)

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

person Community    schedule 11.02.2015
comment
Так было изначально ----- и я получал ту же ошибку. Чтобы продемонстрировать это, я верну его к исходной версии (где опция -l стояла после файла, который в нем нуждался) и покажу вам его в pastebin. Проблема, с которой я сталкиваюсь, заключается в том, что одному объекту в этом архиве .a требуется другой объект внутри архива .a. Но в любом случае -- вот pastebin: pastebin.com/uNE4QTZx - person Sophia_ES; 12.02.2015
comment
@Sophia_ES: ключом, вероятно, является информация Undefined symbols for architecture x86_64. Библиотека, вероятно, является 32-разрядной, а не 64-разрядной библиотекой (или, точнее, объектные файлы в библиотеке, вероятно, являются 32-разрядными объектными файлами, а не 64-разрядными объектными файлами). Вы можете протестировать с помощью: mkdir junk; cd junk; ar x /Users/red_angel/chorebox_sys/lib/libchorebox.a chorebox_apend_string.o; file chorebox_apend_string.o ../ofiles/main.o; cd ..; rm -fr junk, отметив, что два объектных файла имеют разные типы. Обоснованное предположение, но технически все еще предположение. - person Jonathan Leffler; 12.02.2015
comment
@JonathanLeffler --- почему GCC создает библиотеки, которые несовместимы сами с собой на одном компьютере? Как вы это объясните? Как я неоднократно объяснял --- каждый шаг компиляции и сборки выполняется на одной и той же машине с одной и той же версией GCC. Если ваше объяснение является ответом ---- почему GCC ведет себя таким сумасшедшим образом? - person Sophia_ES; 12.02.2015
comment
@Sophia_ES: можно сказать скомпилировать 32-битную или 64-битную версию; вероятно, по умолчанию он компилирует 64-разрядную версию (по крайней мере, ваша сборка ищет 64-разрядную версию), но вполне вероятно, что библиотека chorebox была создана для 32-разрядной версии. Поскольку я понятия не имею, откуда вы взяли библиотеку, или как вы ее построили, или что еще есть рядом с вашей библиотекой (например, есть ли каталог lib64 рядом с каталогом lib?), трудно диагностировать, что вам нужно делать. Полагаю ли я, что тест, который я описал, показал 32-битный код против 64-битного (x86 против x86_64 или что-то подобное)? - person Jonathan Leffler; 12.02.2015
comment
Это кажется маловероятным, но, возможно, вам нужно запустить эту библиотеку - person ; 12.02.2015
comment
@JonathanLeffler Привет. Как вы думаете, ошибка здесь stackoverflow.com/questions/38802547/ можно объяснить одним и тем же явлением? (Я сомневаюсь, что это все-таки тест Google.) - person Olorin; 07.08.2016
comment
@ user10000100_u: ошибка в другом вопросе (вашем вопросе) почти наверняка связана со смесью 32-битных и 64-битных компиляций. В случае сомнений добавьте -m64 (или, если вы действительно уверены, -m32) к имени компилятора (то есть CC = …/gcc-6.0.0 -m64 и т. д.), чтобы убедиться, что все скомпилировано с опцией -m64. Также пересоберите библиотеки GoogleTest; убедитесь, что cmake тоже использует эту опцию. В последнее время я не сталкивался с проблемами (но я давно обновился с Yosemite 10.10.x до El Capitan 10.11.x) — и я создаю свой собственный GCC (в настоящее время 6.1.0), используя текущий XCode для начальной загрузки. - person Jonathan Leffler; 07.08.2016
comment
@JonathanLeffler Прежде всего, спасибо за ваш ответ здесь! С -m64 я получаю следующий вывод: bitbucket.org/snippets/MisesEnForce/k49ML то же, что и до. С -m32 я получаю следующий гораздо более короткий вывод: bitbucket.org/snippets/MisesEnForce/b48op ... Вчера ночью я также использовал Apple clang для загрузки GCC 6.1.0, думая, что проблема может возникнуть из-за него, но я так не думаю. - person Olorin; 07.08.2016
comment
Есть ли риск того, что GCC и Clang создадут несовместимый объектный код C++? Другими словами, что библиотека была скомпилирована Clang, поэтому GCC не может найти символы, потому что они не искажают имена одинаково (вероятно, в качестве меры безопасности, поскольку есть и другие вещи, которые отличаются)? Когда-то давно (в другом тысячелетии) это было проблемой — я не уверен, что это все еще проблема. Я избегаю этой проблемы, используя один компилятор, а не пытаясь взаимодействовать. - person Jonathan Leffler; 07.08.2016
comment
Кроме того, просмотрели ли вы различные типы файлов с file и/или otool, чтобы узнать, что они содержат. Существуют ли файлы с двойной архитектурой или все они имеют единую архитектуру? - person Jonathan Leffler; 07.08.2016
comment
Бывает, что GCC и Clang создают несовместимый объектный код C++, см. несколько сообщений на эту тему на SO. Но здесь я компилирую googletest и свою библиотеку с помощью GCC-6.1.0. Единственная библиотека, которая у меня есть, это libgtest.a, а nm libgtest.a дает следующее: /rjB94/nm-libgtesta - person Olorin; 07.08.2016
comment
file libgtest.a говорит мне libgtest.a: current ar archive. - person Olorin; 07.08.2016