减少可执行文件大小的过程

时间:2008-10-14 07:48:56

标签: embedded arm

我正在生成一个在ARM处理器上运行的hex文件,我想保持在32K以下。它目前比这个要大得多,我想知道是否有人可能会对减少它的最佳方法有一些建议?

这是我到目前为止所做的事情

  1. 所以我在其上运行'size'来确定hex文件有多大。
  2. 然后再次'size'以查看每个目标文件有多大,该链接用于创建十六进制文件。似乎大多数规模来自外部库。
  3. 然后我使用'readelf'来查看哪些函数占用的内存最多。
  4. 我搜索了代码,看看是否可以取消对这些函数的调用。
  5. 这里是我卡住的地方,有一些我不直接调用的函数(例如_vfprintf),我找不到调用它的所以我可以删除调用(因为我认为我不需要它)。

    接下来的步骤是什么?

    回答答案:

    • 正如我所看到的,正在调用的函数会占用大量内存。然而,我无法找到所谓的内容。
    • 我想省略那些功能(如果可能的话),但我找不到叫它们的东西!可以从我想的任意数量的库函数调用。
    • 链接器正在按需运行,我认为它只包含相关的库文件。您如何知道是否仅包含相关功能?你可以为此设置一面旗帜吗?
    • 我正在使用GCC

8 个答案:

答案 0 :(得分:13)

一般清单:

  • 确保已禁用编译器和链接器调试选项
  • 编译并链接启用的所有尺寸选项(gcc中的-Os)
  • 在可执行文件
  • 上运行strip
  • 生成地图文件并检查您的功能大小。您可以让链接器生成映射文件(使用ld时为-M),或者可以在最终可执行文件上使用objdump(请注意,这只适用于未提取的可执行文件!)这实际上不会修复问题,但它会让你知道最严重的罪犯。
  • 使用nm调查从每个目标文件调用的符号。这应该有助于找到你不想调用的函数。

原始问题是一个仅包含相关功能的子问题。 gcc将包含所使用的每个目标文件中的所有函数。换句话说,如果你有一个包含10个函数的目标文件,即使实际调用了1个函数,所有10个函数都包含在你的可执行文件中。

标准库(例如libc)将函数拆分为许多单独的目标文件,然后存档。然后将可执行文件链接到存档。 通过拆分成许多目标文件,链接器只能包含实际调用的函数。 (这假设您是静态链接)

没有理由不能做同样的伎俩。当然,您可以争辩说,如果没有调用函数,您可以自己删除它们。

如果你是静态地链接到其他库,你可以运行上面列出的工具,以确保他们遵循类似的规则。

答案 1 :(得分:5)

可能为您节省工作的另一个优化是-ffunction-sections,-Wl,-gc-sections,假设您正在使用GCC。不过,一个好的工具链不需要被告知。

说明:GNU ld链接部分,GCC每个翻译单元发出一个部分,除非你另有说明。但是在C ++中,依赖图中的节点是对象和函数。

答案 2 :(得分:2)

只需仔细检查并记录以供将来参考,但您是否使用Thumb说明?它们是普通指令的16位版本。有时您可能需要2个16位指令,因此它不会在代码空间中节省50%。

一个体面的链接器应该只需要所需的功能。但是,您可能需要编译器& linke设置包装函数用于单独链接。

答案 3 :(得分:2)

在深度嵌入的项目中,我总是尽量避免使用任何标准库函数。甚至像“strtol()”这样的简单函数也会破坏二进制大小。如果可能的话,只需避免这些电话。

在大多数深度嵌入式项目中,您不需要通用的“printf()”或动态内存分配(许多控制器具有32kb或更少的RAM)。

我使用非常简单的自定义“printf()”而不是仅使用“printf()”,此函数只能打印十六进制或十进制格式的数字而不是更多。大多数数据结构在编译时预先分配。

答案 4 :(得分:1)

好的,最后我把项目简化为最简单的形式,然后逐个慢慢添加文件,直到我想删除的功能出现在'readelf'文件中。然后,当我收到文件时,我对所有内容进行了评论,然后慢慢添加内容,直到该函数再次弹出。所以最后我发现了什么叫它并删除了所有这些调用......现在它可以按照需要运行......甜蜜!

虽然必须是更好的方法。

答案 5 :(得分:1)

Andrew EdgeCombe有一个很棒的列表,但是如果你真的想要刮掉每一个字节,sstrip是一个很好的工具,可以从列表中删除,并且可以减少几个KB。

例如,在strip本身it can shave off ~2kB上运行时。

从旧的README(参见this间接源文件顶部的注释):

  

sstrip是一个小工具,用于删除内容结尾处的内容   不属于程序内存映像的ELF文件。

     

大多数ELF可执行文件都是使用程序头表和   节头表。但是,按顺序只需要前者   用于加载,链接和执行程序的操作系统。 sstrip试图   提取ELF头,程序头表及其内容,   将所有其他内容留在比特桶中。它只能删除部分内容   要保存的部件之后发生的文件。然而,   这几乎总是包括节头表,偶尔也包括   运行程序时未使用的一些随机部分。

请注意,由于它删除了一些信息,因此带有一些工具的sstrip可执行文件为rumoured to have issues。这将在来源的评论中进行更多讨论。

另外......有关如何制作尽可能小的可执行文件的有趣/疯狂阅读,this article值得一读。

答案 6 :(得分:0)

回答这个特定需求:

  

•我想省略这些功能(如果可能的话),但我找不到什么?   叫他们!!可以从任意数量的库函数调用我   猜测。

如果你想分析你的代码库以查看谁调用了什么,调用给定函数的人以及类似的东西,那么有一个很好的工具叫做#34;理解C"由SciTools提供。

https://scitools.com/

我过去经常使用它来执行静态代码分析。它确实有助于确定库依赖树。它允许轻松浏览调用树以及其他内容。

他们提供限时评估,然后您必须购买许可证。

答案 7 :(得分:-1)

您可以查看类似executable compression的内容。