在LLVM中命名错误混淆

我一直在尝试构建和执行LLVM模块 . 我生成模块的代码很长,所以我不会在这里发布 . 相反,我的问题是Clang和LLVM如何协同工作来实现名称修改 . 我会解释我的具体问题来激发这个问题 .

以下是我的一个LLVM模块的源代码:

#include <iostream>

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

Here is the generated LLVM IR;它对于StackOverflow来说太大了 .

当我尝试使用 lli 执行我的模块时,出现以下错误:

LLVM错误:程序使用了无法解析的外部函数'__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1Emc'!

通过demangler运行符号,缺少的符号是:

_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源代码,看起来有两个错误的步骤:

  • 考虑可能重载的C函数并将它们分解为LLVM IR的唯一名称

  • 从LLVM IR中获取一个受损的名称,并添加任何特定于平台的怪癖,例如前导下划线

但是,这只是我的理论 . 有人可以解释一下在Clang和LLVM中修剪过程是如何工作的吗?我应该如何创建我的 llvm::DataLayout 对象以获得我的平台的正确修改?


nm -gU /usr/lib/libc++.dylibnm -gU /usr/lib/libc++abi.dylib 不包含 __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1Emc


当我尝试编译IR时,我收到此错误:

llc generated.ll
clang++ generated.s

体系结构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)in generated-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)in generated-b4252a.o“std :: __ 1 :: basic_ios> :: rdbuf()const”,引用自:std :: __ 1 :: ostreambuf_iterator> :: ostreambuf_iterator(std :: _1 :: basic_ostream>&)in generated-b4252a.o“std :: __ 1 :: basic_ios> :: widen(char)const”,引用自:std :: __ 1 :: basic_ostream>&std :: __ 1 :: endl >(STD :: __ 1 :: b asic_ostream>&)in generated-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)in generated-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 )in generated-b4252a.o ld:找不到架构x86_64 clang-3.9的符号:错误:链接器命令失败,退出代码为1(使用-v查看调用)

回答(1)

2 years ago

我不会怀疑名称错位问题 . C名称重整发生在前端(即 clang ),它是一个非常明确定义的/ -documented ABI standard的一部分 .

此外,我认为没有虚假的下划线,因为这不会产生有效的 C++ 名称,并且您提供的pastebin链接中的错位名称显示为:

_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1Emc

我不是在Mac OS上,而是在Linux上使用我的LLVM 3.8.1进行模拟(使用 --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 给出的目录中,甚至可以使用 readelf -d $(which lli) 检查工具链二进制文件的rpath条目 .

如果有多个LLVM安装(例如,您自己从源代码编译的系统1和一个),您可能必须使用 clang-L 选项 . 指示 ld 在其搜索列表中添加该路径 . 一个快速的替代方案(我不建议常规使用)是在命令行上执行此操作:

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