Название путаницы в LLVM

Я пытаюсь создавать и выполнять модули LLVM. Мой код для создания модулей довольно длинный, поэтому я не буду публиковать его здесь. Вместо этого мой вопрос о том, как Clang и LLVM работают вместе, чтобы достичь наименования. Я объясню свой конкретный вопрос, чтобы мотивировать вопрос.

Вот исходный код одного из моих модулей LLVM:

#include <iostream>

int main() {
  std::cout << "Hello, world. " << std::endl;
  return 0;
}

Вот сгенерированный LLVM IR; он слишком велик для StackOverflow.

Когда я пытаюсь выполнить мой модуль с помощью lli, я получаю следующую ошибку:

LLVM ERROR: программа использовала внешнюю функцию '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1Emc', которая не может быть решена!

Запуск символа через деманглер, отсутствующий символ:

_std:: __ 1:: basic_string, std:: __ 1:: allocator > :: basic_string (unsigned long, char)

Дополнительный _ является подозрительным, и функция без главного подчеркивания, похоже, существует в IR!

; Function Attrs: alwaysinline ssp uwtable
define available_externally hidden void @_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1Emc(%"class.std::__1::basic_string"*, i64, i8 signext) unnamed_addr #2 align 2 {
  %4 = alloca %"class.std::__1::basic_string"*, align 8
  %5 = alloca i64, align 8
  %6 = alloca i8, align 1
  store %"class.std::__1::basic_string"* %0, %"class.std::__1::basic_string"** %4, align 8
  store i64 %1, i64* %5, align 8
  store i8 %2, i8* %6, align 1
  %7 = load %"class.std::__1::basic_string"*, %"class.std::__1::basic_string"** %4, align 8
  %8 = load i64, i64* %5, align 8
  %9 = load i8, i8* %6, align 1
  call void @_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2Emc(%"class.std::__1::basic_string"* %7, i64 %8, i8 signext %9)
  ret void
}

Я нахожусь на macOS, поэтому следует ожидать главного подчеркивания, но я думаю, что Clang мог бы добавить его дважды.

Я просмотрел источник LLVM/Clang, и кажется, что есть два шага:

  • Взяв возможно перегруженные функции С++ и переведя их в уникальные имена для LLVM IR
  • Взятие измененного имени из LLVM IR и добавление каких-либо специфических особенностей платформы, например, подчеркивание

Однако это только моя теория. Может ли кто-нибудь объяснить, как работает процесс манипуляции в Clang и LLVM? Как мне создать объекты llvm::DataLayout, чтобы получить правильное управление для моей платформы?


nm -gU /usr/lib/libc++.dylib и nm -gU /usr/lib/libc++abi.dylib не содержат __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorI‌​cEEEC1Emc


Когда я пытаюсь скомпилировать IR, я получаю эту ошибку:

llc generated.ll
clang++ generated.s

Undefined символы для архитектуры x86_64:    "std:: __ 1:: basic_string, std:: __ 1:: allocator > :: data() const", на который ссылаются:       std:: __ 1:: ostreambuf_iterator > std:: __ 1:: __ pad_and_output > (std:: __ 1:: ostreambuf_iterator > , char const *, char const *, char const *, std:: __ 1:: ios_base &, char) в сгенерированном-b4252a.o    "std:: __ 1:: basic_ostream > :: sentry:: operator bool() const", на который ссылаются:       std:: __ 1:: basic_ostream > & std:: __ 1:: __ put_character_sequence > (std:: __ 1:: basic_ostream > &, char const *, unsigned long) в сгенерированном-b4252a.o    "std:: __ 1:: basic_ios > :: fill() const", на который ссылаются:       std:: __ 1:: basic_ostream > & std:: __ 1:: __ put_character_sequence > (std:: __ 1:: basic_ostream > &, char const *, unsigned long) в сгенерированном-b4252a.o    "std:: __ 1:: basic_ios > :: rdbuf() const", на который ссылаются:       std:: __ 1:: ostreambuf_iterator > :: ostreambuf_iterator (std:: __ 1:: basic_ostream > &) в сгенерированном-b4252a.o    "std:: __ 1:: basic_ios > :: widen (char) const", на который ссылаются:       std:: __ 1:: basic_ostream > & std:: __ 1:: endl > (std:: __ 1:: basic_ostream > &) в сгенерированном-b4252a.o    "std:: __ 1:: basic_string, std:: __ 1:: allocator > :: basic_string (unsigned long, char)", на который ссылаются:       std:: __ 1:: ostreambuf_iterator > std:: __ 1:: __ pad_and_output > (std:: __ 1:: ostreambuf_iterator > , char const *, char const *, char const *, std:: __ 1:: ios_base &, char) в сгенерированном-b4252a.o    "std:: __ 1:: basic_ios > :: setstate (unsigned int)", на который ссылаются:       std:: __ 1:: basic_ostream > & std:: __ 1:: __ put_character_sequence > (std:: __ 1:: basic_ostream > &, char const *, unsigned long) в сгенерированном-b4252a.o ld: символ (-ы) не найден для архитектуры x86_64 clang-3.9: ошибка: команда компоновщика завершилась с кодом выхода 1 (используйте -v для вызова вызова)

Ответ 1

Я бы не стал подозревать проблему с изменением имени. С++ имя mangling происходит в интерфейсе (т.е. clang) и является частью довольно хорошо определенного/-документированного стандарта ABI.

Кроме того, я не думаю, что есть поддельное подчеркивание, потому что это не приводит к действительному имени C++ назад, а искаженное имя в ссылке pastebin, которую вы указали, выглядит как:

_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1Emc

Я не на Mac OS, но моделирую с помощью LLVM 3.8.1 в Linux (используя --stdlib=libc++), используя тот же источник и сопоставляя IR по строкам, Я получаю следующий символ:

_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc

который снова возвращается к:

std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__init(unsigned long, char)

который, как я предполагаю, в значительной степени похож на ту же конструкцию.

Итак, я считаю, что ваш компоновщик подхватил неправильную версию libc++.

Вы можете проверить символы, доступные в libc++, привязанные к используемому clang/LLVM, найденному в каталоге, указанному в llvm-config --libdir, или даже проверять запись rpath ваших двоичных файлов toolchain с помощью readelf -d $(which lli).

Если существует несколько установок LLVM (например, система, которую вы сами компилировали из исходников), вам может понадобиться играть с опцией -L clang, которая направляет ld, чтобы добавить этот путь в его список поиска. Быстрая альтернатива (которую я бы не рекомендовал для регулярного использования) - это сделать в командной строке:

LD_LIBRARY_PATH=$(llvm-config --libdir) clang generated.s