Fot*_*los 2 c linker gcc makefile compilation
最近我意识到我的可执行文件的大小相当大。我正在使用 Eclipse 和 GCC 为 Cortex-M 微控制器开发软件。
为了验证这一点,我使用了在互联网上找到的一个示例项目,该项目只是通过直接操作寄存器来使 LED 闪烁,并且基于 makefile。
我使用我的库、启动代码、链接器脚本等创建了一个非常相似的项目,该项目使用 Eclipse 的托管 makefile。
第一个项目编译成功,并生成了app.txt的二进制文件。6kB。第二个项目产生了app.48kB的二进制文件!对于本质上相同的结果来说,这显然是一个相当大的差异,而且后者绝对是一个巨大的文件,只是让 LED 闪烁。在这两种情况下,优化都被关闭。
在我自己的库中,有一些易失性缓冲区,这可能是大型 BSS 或数据部分的借口,因此我决定首先关注文本部分(仍然是 5 倍大,从 5kB 到 27kB)。
我查看了映射文件以了解与二进制文件真正链接的内容。相同或相似的功能也具有相似的大小。
有一件事情对我来说似乎非常非常奇怪。有些函数在整个项目中仅定义一次,但似乎已链接多次,每次都来自不同的目标文件,并且每次都占用文本部分中的空间。看一下函数的例子.text.port_lock
。
这是正常的吗?如何减少最终文件大小,以及如何告诉工具链每个函数仅链接一次?
编辑: 正如评论中所述,这两个程序没有不同,它是同一件事,只是做了一些小的修改(例如启动代码和访问 GPIO 寄存器的函数)。我不是在测试GCC优化代码的能力,因此我使用了-O0。我尝试理解地图文件,以及为什么我多次看到某些功能。
您误读了地图文件。.text.port_lock
例如,所有出现的 都不代表 ChibiOS 函数的定义void port_lock(void)
。
所有出现的 都.text.port_lock
引用输入链接器部分。
前 4 次出现位于映射文件标题为 的部分中
Discarded input sections
,引用链接器丢弃的输入链接器部分。例如:
.text.port_lock
0x00000000 0x1c /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chmempools.o)
Run Code Online (Sandbox Code Playgroud)
意味着链接器.text.port_lock
在输入文件中找到了大小为 28 字节的部分/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chmempools.o)
并将其丢弃。
接下来的 6 次出现,位于映射文件标题为 all 的部分中,
Linker script and memory map
引用了映射到输出部分的输入链接器部分.text
。例如第一个:
.text.port_lock
0x000012a8 0x1c /tmp/ccaossic.ltrans0.ltrans.o
Run Code Online (Sandbox Code Playgroud)
意味着链接器.text.port_lock
在输入文件中找到了大小为 28 字节的部分/tmp/ccaossic.ltrans0.ltrans.o
,并将其映射到输出.text
部分中的地址 0x000012a8 处。同样,第二次出现:
.text.port_lock
0x00001f70 0x1c /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chsys.o)
Run Code Online (Sandbox Code Playgroud)
意味着在输入文件中也找到了相同名称和大小的输入节/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chsys.o)
,并映射到输出.text
节中的地址 0x00001f70 处。
总共有.text.port_lock
输入部分,全部为 28 字节,.text
从这些输入文件映射到输出部分:
/tmp/ccaossic.ltrans0.ltrans.o
/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chsys.o)
/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chthreads.o)
/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chcore_v7m.o)
/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chmemcore.o)
/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chschd.o)
Run Code Online (Sandbox Code Playgroud)
在所有这 6 种情况中,输入部分不包含任何符号,特别是不包含函数。作为对比,下面是包含符号的输入部分的示例:
.text 0x000002f0 0x28 /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chcoreasm_v7m.o)
0x000002f0 _port_switch
0x00000300 _port_thread_start
0x00000310 _port_switch_from_isr
0x00000314 _port_exit_from_isr
Run Code Online (Sandbox Code Playgroud)
.text
这是来自 的输入部分/home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chcoreasm_v7m.o)
。
映射文件不包含该port_lock
函数被多次链接的指示。它根本没有表明该函数已链接。如果它被链接多次,那么就会出现多重定义链接错误(除非它被注释为弱符号)。
为什么这六个不包含符号的 28 字节输入部分全部链接在一起,或者是否需要链接,这是我没有足够证据或 ChibiOS 专业知识的问题。我注意到,除了这些输入节所来自的目标文件之一之外,所有目标文件都是libChibios
. 有鉴于此,值得记住的是,如果您的链接出于任何原因需要存档成员,那么默认情况下您将链接整个存档
成员,即使它包含的内容比您需要的更多。另一方面,一些port_lock
输入部分被丢弃而一些被保留的事实表明需要保留那些被保留的部分。如果出于我自己狡猾的原因,我编写的源文件基本上如下所示:
static int __attribute__((section(".text.foo"))) __attribute__((used))
boo(int i)
{
return i * 2;
}
int bar(int i)
{
return boo(i);
}
Run Code Online (Sandbox Code Playgroud)
然后在我的地图文件中,您将看到一个名为 的空输入部分.text.foo
。这不会告诉您有关我链接的符号的任何信息。
如何告诉工具链每个函数仅链接一次?
链接器不会多次链接任何符号定义,弱符号的特殊情况除外。您的映射文件不包含任何函数被链接多次的证据。
如何减小最终文件的大小?
-Os
当然,为您的版本进行编译。为了最大限度地减少链接冗余,请参阅此问题。
读取链接器映射文件通常是研究二进制文件中的符号和部分的笨拙方法。更喜欢objdump
,readelf
和nm