这个 Linux / 32位 x86 汇编语言的 "Hello, World" 程序能否再缩小?

8
下面这个32位的x86 Linux程序可以打印任意长度的字符串(只要程序够长)。执行完后会调用exit(0)函数:
.global _start             ; notice on entry here, all regs but %esp are zero
_start:
    call  .L0              ; offset == strlen, provided by your assembler
.byte 'H','e','l','l','o',',',' ','W','o','r','l','d'
.L0:
    pop   %ecx             ; ret addr is starting addr of string
    mov   -4(%ecx),%edx    ; argument to `call`, 4 bytes: strlen
    inc   %ebx             ; stdout == 1
    movb  $4, %al          ; SYS_write == 4
    int   $0x80
    xchg  %eax,%ebp        ; %ebp is still zero
    xchg  %eax,%ebx        ; SYS_exit == 1, return value == 0
    int   $0x80

如果愿意牺牲位置独立性(而是强制链接器插入字符串地址),并且不关心程序返回零,那么可以将其简化为:
.global _start
_start:
    movb  $4, %al
    inc   %ebx
    mov   $.L0, %ecx       ; this address is calculated when linking
    movb  $.Lend-.L0, %dl  ; strlen, calculated by assembler
    int   $0x80
    xchg  %eax,%ebx
    int   %0x80
.L0:
.byte 'H','e','l','l','o',',',' ','W','o','r','l','d'
.Lend:

这两个程序都可以通过 as --32 -o x.o x.S; ld -s -m elf_i386 x.o 组装/链接,并正常运行。第二个程序只有26字节的代码,如果在打印 Hello, World 后允许崩溃,则可以省略最后两条指令,代码长度为23字节,已经是极限了。
一直困扰着我的问题是,是否可能再挤压几个字节?我自己的猜测有以下几种可能:
  • 以某种方式使用“Hello,World”本身的部分作为代码?
  • 有人知道可用的系统调用彩蛋吗?
  • 欺骗链接器使入口点成为16位地址,以便使用 movw $.L0, %cx(节省一个字节)?
  • 进行8位偏移jmp到一个已知的位置(或通过汇编/链接器调用奇技淫巧创建)包含exit(...)系统调用所必需的指令,比xchg; int序列多节省一个字节?
否则,是否可以证明这实际上是最小的、规范的(无崩溃/返回码为零)Linux/x86 "Hello, World"?

编辑

澄清一下,问题不在于最小化ELF可执行文件的大小;那方面的技巧是众所周知的。我的问题明确地询问了一个Linux 32位x86汇编程序的大小,该程序执行了相当于以下代码的编译后代码的操作:
int main(int argc, char **argv)
{
    puts("Hello, World");
    exit(0); /* or whatever code */
}

我会很高兴如果有任何不需要手动编辑ELF头的方法。如果您找到一种方法,例如将"Hello, World"嵌入到某个ELF对象中,并从汇编源代码中引用它,仅使用汇编器/链接器命令行和/或mapfile输入,我认为这足够有效,即使这会增加ELF可执行文件的大小。我只想知道打印"Hello, World"并在之后调用exit()的指令序列是否可以缩小。


问题是关于代码大小,而不是可执行文件大小。


1
你在试图攻破哪里?;-) - Kerrek SB
@Luigi: “小于20字节的hello world”不仅限于Linux/x86汇编语言。我已经阅读了这篇文章,里面最小的Linux/i386程序大于30字节。 - FrankH.
@rlb.usa:还没有使用过SO的子网站;我承认我喜欢老牌的SO ;-) - FrankH.
顺便问一下,你尝试过使用.ascii指令来嵌入字符串吗?如果你想要一个终止的空字节,可以尝试使用.asciiz - fuz
1
我投票关闭此问题,因为它是代码高尔夫。 - Ross Ridge
显示剩余7条评论
2个回答

2
在1999年,这个已经完成了。请看这个页面(剧透:最终结果是一个45字节的ELF文件)。确保阅读附言

2
我并没有在谈论ELF文件能够达到的最小尺寸,以执行“exit(42)”- 这是关于实际_打印_“Hello, World”的指令序列的特定讨论。 - FrankH.
1
来自同一地方,62字节的Hello world http://www.muppetlabs.com/~breadbox/software/tiny/hello.asm.txt - Hasturkun

0

使用libc直接翻译C代码将生成16字节的指令:

.S:
    .asciz "Hello, World"
.globl main
main:
    push $.S
    call puts
    add $4, %esp
    xor %eax, %eax
    ret

如果你使用x86-64而不是x86-32,调用约定会将参数传递到寄存器中,这样我们就可以跳过堆栈操作。
main:
    mov $.S, %rdi
    call puts
    xor %eax, %eax
    ret

仅有15个字节的代码。


在你的32位代码中,当你使用_RET_时,你是否记得调整堆栈以考虑在puts之前推送的字节数? - Michael Petch
问题在于你没有计算所有链接的C运行时库代码。 - Ross Ridge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接