如果不需要对象,则阻止它们被链接?
我有一个正在使用 make 构建的 ARM 项目。我正在根据源目录中所有 .c 和 .cpp 文件的名称创建要链接的对象文件列表。但是,如果对象从未使用过,我想将其排除在链接之外。即使我将这些对象包含在要链接的对象列表中,链接器是否会自动从 .elf 文件中排除这些对象?如果没有,是否有办法生成仅包含需要链接的对象的列表?
I have an ARM project that I'm building with make. I'm creating the list of object files to link based on the names of all of the .c and .cpp files in my source directory. However, I would like to exclude objects from being linked if they are never used. Will the linker exclude these objects from the .elf file automatically even if I include them in the list of objects to link? If not, is there a way to generate a list of only the objects that need to be linked?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您必须以不同的方式编译代码以去除未使用的函数和数据。通常所有对象都被编译成相同的符号,因此如果不使用它们就不能单独省略。
将以下两个开关添加到您的编译器行:
-ffunction-sections -fdata-sections
当您编译时,编译器现在会将各个函数和数据放入各自的部分中,而不是将它们全部集中在一个模块部分中。
然后,在链接器中指定以下内容:
--gc-sections
这指示链接器删除未使用的部分(“gc”用于垃圾收集)。它将垃圾收集部分文件和整个文件。例如,如果您正在编译一个对象,但在该对象中只使用了 100 的 1 个函数,它将丢弃您未使用的其他 99 个函数。
如果遇到找不到函数的问题(由于库之间的外部因素等各种原因而发生),您可以在链接器文件中使用
.keep
指令 (*.ld
)以防止对这些单独的函数进行垃圾收集。You have to compile your code differently to strip out function and data that isn't used. Usually all the objects are compiled into the same symbol, so they can't be individually omitted if they're not used.
Add the two following switches to your compiler line:
-ffunction-sections -fdata-sections
When you compile, the compiler will now put individual functions and data into their own sections instead of lumping them all in one module section.
Then, in your linker, specify the following:
--gc-sections
This instructs the linker to remove unused sections ("gc" is for garbage collection). It will garbage collect parts of files and entire files. For example, if you're compiling an object, but only use 1 function of 100 in the object, it will toss out the other 99 you're not using.
If you run into issues with functions not found (it happens due to various reasons like externs between libraries), you can use
.keep
directives in your linker file (*.ld
) in order to prevent garbage collection on those individual functions.如果你使用RealView,似乎是可以的。本节讨论它:
3.3.3 未使用的节消除
未使用的节消除会删除从未执行的代码或不执行的数据
代码从最终图像中引用。这种优化可以通过以下方式控制
--remove、--no_remove、--first、--last 和 --keep 链接器选项。使用 --info 未使用
链接器选项指示链接器生成已被使用的未使用部分的列表
被淘汰。
If you are using RealView, it seems that it is possible. This section discusses it:
3.3.3 Unused section elimination
Unused section elimination removes code that is never executed, or data that is not
referred to by the code, from the final image. This optimization can be controlled by the
--remove, --no_remove, --first, --last, and --keep linker options. Use the --info unused
linker option to instruct the linker to generate a list of the unused sections that have been
eliminated.
正如很多人所说,答案是“取决于”。根据我的经验,RVCT 对于死代码剥离非常有用。未使用的代码和数据几乎总是会在最终链接阶段被删除。另一方面(至少没有 LLVM 后端),GCC 在整个图像静态分析方面相当差,并且在删除未使用的代码方面表现不佳(如果您的代码位于不同的部分,那么您将遭遇不幸)需要长距离跳跃)。您可以采取一些措施来缓解这种情况,例如使用函数部分,它为每个函数创建一个单独的部分,并实现一些更好的死代码剥离。
让链接器生成二进制文件的映射文件,以便您可以看到其中的内容以及被删除的内容。
Like many people said, the answer is "depends". In my experience, RVCT is very good about dead code stripping. Unused code and data will almost always be removed in the final link stage. GCC, on the other hand (at least without the LLVM back end), is rather poor at whole image static analysis and will not do a very good job at removing unused code (and woe be it to you if your code is in different sections requiring long jumps). You can take some steps to mitigate it, such as using function-sections, which creates a separate section for each function and enables some better dead code stripping.
Have your linker generate a map file of your binary so you can see what made it in there and what got stripped out.
根据编译器/链接器的复杂程度和优化级别,链接器不会链接未调用的代码。
Depending on the sophistication of the compiler/linker and optimization level, the linker will not link in code that isn't being called.
您使用什么编译器/链接器?有些链接器会自动执行此操作,有些链接器则以命令行选项的形式提供该功能。
What compiler/linker are you using? Some linkers do this automatically, and some provide the feature as a command-line option.
根据我的经验,许多编译器不会在目标文件的基础上包含未使用的代码。有些可能没有这个解决方案,并且将包含整个库(“因为这使得构建过程更快”)。
例如,给定一个文件
junk.c
,它具有三个函数:Func1
、Func2
和Func3
。构建过程创建一个目标文件,junk.o
,其中包含所有三个函数。如果未使用函数Func2
,则无论如何都会包含该函数,因为链接器无法从目标文件中排除某个函数。另一方面,给定文件:
Func1.c
、Func2.c
和Func3.c
,具有上述函数,每个文件一个。如果Func2.c
中的Func2
未使用,链接器将不会包含它。有些链接器足够智能,可以将文件排除在库之外。但是,每个链接器的文件包含(以及文件排除)粒度都不同。请阅读链接器手册或联系他们的客户支持以获取确切信息。
我建议将可疑函数移至单独的文件中(每个文件一个函数)并重建。测量前后的代码大小。此外,调试和发布链接之间可能存在差异。调试链接可能是懒惰的,只是将所有内容都扔进去,而发布链接则投入更多精力来删除未使用的代码。
只是我的想法和经验,您的里程可能会有所不同(YMMV)。
In my experience, many compilers will not include unused code on an object file basis. Some may not have this resolution and will include entire libraries ("because this makes the build process faster").
For example, given a file
junk.c
and it has three functions:Func1
,Func2
andFunc3
. The build process creates an object file,junk.o
, which has all three functions in it. If functionFunc2
is not used, it will be included anyway because the linker can't exclude one function out of an object file.On the other hand, given files:
Func1.c
,Func2.c
, andFunc3.c
, with the functions above, one per file. IfFunc2
inFunc2.c
is not used, the linker will not include it.Some linkers are intelligent enough to exclude files out of libraries. However, each linker is different on its granularity of file inclusion (and thus file exclusion). Read your linker's manual or contact their customer support for exact information.
I suggest moving the suspect functions into a separate file (one function per file) and rebuild. Measure the code size before and after. Also, there may be a difference between Debug and Release linking. The Debug linking could be lazy and just throw everything in while the Release linking puts more effort into removing unused code.
Just my thoughts and experience, Your Mileage May Vary (YMMV).
传统上,链接器链接在命令行中明确指定的所有目标文件,即使它们可以被省略并且程序不会有任何未解析的符号。这意味着您可以通过包含一个目标文件来故意更改程序的行为,该目标文件执行由静态初始化触发的操作,但不会从
main
直接或间接调用。通常,如果您将大部分目标文件放在静态库中,并将该库与包含入口点的单个目标文件链接,则链接器只会(迭代地)挑选出有助于解析原始对象中未解析的符号引用的库成员文件或随后包含的文件,因为它解决了先前未解决的符号。
简而言之,将大部分对象文件放入库中,然后将其与包含入口点的一个对象链接。
Traditionally linkers link in all object files that are explicity specified in the command line, even if they could be left out and the program would not have any unresolved symbols. This means that you can deliberately change the behaviour of a program by including an object file that does something triggered from static initialization but is not called directly or indirectly from
main
.Typically if you place most of your object files in a static library and link this library with a single object file containing your entry point the linker will only pick out members of the library (iteratively) that help resolve an unresolved symbol reference in the original object file or one included subsequently because it resolved a previous unresolved symbol.
In short, place most of your object files in a library and just link this with one object containing your entry point.