Skip to content

Commit 202213e

Browse files
authored
Update lto-livepatch.md
1 parent 3e7c598 commit 202213e

File tree

1 file changed

+34
-63
lines changed

1 file changed

+34
-63
lines changed

source/_posts/lto-livepatch.md

Lines changed: 34 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,23 @@ GCC9支持设置LTO使用时的并行度(flto=n),这个并行度会影响WPA
124124

125125
### LTO编译相关的参数
126126

127-
1. 开启LTO编译 - (-flto)
127+
1. 开启LTO编译 - -flto
128128

129-
2. 控制是否产生fat objects (-ffat-lto-objects)
129+
2. 设置partion agrithom - -flto-partition=alg
130+
131+
3. 开启LTO的增量编译 (supported from GCC 15)
132+
+ -flto-incremental=path
133+
+ -flto-incremental-cache-size=n
134+
135+
4. LTO objects生成时的压缩参数 - -flto-compression-level=n
136+
137+
5. 控制是否产生fat objects - -ffat-lto-objects
130138

131139
所谓的fat object,是指产生的object中既包含了正常object code,还有LTO所需的IR。fat object的优点主要是兼容性,对于不支持LTO的工具链依然可以使用,在link阶段依然可以进行normal链接。缺点是编译耗时严重,产生的object过大。
132140

133141
LTO发展早期,GCC使能LTO机制之后,默认产生的为fat object。后期随着GNU工作链发展成熟,默认产生的object为slim object,即object只包含LTO需要的IR。
134142

135-
3. LTO的编译模式
143+
6. LTO的编译模式
136144

137145
+ LTO mode
138146
整个程序作为一个源码文件进行优化,优点是进行最大程度的优化,缺点是编译并行度差。
@@ -143,12 +151,15 @@ a. Local generation(LGEN)
143151
b. Whole Program Analysis(WPA)
144152
c. Local transformations(LTRANS)
145153

