c - 了解 map 文件,优化大小

标签 c gcc compilation makefile linker

最近我意识到我的可执行文件的大小非常大。我正在使用 Eclipse 和 GCC 为 Cortex-M 微 Controller 开发软件。

为了验证这一点,我使用了在互联网上找到的一个示例项目,该项目只是通过直接操作寄存器来使 LED 闪烁,并且基于 makefile。

我使用我的库、启动代码、链接器脚本等创建了一个非常相似的项目,该项目使用 Eclipse 的托管 makefile。

第一个项目编译成功,并生成了app.txt的二进制文件。 6kB。第二个项目产生了app.48kB的二进制文件!对于本质上相同的结果来说,这显然是一个相当大的差异,而且后者绝对是一个巨大的文件,只是让 LED 闪烁。在这两种情况下,优化都已关闭。

在我自己的库中,有一些 volatile 缓冲区,这可能是大型 BSS 或数据部分的借口,因此我决定首先关注文本部分(仍然是 5 倍大,从 5kB 到 27kB) .

我查看了映射文件,以了解与二进制文件真正链接的内容。相同或相似的功能也具有相似的大小。

有一件事对我来说似乎非常非常奇怪。有些函数在整个项目中仅定义一次,但似乎已链接多次,每次都来自不同的目标文件,并且每次都占用文本部分中的空间。查看函数 .text.port_lock 的示例。

这正常吗?如何减少最终文件大小,以及如何告诉工具链每个函数仅链接一次?

The map file

编辑: 正如评论中所述,这两个程序没有不同,它是同一件事,只是做了一些小的修改(例如启动代码和访问 GPIO 寄存器的函数)。我不是在测试GCC优化代码的能力,因此我使用了-O0。我尝试理解 map 文件,以及为什么我多次看到某些功能。

最佳答案

您误读了 map 文件。没有出现 .text.port_lock, 例如,表示 ChibiOS 函数 void port_lock(void) 的定义。

所有出现的 .text.port_lock 均引用输入链接器部分。

前 4 次出现,位于 map 文件标题为 丢弃的输入节,指的是链接器输入的链接器节 被丢弃。例如:

.text.port_lock
                0x00000000       0x1c /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chmempools.o)

表示链接器找到了大小为 28 字节的 .text.port_lock 部分 在输入文件中 /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chmempools.o) 然后把它扔掉了。

接下来的 6 次出现,位于 map 文件标题为 链接器脚本和内存映射都引用了输入链接器部分 映射到输出 .text 部分。例如第一个:

.text.port_lock
            0x000012a8       0x1c /tmp/ccaossic.ltrans0.ltrans.o

表示链接器找到了大小为 28 字节的 .text.port_lock 部分 在输入文件 /tmp/ccaossic.ltrans0.ltrans.o 并将其映射到输出 .text 部分中的地址 0x000012a8。同样的 第二次出现:

.text.port_lock
                0x00001f70       0x1c /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chsys.o)

表示在输入中也找到了具有相同名称和大小的输入节 文件/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)

在所有 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

这是来自 /home/fotis/Documents/Resources/Chibios/Chibios/Debug/libChibios.a(chcoreasm_v7m.o) 的输入 .text 部分。

映射文件不包含 port_lock 函数被多次链接的指示。它不包含 表明该函数已链接。如果它被链接多次那么就有 将是一个多重定义链接错误(除非它有 被注释为 weak symbol )。

为什么这六个不包含符号的 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);
}

然后在我的 map 文件中,您将看到一个名为 .text.foo 的空输入部分。这 不会告诉您有关我链接的符号的任何信息。

How can I tell the toolchain to only link once each function?

链接器不会多次链接任何符号定义,特殊情况除外 弱符号的情况。您的映射文件不包含任何函数被多次链接的证据。

How can I reduce the final file size?

当然,使用 -Os 编译您的版本。并尽量减少链接冗余, 请参阅this question .

读取链接器映射文件通常是研究符号和部分的笨拙方法 在你的二进制文件中。喜欢objdump , readelfnm

关于c - 了解 map 文件,优化大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36667991/

相关文章:

java - 编译为 1.5 编写的 Java 代码以使用 1.4 JRE?

C 项目结构、代码共享和具有依赖关系的代码编译

c - 如何告诉 GCC 发出一个由我命名的特定调试符号?

c - c程序中的隐藏错误(数值计算)

assembly - 二进制文件未在 debian wheezy 上运行

c# - 自动部署,Global.asax : Could not load type Namespace. Global

c - 为什么我的 "=r"(var) 输出没有选择与 "a"(var) 输入相同的寄存器?

c - 使用 struct 关键字的函数定义不合适

c - (gvim 8271) IBUS 警告

c - 如何将c程序编译为dll