【问题标题】:Size of a library and the executable库的大小和可执行文件
【发布时间】:2008-12-16 05:51:45
【问题描述】:

我有一个在 Windows 上使用 MSVC 创建的静态库 *.lib。库的大小是 70KB。然后我有一个链接这个库的应用程序。但现在最终可执行文件 (*.exe) 的大小为 29KB,小于库。我想知道的是:

  1. 由于库是静态链接的,我在想它应该直接添加到可执行文件的大小,最终的 exe 大小应该比这更多吗? windows exe格式是否也会对二进制数据进行一些压缩?

  2. linux 系统的情况如何,即 linux 上的库大小(*.a/*.la 文件)与 linux 可执行文件(*.out)的大小有什么关系?

-AD

【问题讨论】:

    标签: static-libraries static-linking


    【解决方案1】:

    Windows 和 Unix 上的静态库都是 .obj/.o 文件的集合。链接器查看这些目标文件中的每一个并确定程序是否需要链接。如果不需要,那么目标文件将不会包含在最终的可执行文件中。这可能导致可执行文件比库小。

    编辑:正如 MSalters 所指出的,在 Windows 上,VC++ 编译器现在支持生成启用函数级链接的目标文件,例如,请参阅here。事实上,edit-and-continue 需要这样做,因为edit-and-continue 需要能够替换可执行文件的最小可能部分。

    【讨论】:

    • 文件级链接确实是一个 UNIX 问题;对于 MSVC,规范已成为功能级链接。
    【解决方案2】:

    .lib 文件中有额外的簿记信息,最终可执行文件不需要这些信息。此信息有助于链接器找到实际链接的代码。此外,调试信息可能存储在.lib 文件中,但不存储在.exe 文件中(我不记得在 lib 文件中为 objs 存储调试信息的位置,它可能在其他地方)。

    【讨论】:

      【解决方案3】:

      静态库可能包含几个从未使用过的函数。当链接器将库与主可执行文件链接时,它发现某些函数从未使用过(并且它们的地址从未被获取并存储在函数指针中),它只是丢弃了代码。它也可以递归地执行此操作:如果从未调用函数 A(),并且 A() 调用 B(),但从未调用 B(),则它可以删除 A() 和 B() 的代码。在 Linux 上,同样的事情也会发生。

      【讨论】:

      • 实际上不仅仅是引用的函数,而是它们加上同一个 *.o 文件中的任何符号(即,整个对象被扔到最终输出中,而不仅仅是函数),所以如果 A 和 B与 C 在同一个 .o 中,并且 C 被调用,那么它们都会在其中。
      • 不一定,只有愚蠢的编译器/链接器。
      • 我可以假设 GCC 工具链中的 GNU 链接器是更智能的链接器之一吗? (您确实说过“在 Linux 上”,这强烈暗示了 GCC 工具链。)
      【解决方案4】:

      静态库必须包含在其源代码中定义的每个符号,因为它可能链接到只需要该特定符号的可执行文件中。但是一旦它被链接到一个可执行文件中,我们就确切地知道哪些符号最终被使用,哪些没有。因此,链接器可以轻松删除未使用的代码,从而大大减少文件大小。同样,任何重复的符号(在静态库和它链接到的可执行文件中定义的任何东西都会合并到一个实例中。

      【讨论】:

        【解决方案5】:

        免责声明:我已经很久没有处理静态链接了,所以我的回答持保留态度。

        您写道:我在想它应该直接添加到可执行文件的大小,最终的 exe 大小应该不止于此?

        朴素的链接器正是以这种方式工作的——当我为 CP/M 系统进行业余开发时(很久以前),这是一个真正的问题。

        不过,现代链接器更智能 - 它们只链接原始代码引用的函数,或根据需要链接。

        【讨论】:

        • 实际上不仅仅是引用的函数,而是它们加上同一个 *.o 文件中的任何符号(即 .o 文件是链接的粒度)。
        • 你说得对——我在此期间忘记了一个细节。这几乎就像软件考古学!
        【解决方案6】:

        除了当前的答案之外,如果函数定义具有相同的目标代码,则允许链接器删除它们 - 这是为了帮助减少模板代码的膨胀效应。

        【讨论】:

        • 我认为这不太对:我猜你是在考虑 C++ 吗?重复项的删除应该基于函数的签名(例如,名称和参数的类型)而不是目标代码。如果您有两个代码不同但签名相同的函数,会发生什么...???
        • 不,实际上我正在考虑目标代码。如果生成的目标代码与另一个目标代码相同,则链接器可以删除一个函数。您可以在调试器中看到这一点,例如,您可以看到优化代码的堆栈跟踪,这些代码具有与您预期不同的功能
        【解决方案7】:

        @All:感谢您的指点。 @Greg Hewgill - 你的回答是一个很好的指针。谢谢。

        我发现的答案如下:

        1.) 在库构建过程中,如果 MSVC(或类似的东西)中的“保持程序调试数据库”选项为 ON,那么库将具有此调试信息膨胀其大小。 但是当我静态包含该库并创建可执行文件时,链接器会在生成 exe 之前从库中删除所有调试信息,因此 exe 的大小小于库的大小。

        2.) 当我禁用“保留程序调试数据库”选项时,我得到了一个大小小于最终可执行文件的库,这在大多数情况下我认为是正常的。

        -AD

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-01
          • 2010-10-01
          • 2018-05-25
          • 1970-01-01
          • 1970-01-01
          • 2010-09-17
          相关资源
          最近更新 更多