Go:对象文件&重定位

vearne · · 1485 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_HxAju6n33e9Y8AJwMuQL3w.png) **本文章基于 Go 1.14** 重定位是链接过程中的一个阶段,重定位是链接过程中为每个外部符号分配适当地址。由于每个包都是单独编译的,因此它们不知道来自其它包的函数或者变量在哪里。 让我们从一个需要重定位的简单示例开始。 ## 编译 以下程序涉及两个不同的程序包:main 和 fmt。 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_4_DaAwHmqJbhwP8Tn10Dzg.png) 构建此程序将首先涉及编译器,该编译器分别编译每个包。 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_4HLpept1qBXFJvL_r4qptQ.png) 通过命令 ```bash go tool compile -S -l main.go ``` 我们可以查看指令在中间文件(译者注:目标文件)中的临时地址。 一旦我们的程序被编译,我们可以使用 ```bash go tool compile -S -l main.go ``` 来查看程序对应的汇编代码 要查看编译器生成的指令,你有多种不同的方法: * 重新编译,并打印汇编指令。命令是: ```bash go tool compile -S -l main.go "".main STEXT size=137 args=0x0 locals=0x58 0x0000 00000 (main.go:7) TEXT "".main(SB) [...] 0x0058 00088 (main.go:8) CALL fmt.Println(SB) ``` 参数 `-l` 用于避免内联,使得汇编代码更容易被阅读。 生成的汇编文件表明调用 `Println` 的指令相对 `main` 函数入口偏移 88 个字节。这个偏移对于链接器重新定位函数调用将会非常有用。 * 使用以下命令,反汇编已经生成的 main.o: ```bash go tool objdump main.o TEXT %22%22.main(SB) [...] main.go:8 0x57e e800000000 CALL 0x583 [1:5]R_CALL:fmt.Println ``` 标识符 `R_CALL` 代表重定位调用 然而由于该函数属于另一个包,因此编译器不知道该函数实际位于何处。使用命令: ```bash go tool nm main.o ``` 可以检查生成的文件 `main.o`,并列出其中包含的符号。下图是输出 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1__cz0Ozr4acR3Sj0GbirP2Q.png) 我们可以注意到,它需要使用 go 工具 nm 命令而不是本机 nm 命令。 实际上,Go 生成的目标文件(.o)具有自定义格式。 符号 U 代表未定义,表示编译器不知道该符号在哪里。该符号必须重定位,即找到 `Println` 的地址,才能成功的进行调用。这就是链接器需要参与的工作。在介绍链接器的工作之前,我们分析了目标文件 `main.o`, 以及它能够提供所有可用数据。链接器可以基于这些数据开展工作。 ## 目标文件 这篇[文档](https://golang.org/pkg/cmd/internal/objabi/)解释了目标文件的内容和格式 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_WwlsAnj0J9-dUkvBYWS5sQ.png) 该文件由依赖项,调试信息(DWARF), 索引符号列表,数据段以及符号列表。符号列表中包含每个符号都需要我们进行重定向。以下是它的格式: ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_so340hPaauZOPChu3tvSCA.png) 每个符号均以十六进制字节 fe 开头。可以使用十六进制编辑器打开目标文件 main.o 时。例如,对于 Mac,可以使用 xxd(译者注:xxd 是 mac 下的一个命令)。 下面是内容的一部分,对符号(译者注:实际是对符号开头的标志"fe")进行了高亮显示。 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_PL_o1t7dokehoO3X6rbUaw.png) 符号 `main.main` 是符号列表中的第一个符号。 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_KOng-8Ed1XkprfvxSsqaXg.png) 前几个字节 `0102 00dc 0100 dc01 0a` 代表了前面定义的一系列属性:type、flag、size、data、以及重定位的次数。 字节以 `zigzag-varint` 格式存储。`varint` 是以可变长字节的方式存储整数的值。 `zigzag` 通过对最少有效位进行编码来是以减少编码后数据的大小 然后,重定位 `Println` 是一组字节序列 `b201 0810 0008`: * `b201` 是偏移值 89 编码后的结果。这个偏移值是一个 `int32` 类型。感谢 `varint`,存储它仅耗费了 2 个字节. * `08` 是需要重写的字节的数量,编码后的值是 4 * `10` 是重定位的类型,编码值 8 表示 `R_CALL`, 即重定位函数调用 * `08` 是对索引符号的引用 装载器现在已经拥有了重定位所需的所有必要信息,可以生成可执行的二进制文件了。 ## 重定位 链接器的其中一个阶段是分配虚地址给所有的段和指令。可以使用命令 ```bash objdump -h my-binary ``` 可视化每个段的地址。下面是前面示例的输出 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_JGMu2mnGI-HTp35GHqx3mg.png) 函数 `main` 位于__text 段,它也能通过命令 ```bash objdump -d my-binary ``` 找到,这个命令显示了指令的地址。 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200701-Go:Object-File%26Relocations/1_tZX5Ills5d4Dnk0Z5iZ1pA.png) 函数 `main` 入口地址是 `109cfa0`,函数 `fmt.Println` 的入口地址是 `1096a00`。一旦虚地址被分配,就会非常容易的重定位 `fmt.Println` 的入口地址。链接器将会用 `fmt.Println` 的入口地址依次减去 `main` 的入口地址、指令的偏移值、指令所占的字节大小。这样我们就能得到调用 `fmt.Println` 的全局偏移。对于前面的例子中,我们可以进行如下的操作: ```bash 1096a00 (fmt.Println) — 109cfa0 (main) — 84 (offset inside the main function) — 4 (size) = -26109 ``` 现在,指令知道函数 `fmt.Println` 的入口地址与当前内存地址的偏移是 `-26109`,调用可以成功执行。

via: https://medium.com/a-journey-with-go/go-object-file-relocations-804438ec379b

作者:Vincent Blanchon  译者:vearne  校对:unknwon

本文由 GCTT 原创编译,Go语言中文网 荣誉推出


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1485 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传