【问题标题】:linking llvm produced object code with ld将 llvm 生成的目标代码与 ld 链接
【发布时间】:2018-08-03 17:24:19
【问题描述】:

我编写了一个小型编译器,它使用 llvm(通过 c++)生成目标文件(在 linux 系统中)。

当我用 gcc 链接编译后的输出时,程序运行良好:

myCompiler source.mylang -o objCode
gcc objCode -o program
./program #runs fine

但如果我尝试将其与 ld 链接,则在运行程序时会出现分段错误:

myCompiler source.mylang -o objCode
ld objCode -e main -o program   #ld does not print any error or warning.
./program #Segmentation fault (core dumped)

这是编译器输出的 llvm 代码(使用 myLlvmModule->print 函数):

; ModuleID = 'entryPointModule'
source_filename = "entryPointModule"

define i32 @main() {
entry:
  %x = alloca i32
  store i32 55, i32* %x
  ret i32 0
  ret i32 0
}

为什么 ld 失败,而 gcc 成功了? 我认为编写编译器后,唯一需要的步骤就是调用链接器。是否需要其他编译器(如 gcc)?

如果是,为什么? 如果没有,我怎样才能让 ld 工作?

编辑: readelf -d 工作二进制文件:

Dynamic section at offset 0xe00 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x4b8
 0x000000000000000d (FINI)               0x684
 0x0000000000000019 (INIT_ARRAY)         0x200df0
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200df8
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x298
 0x0000000000000005 (STRTAB)             0x348
 0x0000000000000006 (SYMTAB)             0x2b8
 0x000000000000000a (STRSZ)              125 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x200fc0
 0x0000000000000007 (RELA)               0x3f8
 0x0000000000000008 (RELASZ)             192 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW PIE
 0x000000006ffffffe (VERNEED)            0x3d8
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x3c6
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

损坏的二进制文件的相同命令:

There is no dynamic section in this file.

【问题讨论】:

  • 您需要与 C 运行时库链接。 en.wikipedia.org/wiki/Runtime_library
  • 您能发布两个生成的最终二进制文件中readelf -d 的输出吗?另外,当你用gdb 运行它时会发生什么?回溯是什么?
  • 而 gdb 说“程序收到信号 SIGSEGV,分段错误。0x0000000000000001 in ?? ()”
  • 至于 readelf,我更新了帖子并包含了输出。
  • ewindes,你能解释一下为什么我需要 c-runtime 库吗?该程序只是声明一个变量并返回。我想了解 c 运行时库将添加到程序中的内容。此外,当我在 ld 命令中添加 -lc 时,最终的二进制文件根本不会执行。我得到“bash:../data/pr:没有这样的文件或目录”,即使它确实存在。

标签: gcc llvm ld


【解决方案1】:

您的入口点尝试返回堆栈上不存在的返回地址,这就是程序跳转到地址零的原因。

程序的入口点预计不会返回。它必须通过调用_exit(或相关的系统调用)来终止进程。

【讨论】:

  • 我明白了。所以我想我应该将 main 方法包装到其他方法,然后进行系统调用。我会尽快尝试!非常感谢您的回答。
  • Florian Weimer,你知道 llvm 库是否可以发出系统调用指令来终止程序吗?我做了包装函数(我称之为_start),只是调用了main。现在我必须添加系统调用,但我没有找到任何这样的指令llvm.org/doxygen/classllvm_1_1IRBuilder.html...
  • 我认为 LLVM 本身不支持系统调用调用约定。您需要使用内联汇编器或调用用汇编器编写的单独函数。
  • 弗洛里安·韦默,我终于成功了。正如您所说,问题是程序的退出。我做了以下事情:我将我的主函数(它返回一个 int)包装到另一个我称之为 _customStart 的函数中。此方法永远不会真正返回。它调用 main,然后从 C lib 调用 exit(),返回值为 main,作为 agrument(即程序的退出代码)。然后我用 -e _customStart 调用 ld,加上 gcc 也有的一些链接选项(我用 gcc -v 编译了一个小程序来查看链接选项)。程序现在运行良好,退出代码也得到尊重。
  • 我更喜欢使用 exit() 而不是编写内联汇编,因为第二种方法是特定于操作系统和架构的,所以我必须自己处理所有这些复杂性。
猜你喜欢
  • 2018-06-11
  • 1970-01-01
  • 1970-01-01
  • 2021-01-27
  • 1970-01-01
  • 2012-12-19
  • 2011-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多