如何用二进制写出“hello world”?

23
假设我想编写一个以二进制形式显示“hello world”的程序,我该如何做?
我有一些想法:
- 我需要确定我正在使用哪种芯片架构 - 我需要了解它使用的二进制类型 - 我需要一些关于这种二进制类型的参考资料 - 我可能需要更改编辑器(Vim)中的某些设置。
有人可以帮我详细说明吗?

4
你需要区分CPU将会接收到的指令(可以在处理器参考手册中查找)和操作系统为了识别文件为可执行文件并实际加载这些指令所需的“废话”。 - Benjamin Kloster
2
简单:用C编写并将其编译为二进制文件。 - Doc Brown
2
@Vartec - 我在问题中没有看到最小二进制的要求。 - Chad
我看到有人将这个问题投票为“离题”。StackOverflow会是一个更好的地方吗? - Nathan Long
3
@DocBrown - 我知道编写二进制代码的正常、理智方式需要使用编译器。我也没有意图用二进制编写任何重要的程序。但是我认为通过困难的方法来输出“hello world”会很有趣,我提出这个问题只是为了学习。 - Nathan Long
显示剩余5条评论
2个回答

29
这有点复杂,因为将“Hello, world!”打印到标准输出实际上是一个系统调用,因此您需要知道正确的内核系统调用号。当然,这会因操作系统而异。另外,您需要知道二进制格式,这也往往会有所不同,尽管ELF(可执行和可链接格式)在几种Unix和Linux版本中是通用的。
请参见汇编中的Hello, world!
这是Linux汇编代码:
section .text
    global _start           ;must be declared for linker (ld)

_start:                 ;tell linker entry point

    mov edx,len ;message length
    mov ecx,msg ;message to write
    mov ebx,1   ;file descriptor (stdout)
    mov eax,4   ;system call number (sys_write)
    int 0x80    ;call kernel

    mov eax,1   ;system call number (sys_exit)
    int 0x80    ;call kernel

section .data

msg db  'Hello, world!',0xa ;our dear string
len equ $ - msg         ;length of our dear string

在32位Linux上编译后,生成的二进制文件只有360字节,尽管其中大部分都是零:

00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  80 80 04 08 34 00 00 00  |............4...|
00000020  c8 00 00 00 00 00 00 00  34 00 20 00 02 00 28 00  |........4. ...(.|
00000030  04 00 03 00 01 00 00 00  00 00 00 00 00 80 04 08  |................|
00000040  00 80 04 08 9d 00 00 00  9d 00 00 00 05 00 00 00  |................|
00000050  00 10 00 00 01 00 00 00  a0 00 00 00 a0 90 04 08  |................|
00000060  a0 90 04 08 0e 00 00 00  0e 00 00 00 06 00 00 00  |................|
00000070  00 10 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  ba 0e 00 00 00 b9 a0 90  04 08 bb 01 00 00 00 b8  |................|
00000090  04 00 00 00 cd 80 b8 01  00 00 00 cd 80 00 00 00  |................|
000000a0  48 65 6c 6c 6f 2c 20 77  6f 72 6c 64 21 0a 00 2e  |Hello, world!...|
000000b0  73 68 73 74 72 74 61 62  00 2e 74 65 78 74 00 2e  |shstrtab..text..|
000000c0  64 61 74 61 00 00 00 00  00 00 00 00 00 00 00 00  |data............|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000f0  0b 00 00 00 01 00 00 00  06 00 00 00 80 80 04 08  |................|
00000100  80 00 00 00 1d 00 00 00  00 00 00 00 00 00 00 00  |................|
00000110  10 00 00 00 00 00 00 00  11 00 00 00 01 00 00 00  |................|
00000120  03 00 00 00 a0 90 04 08  a0 00 00 00 0e 00 00 00  |................|
00000130  00 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000140  01 00 00 00 03 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  ae 00 00 00 17 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  01 00 00 00 00 00 00 00                           |........|

由于您想要“手工编译”,这基本上意味着将汇编助记符翻译成其操作码,然后将结果包装在正确的二进制格式中(如上面的ELF示例)

更新:正如@adam-rosenfield所展示的这个答案, “Hello, world!” 的 ELF 二进制文件可以手工制作到116个字节。原始答案现已删除,但对于管理员仍可见,因此这里有一份副本:

Here's a 32-byte version using Linux system calls:

 .globl _start
_start:
        movb $4, %al
        xor %ebx, %ebx
        inc %ebx
        movl $hello, %ecx
        xor %edx, %edx
        movb $11, %dl
        int $0x80               ;;; sys_write(1, $hello, 11)
        xor %eax, %eax
        inc %eax
        int $0x80               ;;; sys_exit(something) hello:
        .ascii "Hello world" 

When compiled into a minimal ELF file, the full executable is 116 bytes:

00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............| 
00000010  02 00 03 00 01 00 00 00  54 80 04 08 34 00 00 00  |........T...4...| 
00000020  00 00 00 00 00 00 00 00  34 00 20 00 01 00 00 00  |........4. .....| 
00000030  00 00 00 00 01 00 00 00  00 00 00 00 00 80 04 08  |................|
00000040  00 80 04 08 74 00 00 00  74 00 00 00 05 00 00 00  |....t...t.......|
00000050  00 10 00 00 b0 04 31 db  43 b9 69 80 04 08 31 d2  |......1.C.i...1.|
00000060  b2 0b cd 80 31 c0 40 cd  80 48 65 6c 6c 6f 20 77  |....1.@..Hello w|
00000070  6f 72 6c 64                                       |orld| 
00000074 

你给了我一个可行的例子,但我真正想知道的是“我该如何为我的机器找出解决方案?”我习惯于使用非常高级的编程语言,所以对此有点不知所措。 - Nathan Long
@NathanLong:嗯,我有点觉得你已经知道了...;-) - vartec
链接 https://stackoverflow.com/a/285093/60711 是一个未找到的页面,请问您能否更新一下这篇帖子?我对那个116字节的ELF二进制文件非常感兴趣。 - user2188550
顺便说一句,你链接的答案(来自adam-rosenfield)已经不存在了。 - ljleb
为什么二进制代码中会有大于1的字母和数字?例如第一行的“7f 45 4c 46 01 01 01 00”?难道不应该全部都是0和1吗? - amaatouq
显示剩余2条评论

3
通常,您需要使用十六进制编辑器来完成此操作。找出汇编代码,手动汇编它,使用十六进制编辑器输入二进制值,然后将它们保存到文件中。一旦您拥有了文件,就可以进入您的机器监视器,并在可用地址处加载该文件,然后跳转到第一条指令。这在单板计算机上是非常普遍的做法,今天在微控制器上仍然存在,但不是您在当代操作系统上要做的事情。如果您真的想这样做,我建议运行低级模拟器(SIMH 可以工作),或者使用微控制器(您可以购买一款 TI MSP430 开发套件,成本不到五美元)。

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