17

我正在生成一个十六进制文件以在我希望保持在 32K 以下的 ARM 处理器上运行。它目前比这大得多,我想知道是否有人可能对瘦身的最佳方法有一些建议?

这是我到目前为止所做的

  1. 所以我在它上面运行了'size'来确定十六进制文件有多大。
  2. 然后再次“大小”以查看每个目标文件有多大,该链接用于创建十六进制文件。似乎大部分大小来自外部库。
  3. 然后我使用'readelf'来查看哪些函数占用的内存最多。
  4. 我搜索了代码,看看是否可以消除对这些函数的调用。

这就是我卡住的地方,有些函数我不直接调用(例如_vfprintf),我找不到调用它的函数,所以我可以删除调用(因为我认为我不需要它)。

那么接下来的步骤是什么?

对答案的回应:

  • 正如我所看到的,调用了一些占用大量内存的函数。但是我找不到它的名字。
  • 我想省略那些函数(如果可能的话),但我找不到调用它们的东西!我猜可以从任意数量的库函数中调用。
  • 链接器按需要工作,我认为,它只包含相关的库文件。你怎么知道是否只包含相关功能?你可以为此设置一个标志或其他东西吗?
  • 我正在使用 GCC
4

8 回答 8

17

一般清单:

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

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

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

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

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

于 2008-10-14T10:04:58.977 回答
5

另一个可能节省您工作量的优化是 -ffunction-sections、-Wl、--gc-sections,假设您使用的是 GCC。但是,不需要告诉一个好的工具链。

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

于 2008-10-14T11:24:58.863 回答
3

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

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

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

于 2008-12-09T20:19:17.310 回答
2

只是为了仔细检查和记录以供将来参考,但是您使用拇指指令吗?它们是普通指令的 16 位版本。有时您可能需要 2 条 16 位指令,因此不会节省 50% 的代码空间。

一个体面的链接器应该只具有所需的功能。但是,您可能需要编译器和链接设置来为单个链接打包函数。

于 2008-10-14T08:15:58.013 回答
1

好的,最后我只是将项目简化为最简单的形式,然后慢慢地一个一个地添加文件,直到我要删除的函数出现在“readelf”文件中。然后,当我得到文件时,我将所有内容都注释掉并慢慢添加内容,直到该功能再次弹出。所以最后我发现了它的名字并删除了所有这些电话......现在它可以按预期工作......甜!

必须是一个更好的方法来做到这一点。

于 2008-10-14T09:37:25.413 回答
1

Andrew EdgeCombe 有一个很棒的列表,但是如果你真的想抓取最后一个字节,sstrip是一个很好的工具,它是列表中缺少的,并且可以减少更多 kB。

例如,当它自己运行时strip它可以减少 ~2kB

来自旧的 README(请参阅此间接源文件顶部的注释):

strip 是一个小型实用程序,可删除 ELF 文件末尾不属于程序内存映像的内容。

大多数 ELF 可执行文件都使用程序头表和节头表构建。然而,为了让操作系统加载、链接和执行程序,只需要前者。strip 尝试提取 ELF 标头、程序标头表及其内容,将其他所有内容留在位桶中。它只能删除文件末尾出现的部分,在要保存的部分之后。然而,这几乎总是包括节头表,偶尔还有一些运行程序时不使用的随机节。

请注意,由于它删除了一些信息,有传言说 sstrip'd 可执行文件与某些工具存在问题。这在来源的评论中有更多讨论。

另外...对于如何制作尽可能小的可执行文件的有趣/疯狂的阅读,这篇文章值得一读。

于 2012-04-26T19:37:16.507 回答
0

为了满足这一特定需求:

•我想省略那些函数(如果可能的话),但我找不到调用它们的东西!!我猜可以从任意数量的库函数中调用。

如果您想分析您的代码库以查看谁调用了什么、由谁调用给定函数等等,SciTools 提供了一个名为“Understand C”的出色工具。

https://scitools.com/

我过去经常使用它来执行静态代码分析。它确实可以帮助确定库依赖树。它允许轻松地上下浏览调用树等。

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

于 2015-07-24T13:21:54.193 回答
-1

你可以看看可执行压缩之类的东西。

于 2008-10-14T08:02:39.553 回答