GCC умирает, пытаясь скомпилировать 64-битный код на OSX 10.6 - PullRequest
4 голосов
/ 14 января 2010

У меня установлена ​​совершенно новая установка OSX 10.6. Теперь я хотел бы скомпилировать следующую простую C-программу в виде 64-битного двоичного файла:

 #include <stdio.h>

 int main() 
 {
    printf("hello world");
    return 0;
 }

Я вызываю gcc следующим образом:

gcc -m64 hello.c

Тем не менее, это происходит со следующей ошибкой:

Неопределенные символы:

  "___gxx_personality_v0", referenced from:
  _main in ccUAOnse.o
  CIE in ccUAOnse.o
  ld: symbol(s) not found
  collect2: ld returned 1 exit status

Что здесь происходит? Почему умирает GCC? Компиляция без флага -m64 работает нормально.

Ответы [ 4 ]

17 голосов
/ 14 января 2010

Две вещи:

Я не думаю, что вы на самом деле использовали gcc -m64 hello.c. Полученная ошибка обычно является результатом действия, подобного gcc -m64 hello.cc - использованием компилятора C для компиляции кода C ++.

shell% gcc -m64 hello.c
shell% ./a.out
hello world [added missing newline]
shell% cp hello.c hello.cc
shell% gcc -m64 hello.cc
Undefined symbols:
  "___gxx_personality_v0", referenced from:
      _main in ccYaNq32.o
      CIE in ccYaNq32.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Вы можете «заставить это работать» с помощью следующего:

shell% gcc -m64 hello.cc -lstdc++
shell% ./a.out
hello world

Во-вторых, -m64 не является предпочтительным способом указания того, что вы хотите генерировать 64-битный код в Mac OS X. Предпочтительным способом является использование -arch ARCH, где ARCH один из ppc, ppc64, i386 или x86_64. Может быть доступно больше (или меньше) архитектур, в зависимости от того, как настроены ваши инструменты (например, iPhone ARM, устаревший ppc64 и т. Д.). Кроме того, в 10.6 gcc по умолчанию равно -arch x86_64 или генерируется 64-битный код по умолчанию.

Используя этот стиль, компилятор может автоматически создавать «толстые двоичные файлы» - вы можете использовать -arch несколько раз. Например, чтобы создать «Universal Binary»:

shell% gcc -arch x86_64 -arch i386 -arch ppc hello.c
shell% file a.out
a.out: Mach-O universal binary with 3 architectures
a.out (for architecture x86_64):    Mach-O 64-bit executable x86_64
a.out (for architecture i386):  Mach-O executable i386
a.out (for architecture ppc7400):   Mach-O executable ppc

РЕДАКТИРОВАТЬ: Следующее было добавлено, чтобы ответить на вопрос ОП: «Я сделал ошибку и позвонил в мой файл .cc вместо .c. Я все еще не понимаю, почему это должно иметь значение?»

Ну ... это довольно сложный ответ. Я дам краткое объяснение, но я попрошу, чтобы у вас была небольшая вера, что «на самом деле есть веская причина».

Справедливо сказать, что «компиляция программы» - довольно сложный процесс. По историческим и практическим причинам, когда вы выполняете gcc -m64 hello.cc, он фактически разбивается на несколько отдельных шагов за кулисами. Эти шаги, каждый из которых обычно передает результат каждого шага на следующий шаг, приблизительно:

  • Запустите препроцессор C, cpp, для исходного кода, который компилируется. Этот шаг отвечает за выполнение всех операторов #include, различных расширений макросов #define и других «предварительных обработок».
  • Запустите компилятор C правильно для предварительно обработанных результатов C. Результатом этого шага является файл .s или результат кода C, скомпилированного для языка ассемблера.
  • Запустите as ассемблер на источнике .s. Это собирает язык ассемблера в объектный файл .o.
  • Запустите компоновщик ld для файлов .o, чтобы связать различные скомпилированные объектные файлы и различные статические и динамически связанные библиотеки с используемым исполняемым файлом.

Примечание: Это «типичный» поток для большинства компиляторов. Отдельная реализация компилятора не должна следовать вышеуказанным шагам. Некоторые компиляторы объединяют несколько шагов в один из соображений производительности. Например, современные версии gcc не используют отдельный проход cpp. Компилятор tcc, с другой стороны, выполняет все вышеуказанные шаги за один проход, не используя никаких дополнительных внешних инструментов или промежуточных шагов.

