在编写代码或构建用LTO编译的脚本时,需要记住哪些注意事项?
这个问题背后的动机是为了更好地理解为什么某些项目在启用LTO时不干净地编译。特别是,我不能建立ICU的LTO启用,无论是在MSVC或GCC。在其他情况下,我可以使用给定的工具链版本启用LTO,但不能使用另一个(较新的)版本;例如,在利比索诺夫中就会出现这种情况。
在我遇到的所有失败案例中,都涉及到由于未解决的符号而导致的链接失败。
为什么会发生这种事?这是工具链、构建脚本或源代码的问题吗?
发布于 2017-02-21 19:50:20
这个答案总结了我在GCC和MSVC中使用LTO进行项目建设时所涉及的一些复杂问题的发现。
GCC
首先,按照GCC维基,要正确构建启用LTO的项目,必须:
gcc-ar而不是二进制ar;gcc-ranlib而不是二进制ranlib;gcc-nm而不是二进制nm;-flto链接。这意味着在传统的./configure && make循环中,必须考虑在相关时设置AR=、RANLIB=和NM=的值。不过,这些步骤很容易被忽略,因为需要改变例如的值。AR是相当罕见的。
现在谈一谈问题:
在GCC 4.8和更早版本中,编译器会将fat对象文件作为默认文件发出。这意味着即使编译后工具(链接器、归档器等)不要识别LTO对象,它们将正常工作(但不实际执行LTO)。
在GCC 4.9及更高版本中,编译器会将瘦对象文件作为默认文件发出,这意味着编译后的工具必须识别LTO对象,否则这些工具就会失败。这就解释了为什么有时LTO的建筑在使用GCC 4.8时会通过,而当使用GCC 4.9和更高版本的时候就会失败。
我还注意到,构建脚本并不总是在需要时将某些配置指令的值正确传递给子脚本。例如,当在MinGW-w64中使用LTO构建静态libiconv时,配置脚本仍然使用ar而不是gcc-ar配置内部libtool,即使被告知AR=gcc-ar。
LTO构建倾向于发现隐藏的错误,特别是由静态init顺序失败引起的错误。它们还可以阻止其他优化,例如ICF (由Gold执行)。
最后,在LTO机器中仍然存在一些明显的错误。在尝试使用启用LTO和其他优化的MinGW-w64编译ICU时,我遇到了这只虫子和内部编译器错误(internal compiler error: in splice_child_die, at dwarf2out.c,可能与在LTO中使用-g有关)。
所有这一切都意味着,由于工具链中的一些缺陷,使用LTO构建一个随机项目仍然是非常重要的。有些项目将成功构建,而另一些项目则不会成功。
MSVC
要在MSVC (称为LTCG)中使用LTO进行编译,编译时必须使用/GL,链接时必须使用/LTCG,这就是事实。
尽管如此,当在MSVC中启用LTCG时,编译器不会发出传统的COFF对象。它发射一个包含IR的特殊类型的对象文件,其头部(ANON_OBJECT_HEADER_BIGOBJ)与COFF头(IMAGE_FILE_HEADER)不同。显然,在构建项目时,这一点都没有区别,因为这些细节留给工具链来处理。
现在,为什么在MSVC中启用LTCG时,ICU不能正确构建?
ICU有一个名为pkgdata的工具,它为给定的体系结构生成对象代码。在构建过程中,该工具用于构建包中的其他实用程序。但是,pkgdata试图通过检查给定的引用对象文件来猜测目标体系结构。在Windows中,该工具假定COFF头,并在32位构建中错误地确定64位体系结构是目标(由于pkg_genc.c:getArchitecture()内部的草率逻辑)。因此MSVC 32位LTCG构建失败.
https://stackoverflow.com/questions/42230141
复制相似问题