146-
4. [lto1的参数](https://gcc.gnu.org/onlinedocs/gccint/Internal-flags.html)
154+
7. [lto1的参数](https://gcc.gnu.org/onlinedocs/gccint/Internal-flags.html)
147155
+ -fwpa
148156
+ -fltrans
149157
+ -fltrans-output-list=file
150158
+ -fresolution=file
151159

160+
5. dwarf相关的 - -fdump-earlydebug
161+
LTO objects的dwarf信息不够准确,earlydebug项目致力于解决该问题。这个选项可以dump出中间过程使用的earlydebug信息。
162+
152163
### [LTO开启后,objects包含的相关section](https://gcc.gnu.org/onlinedocs/gccint/LTO-object-file-layout.html)
153164

154165
1. .gnu.lto_.opts(Command line options)
@@ -169,79 +180,42 @@ c. Local transformations(LTRANS)
169180

170181
## LTO开启后的编译过程
171182

172-
在不开启的LTO的情况下,C语言编译涉及到的组件有cc1,collect2,其中cc1生成objects文件,collect2被ld(linker)调用,生成最终的binary。
173-
174-
在加入LTO之后,C语言编译的组件有cc1,collect2,lto1和lto-wrapper,其中lto1和lto-wrapper是针对LTO机制所增加的组件。
183+
在不开启的LTO的情况下,C语言编译涉及到的组件有cc1,collect2,其中cc1生成objects文件,再通过collect2的调用,生成最终的binary。
175184

176-
在环境上打开execsnoop,运行以下命令,观察gcc命令的调用过程:
177-
> gcc -o server a.c b.c c.c -v -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -save-temps
185+
在加入LTO之后,C语言编译的组件有cc1,collect2,lto1和lto-wrapper,其中lto1和lto-wrapper是针对LTO机制所增加的组件,以编译器插件的形式引入。
178186

179-
获得的exec调用结果输出为:
180-
181-
```
182-
gcc 398299 398005 0 /usr/lib64/ccache/gcc -o server a.c b.c c.c -v -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -save-temps
183-
gcc 398299 398005 0 /usr/bin/gcc -o server a.c b.c c.c -v -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -save-temps
184-
cc1 398300 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/cc1 -E -quiet -v a.c -mtune=generic -march=x86-64 -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -fpch-preprocess -o server-a.i
185-
cc1 398301 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/cc1 -fpreprocessed server-a.i -quiet -dumpdir server- -dumpbase a.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -o server-a.s
186-
as 398302 398299 0 /usr/bin/as -v --64 -o server-a.o server-a.s
187-
cc1 398303 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/cc1 -E -quiet -v b.c -mtune=generic -march=x86-64 -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -fpch-preprocess -o server-b.i
188-
cc1 398304 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/cc1 -fpreprocessed server-b.i -quiet -dumpdir server- -dumpbase b.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -o server-b.s
189-
as 398305 398299 0 /usr/bin/as -v --64 -o server-b.o server-b.s
190-
cc1 398306 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/cc1 -E -quiet -v c.c -mtune=generic -march=x86-64 -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -fpch-preprocess -o server-c.i
191-
cc1 398307 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/cc1 -fpreprocessed server-c.i -quiet -dumpdir server- -dumpbase c.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -flto=auto -ffat-lto-objects -fdata-sections -ffunction-sections -o server-c.s
192-
as 398308 398299 0 /usr/bin/as -v --64 -o server-c.o server-c.s
193-
collect2 398309 398299 0 /usr/libexec/gcc/x86_64-redhat-linux/13/collect2 -plugin /usr/libexec/gcc/x86_64-redhat-linux/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-redhat-linux/13/lto-wrapper -plugin-opt=-fresolution=server.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -flto=auto --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o
194-
ld 398310 398309 0 /usr/bin/ld -plugin /usr/libexec/gcc/x86_64-redhat-linux/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-redhat-linux/13/lto-wrapper -plugin-opt=-fresolution=server.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o server
195-
lto-wrapper 398311 398310 0 /usr/libexec/gcc/x86_64-redhat-linux/13/lto-wrapper @server.lto_wrapper_args
196-
make 398312 398311 0 /usr/bin/make --version
197-
gcc 398313 398311 0 /usr/bin/gcc @./server.ltrans_args
198-
lto1 398314 398313 0 /usr/libexec/gcc/x86_64-redhat-linux/13/lto1 -quiet -dumpbase ./server.wpa -mtune=generic -march=x86-64 -version -fno-openmp -fno-openacc -fno-pie -fcf-protection=none -ffat-lto-objects -fdata-sections -ffunction-sections -fltrans-output-list=./server.ltrans.out -fwpa=8 -fresolution=server.res -flinker-output=exec @./server.wpa.args.0
199-
make 398315 398311 0 /usr/bin/make -f /tmp/ccSoHZVP.mk -j8 all
200-
gcc 398316 398315 0 /usr/bin/gcc -xlto -c -fno-openmp -fno-openacc -fno-pie -fcf-protection=none -mtune=generic -march=x86-64 -v -ffat-lto-objects -fdata-sections -ffunction-sections -save-temps -mtune=generic -march=x86-64 -dumpdir server. -dumpbase ./server.ltrans0.ltrans
201-
lto1 398317 398316 0 /usr/libexec/gcc/x86_64-redhat-linux/13/lto1 -quiet -dumpbase ./server.ltrans0.ltrans -mtune=generic -march=x86-64 -version -fno-openmp -fno-openacc -fno-pie -fcf-protection=none -ffat-lto-objects -fdata-sections -ffunction-sections -fltrans ./server.ltrans0.o -o ./server.ltrans0.ltrans.s
202-
as 398318 398316 0 /usr/bin/as -v --64 -o ./server.ltrans0.ltrans.o ./server.ltrans0.ltrans.s
203-
```
204-
205-
分析pid以及ppid,可以得到以下的调用结构:
187+
[redis](https://github.com/redis/redis)源码为例,开启LTO后,通过execsnoop观察进程的执行过程,可以得到以下的进程树(有省略):
206188

207189
![](./images/gcc-compile-lto.png)
208190

209-
树的不同层次之间的箭头代表是父子进程,同一层次之间的箭头代表前者的输出是后者的输入。
210191

211-
在这个样例里,由于源码数量较少,所有的源码都被编译到了一个object内。在处理更大的项目构建时,会观察到原有的objects被划分到若干个子object内。
192+
可以看到,在原有的linker基础上,通过插件的方式引入lto-wrapper,对应上面的讨论,lto-wrapper分为两个过程:
193+
1. WPA阶段:这阶段会读取全部的object信息,并且按照LTO的partion设置,重新划分成*一定数量*的新object
194+
2. ltrans阶段:将划分后的新object以一定的*并发设置*重新进行编译
195+
196+
下面,我们以[simple-ftp](https://github.com/sunaku/simple-ftp)的源码为例,来具体分析LTO的编译过程,以及中间文件的生成情况。
212197

213-
以openssh的sshd为例,产生的子objects的命名如下:
198+
通过以下命令,编译server二进制:
214199
```
215-
/tmp/ccW5uCAJ.ltrans0.ltrans.o
216-
/tmp/ccW5uCAJ.ltrans7.ltrans.o
217-
/tmp/ccW5uCAJ.ltrans10.ltrans.o
200+
gcc -v --save-temps -g -flto -ffat-lto-objects -fdata-sections -ffunction-sections -fdump-earlydebug server.c service.c siftp.c -o server
218201
```
219202

220-
该命令具有如下特点:
221-
1. 具备统一的前缀 - ccW5uCAJ
222-
2. 具备连续的编号,这些编号也是链接顺序,决定了在符号表中的排列次序
223-
224-
这一过程的具体描述可以参考[GCC-WHOPR](https://gcc.gnu.org/wiki/whopr/driver)的描述,里面描述了LGEN,WPA,LTRANS阶段的详细行为以及LTO插件的具体实现。
225-
226-
227-
## LTO编译过程拆解
228-
[simple-ftp](https://github.com/sunaku/simple-ftp)为例,安装上一章节的概况,具体分析LTO过程的执行过程,以及生成中间文件的内容。
229-
230-
这里我们只观察server的生成过程,编译命令如下:
203+
观察日志输出,可以得到以下编译输出(截取链接相关的部分):
231204
```
232-
gcc -v --save-temps -g -flto -fdump-earlydebug server.c service.c siftp.c -o server
205+
233206
```
234207

235-
观察对应的日志输出,gcc大致进行了以下过程:
236-
1. 分别将每个源码文件编译成汇编文件
237-
2. 以collect2为入口,调用ld,尝试链接生成可执行文件
208+
### WPA的执行过程,以及生成的中间文件
238209

239-
到以上过程,LTO/no LTO的流程都是一样。
210+
### ltrans的执行过程,以及生成的中间文件
240211

241-
对于开启LTO的编译来说
212+
## 以GCC15为例,分析LTO的源码实现
242213

243-
## LTO的源码实现
214+
1. collect2如何调用lto-wrapper
244215

216+
2. LTO的partion过程
217+
218+
3. 经过ltrans生成的object如何对应dwarf信息 - early debug
245219

246220
## 发行版使用的LTO参数
247221

@@ -255,8 +229,6 @@ fedora使用的编译参数通过rpm包redhat-rpm-config引入,解开这个包
255229
其他发行版的使用情况可以参考以下wiki:
256230
+ [gentoo](https://wiki.gentoo.org/wiki/LTO)
257231

258-
259-
260232
## 参考链接
261233

262234
1. [Interprocedural_optimization](https://en.wikipedia.org/wiki/Interprocedural_optimization)
@@ -266,4 +238,3 @@ fedora使用的编译参数通过rpm包redhat-rpm-config引入,解开这个包
266238

267239

268240

269-

0 commit comments

Comments
 (0)