В приведенном выше традиционном потоке цепочек инструментов компилятора команда cc (или, в нашем случае, gcc) называется «драйвером компилятора». Это «логический интерфейс» для всех вышеперечисленных инструментов и этапов, и он знает, как разумно применять все этапы и инструменты (например, ассемблер и компоновщик) для создания окончательного исполняемого файла. Однако для этого ему обычно необходимо знать «тип» файла, с которым он имеет дело. Например, вы не можете действительно передать собранный файл .o в компилятор C. Следовательно, есть пара «стандартных» .* обозначений, используемых для указания «вида» файла (см. man gcc для получения дополнительной информации):

  • .c, .h Исходный код C и файлы заголовков C.
  • .m Исходный код Objective-C.
  • .cc, .cp, .cpp, .cxx, .c++ C ++ Исходный код.
  • .hh Файл заголовка C ++.
  • .mm, .M Исходный код Objective-C ++.
  • .s Исходный код на ассемблере.
  • .o Код собранного объекта.
  • .a ar архив или статическая библиотека.
  • .dylib Динамическая разделяемая библиотека.

Также возможно переопределить этот «автоматически определяемый тип файла», используя различные флаги компилятора (см. man gcc, как это сделать), но, как правило, НАМНОГО проще придерживаться стандартных соглашений, чтобы все «просто работает "автоматически.

И, наоборот, если бы вы использовали «драйвер компилятора» C ++, или g++, в вашем первоначальном примере, вы бы не столкнулись с этой проблемой:

shell% g++ -m64 hello.cc
shell% ./a.out
hello world

Причина этого в том, что gcc в основном говорит: «Используйте правила C при управлении цепочкой инструментов», а g++ говорит: «Используйте правила C ++ при управлении цепочкой инструментов». g++ знает, что для создания рабочего исполняемого файла необходимо передать -lstdc++ на этап компоновщика, тогда как gcc, очевидно, не считает это необходимым, даже если он знал, что использовать компилятор C ++ при «Компиляции исходного кода» code "stage из-за окончания файла .cc.

Некоторые другие компиляторы C / C ++, доступные для Mac OS X 10.6 по умолчанию: gcc-4.0, gcc-4.2, g++-4.0, g++-4.2, llvm-gcc, llvm-g++, llvm-gcc-4.0, llvm-g++-4.0, llvm-gcc-4.2, llvm-g++-4.2, clang. Эти инструменты (обычно) меняют первые два шага в цепочке инструментов и используют те же инструменты нижнего уровня, что и ассемблер и компоновщик. Компиляторы llvm- используют внешний интерфейс gcc для анализа кода C и превращения его в промежуточное представление, а затем используют инструменты llvm для преобразования этого промежуточного представления в код. Поскольку инструменты llvm используют "низкоуровневую виртуальную машину" в качестве своего почти конечного результата, он позволяет использовать более богатый набор стратегий оптимизации, наиболее заметным из которых является то, что он может выполнять оптимизацию для различных уже скомпилированных файлов .o , Обычно это называется link time optimization. clang - это совершенно новый компилятор C, который также нацеливается на инструменты llvm в качестве выходных данных, что позволяет выполнять те же виды оптимизации.

Итак, поехали. Не очень краткое объяснение того, почему gcc -m64 hello.cc не удалось для вас. :)

РЕДАКТИРОВАТЬ: Еще одна вещь ...

Это обычная «техника драйвера компилятора», когда такие команды, как gcc и g++ sym-ссылка на один и тот же исполняемый файл драйвера компилятора «все в одном». Затем во время выполнения драйвер компилятора проверяет путь и имя файла, которые использовались для создания процесса, и динамически переключает правила на основе того, заканчивается ли это имя файла gcc или g++ (или эквивалентным). Это позволяет разработчику компилятора повторно использовать большую часть кода внешнего интерфейса, а затем просто изменить несколько различий, требуемых между ними.

1 голос
/ 14 января 2010

Я не знаю, почему это происходит (у меня работает нормально), но

  1. Попробуйте скомпилировать с g++ или ссылкой на libstdc++. ___gxx_personality_v0 - это символ, используемый GNU C ++ для настройки обратного вызова SjLj для деструкторов, поэтому по какой-то причине код C ++ проникает в ваш код C. или
  2. Снять флаг -m64. Насколько мне известно, двоичные файлы, сгенерированные GCC 4.2 на 10.6, по умолчанию устанавливаются на 64-разрядные. Вы можете file проверить вывод и убедиться, что он читает "Mach-O 64-bit исполняемый файл x86_64". или
  3. Переустановите последний Xcode из http://developer.apple.com/technology/xcode.html.
0 голосов
/ 20 мая 2015

У нас была похожая проблема при работе с CocoaPods при создании и использовании модуля, содержащего код Objective-C ++, поэтому я подумал, что также стоит упомянуть:

Вы должны отредактировать .podspec модуля, который содержит код c ++, и добавить строку вроде:

s.xcconfig = {
    'OTHER_LDFLAGS' => '$(inherited) -lstdc++',
}
0 голосов
/ 14 января 2010

OK:

  • добавление -m64 ничего не делает, обычный gcc без параметров - это 64-битная компиляция
  • если вы действительно не в курсе, вам следует обновить и установить новый xcode
  • ваша программа отлично работает с или без -m64 на 10.6.2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...