【问题标题】:How do I cut out assembler executable bloat?如何消除汇编器可执行文件膨胀?
【发布时间】:2012-09-20 20:42:10
【问题描述】:

我在 GasNASMYASM 中有工作的多平台 Hello World 代码,我想将它们对应的可执行文件从 76KB 缩小到对于 Hello World 汇编程序更合理的东西,看到因为一个基本的 Hello World C 程序导致一个 80KB 的可执行文件,并且程序集应该小得多。我相信大部分可执行文件都充满了来自链接器选项的垃圾。

追踪:

LIBS=c:/strawberry/c/i686-w64-mingw32/lib/crt2.o -Lc:/strawberry/c/i686-w64-mingw32/lib -lmingw32 -lmingwex -lmsvcrt

ld ld -o $(EXECUTABLE) hello.o $(LIBS)

hello.exe
Hello World!

代码:

.data

msg: .ascii "Hello World!\0"

.text

.global _main

_main:

pushl $msg
call _puts

leave
movl $0, %eax
ret

如果我删除 LIBS 中的任何选项,链接过程会失败,或者生成的可执行文件在运行时会引发 Windows 错误。所以合乎逻辑的做法是将puts 调用替换为更简单的调用,例如 sys_write,但我不知道如何实现这个多平台。网上的小文档说要使用int 0x80 来调用内核,但这仅适用于Linux,不适用于Windows,我希望我的汇编代码是多平台的。

【问题讨论】:

  • 您可以使用条件汇编,例如。在 NASM 中,这样使用例如。 int 80h 适用于 Linux,对于 Windows,您可以使用 Windows API 调用(我不了解 Windows API)。对于创建非常小的ELF 可执行文件,A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux 可能很有趣(但在多平台汇编编程中可能没有多大帮助)。
  • C 语言中的 Hello World 程序通常占用 80K 的原因是由于 C 运行时库,而不是代码大小。如果你不在 CRT 中链接,你的 EXE 应该是 5-10K。
  • @BitBank,谢谢!你能帮我重写代码,让它不依赖于 C 运行时吗?
  • @nrz,Teensy 是正确的想法,但你是对的,这还不够。 ld ... -nostdlib 导致可执行文件的文件大小与以前完全相同。 ld ... -nostartfiles 会导致一个可执行文件在运行 hello.exe 时显示一个奇怪的错误:process_begin: CreateProcess(NULL, hello.exe, ...) failed.

标签: optimization assembly filesize


【解决方案1】:

您的程序膨胀主要来自 C 运行时库。在 Windows 中,如果您编写自己的“小”CRT,一个简单的 hello world 程序可以小于 5K。这是一个项目的链接,它解释了有关如何将 EXE 缩小到尽可能小的大小的所有细节:

http://www.codeproject.com/Articles/15156/Tiny-C-Runtime-Library

【讨论】:

    【解决方案2】:

    对于 Windows,您可以调用原生 Win32 API 函数,例如 GetStdHandle()WriteFile() 直接写入标准输出。

    对于类 Unix 系统,您可以使用文件描述符 1 调用 write() 系统调用以获取标准输出。

    具体如何执行这些操作的详细信息取决于您使用的汇编程序和操作系统。

    【讨论】:

    • 您能否举一个代码示例,该代码将与一些汇编程序进行汇编并在 Unix 和 Windows 中生成可工作的可执行文件?
    • 谢谢,但这些示例是特定于操作系统的。此外,他们甚至不使用相同的汇编程序。 Windows 使用 MASM,Linux 使用 Gas。
    • 根据定义,您不能编写“跨平台”汇编代码,至少如果您想以任何方式与操作系统本身交互,则不能。如您所见,这些示例显然非常不同。
    • HLA。 LLVM。我在文章开头列出的 NASM 和 YASM 代码是多平台的。这家伙甚至用汇编程序编写了一个在 Windows 和 Linux 上运行的 GUI 应用程序。 dreamincode.net/forums/topic/…
    【解决方案3】:

    您应该能够动态链接到 C 运行时库,而不是静态地包含它。我不知道如何在 Linux 中做到这一点,但在 Windows 中你可以使用msvcrt.dll

    【讨论】:

    • 谢谢,这会减少大小,但我更愿意在可执行文件中包含所有必要的组件,当然除了默认安装在操作系统中的任何部分,例如内置 -在 Windows DLL 中。
    • msvcrt.dll 自 Windows 2000 以来内置于 Windows 中,我认为。
    • 你能帮我重写makefile中的链接命令来动态地做到这一点吗?
    • 抱歉,我不熟悉链接器,但是如果你删除了 lib 引用会发生什么?
    • 如果我删除-L... 标志,程序仍会构建,但生成的可执行文件会在运行时从 Windows 生成“hello.exe 已停止工作”弹出窗口。删除任何 -lXYZ 标志会导致链接器错误,因为某些 C lib 依赖项或其他缺失。
    【解决方案4】:

    汇编器膨胀很可能来自 C lib 依赖项,尤其是 puts。重构代码以在不使用 C 调用的情况下打印 Hello World 很可能需要特定于操作系统的汇编代码,因为 Unix 标准涉及调用内核的中断,而 Windows 有自己的类似 VB 的 API 用于此类任务。

    我确实设法找到了一种解决方案,该解决方案可以创建小型可执行文件,同时仍保持平台无关性。通常,C 预处理器指令可以解决问题,但我不确定哪些汇编语言甚至具有预处理器语法。但是可以通过使用受控的、包含的汇编代码文件来实现类似的效果。包装代码文件的集合可以处理特定于操作系统的汇编代码,而包含的汇编文件则负责其余的工作。并且一个简单的 Makefile 可以运行相应的构建控制台命令来引用所需平台上的相应包装器代码。

    例如,我能够快速构建以这种方式工作的FASM 代码。 (虽然我还没有通知它实际上绕过puts 一些不那么臃肿的东西。)无论如何,这是进步。

    【讨论】:

      【解决方案5】:

      因为几乎所有 C 函数都使用 CDECL 调用约定,调用者调整堆栈而不是被调用者(函数)。

      如果你现在不学习如何正确地做事,你会遇到麻烦,更努力地阅读以追踪错误。

      试试这个:

          push    szLF
          push    esp
          push    fmtint2
          call    printf
          add     esp, 4 * 3
      
          push msg
          call puts 
      
          push    szLF
          push    esp
          push    fmtint2
          call    printf
          add     esp, 4 * 3
      

      运行它并注意调用 put 之前和之后的数字。他们不一样不是吗?嗯,他们应该是一样的。现在添加:

          add     esp, 4
      

      在您调用 puts 并再次运行它之后.. 现在数字是一样的吗?这意味着您有一个平衡的堆栈指针,并且该函数使用 CDECL 调用约定。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-06
        • 1970-01-01
        • 1970-01-01
        • 2014-08-05
        • 1970-01-01
        • 2013-09-18
        • 2016-09-29
        • 1970-01-01
        相关资源
        最近更新 更多