compilation - 编译后 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 方法也会出现更多问题,但我认为在攻击之前我需要更好地理解这个简单的示例。
任何帮助,将不胜感激。
解决方案
对于发现这个并试图将.ll
文件制作成可执行文件的其他人:我发现 C 运行时库是缺少的。这些默认情况下都包含在内,gcc
但clang
遗憾的是,这些-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>
无效。
如果有人可以提供澄清,我会很乐意接受他们的回答。
推荐阅读
- scala - 为什么在 Spark 中创建数据集需要编码器
- r - r- ggplot2 颜色和填充不起作用
- java - 有没有办法使用 Jackson(或类似的库)针对现有的 Java 对象值对 JSON 对象键进行拼写检查?
- c++ - 为什么声明私有基类会使类型名称无法访问?
- c++ - Switch 在 while 循环中不断进入默认情况
- php - 在codeigniter3.0中重定向到其他功能后,会话自动取消设置
- class - 在堆栈上分配 D 类
- c# - 将 ASP Classic 数学语句转换为 Razor
- java - 从 Java 中不断更新的文件中读取新数据
- c# - 如何确定一个值是由 NHibernate C# 还是由应用程序代码设置的?