首页 > 解决方案 > 编译后 LLVM IR 到可执行段错误

问题描述

我目前正在为一种玩具语言编写编译器。这对我来说是一个新领域。我正在使用LLVM C++ API 生成 LLVM IR并从那里生成一个对象。

问题出在(我认为)链接对象并能够执行它。


我已经main.ll包含了我能想出的最低限度的 IR:

define void @main() {
  ret void
}

这运行得很好lli main.ll,即它什么也不做。

我将其编译为对象格式:llc --filetype=obj -o main.{o,ll}.

并链接到没有现有的图书馆:ld.lld -o main{,.o}

但是,生成的二进制文件会立即出现 segfaults。我接受了一些教程的建议,这些教程使我尝试通过 GCC 进行链接,我被告知“在制作 PIE 对象时不能使用 [Relocations]”,维基百科告诉我这是指生成的二进制文件中的位置独立性。

所以我重新编译为对象:llc --filetype=obj --relocation-model=pic main.{o,ll}并使用 GCC 重新编译并且它工作,运行输出没有按预期做任何事情。

但是再次运行该ld.lld命令并再次尝试运行该二进制文件,会立即出现段错误。

所以,我遇到的第一个问题是:对于这个简单的例子,我在链接对象(假设我链接正确)和二进制文件之间缺少什么步骤?

ld即使我没有特别使用任何一个标志,是否还有一些必需的库?


当我尝试链接 libc 以printf在 IR 中使用时,即使使用 GCC 方法也会出现更多问题,但我认为在攻击之前我需要更好地理解这个简单的示例。

任何帮助,将不胜感激。

标签: compilationllvmexecutablellvm-ir

解决方案


对于发现这个并试图将.ll文件制作成可执行文件的其他人:我发现 C 运行时库是缺少的。这些默认情况下都包含在内,gccclang遗憾的是,这些-v选项并没有给我太多继续......

使用LLVM 下的重定位模型PIC_并将生成的对象与 C 运行时库动态链接,我设法让.ll文件定期运行。

一个示例命令(显然是特定于操作系统的)将是:

ld --verbose -L/usr/lib -lc \
  -dynamic-linker \
  /lib64/ld-linux-x86-64.so.2 \
  /usr/lib/Scrt1.o \
  /usr/lib/crti.o \
  /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtbeginS.o \
  /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtendS.o  \
  <object file> \
  -o <binary> \
  /usr/lib/crtn.o

除了猜测之外,我不能 100% 确定这件事的“原因”,但这有效,而您的标准ld -L... -lc <object file>无效。

如果有人可以提供澄清,我会很乐意接受他们的回答。


推荐阅读