来自瑞典Linköping University的Filip Strömbäck老师来我们组访问,进行的一个名为Bootstrapping a Programming Language from Nothing的Seminar。本文的内容为基于此材料的个人理解。
设想你在一个无人岛上求生,身边只有一个x86电脑,上面除了Linux操作系统、文本编辑器之外什么都没有,作为没有好用的编程语言就会似的PL nerd,是时候捏着鼻子写机器码了……那我们开始吧!
Week 1: Hex to Bin
罗马不是一天建成的,要想手撸GCC这样的编译器的机器码肯定也是不现实的。和任何生存游戏一样,第一步肯定是徒手撸树,做简单的工具改善效率,然后再用简单的工具去做更复杂的工具……
手写机器码是个从各种角度看都很反人类的事情。我们的第一个目标是做一个可以减轻一些写机器码的痛苦的工具:
- 可以写十六进制文本代码,翻译成二进制文件
- 支持自动转换成小端序减轻心智负担
- 可以写空格、可以写注释
它看起来的效果是这样的:
1 | ;main: |
开干
即使没有汇编器,也不妨碍我们手写汇编代码,然后手动汇编。鉴于我们之后要将它手动翻译、链接,我们在写的时候最好能尽可能的代码少一些,而且跳转尤其是大跳转尽可能少一些(越远你要手动数的字节数越多!)。
事后我发现我并没有做到这一点……
代码的逻辑有这么几个重点:
用标准输入输出,系统调用的使用要用
eax, ebx, ecx, edx,变量用别的寄存器逐个字符的读入,空格分隔,这意味着我们需要一个变量累积现在读到的数,记在
esi在逐字节输出的时候,我们要知道这个数是几个字节的,记在
edi可能有连续的空格,如果没有读到数,就不输出继续就行
找到
;开始的注释,就一直消耗输入直到换行
其中有些小技巧,因为这个工具是自己临时用的,为了让实现越简单越好,我们可以把不用的部分都当成UB:例如在判断字符的时候,直接按照
<= 0x20为空格< 0x40为数字,其他的就按a-f A-F处理. 接下来只要自己用的时候小心一点别写超出specification的代码就好!
然后耐心一点写汇编代码就好:
1 | ;; esi : current number |
流程:正攻vs.邪道
首先,我想确定我写的代码对不对,然后我想要一个可执行的程序,这样任务就完成了。然而身处无人岛的你,会发现首先没有汇编器,也没有链接器,也没有debugger!绝望之中,你只能硬着头皮查手册把汇编一条条翻译成二进制,然后扳手指一个个字节的数跳转指令的地址,填进去。最后,把这些数字一个个敲进HEX编辑器得到一个可执行文件,然后祈祷它是正确的,否则整个过程还要从头来过!
在讲义中,Filip Strömbäck老师大发慈悲地允许了使用gdb。不过这也是你得到可执行文件之后的事情了,不过既然在无人岛,你也有的是时间。这就是生存模式吗?!
不过所幸我现在并没有在无人岛,而我也并不“有的是时间”,所以在不违背大原则的情况下,我允许自己用一些邪道来节约时间精力了()总的来说,如果只是节省纯体力劳动的时间,还是可以接受的(希望如此)。
首先我还是用的现成的汇编器得到了二进制,目的是用gdb调试,验证代码逻辑对不对
1 | nasm -f elf32 stage1.asm |
调得差不多了,接下来要手工编码了,这一步并不是很难,不过现在跳转的地址还没确定,先用XXXXXXXX占位,避免后面忘了填。
最后是链接部分,最大的困难是搞对地址,不过可以做一个按一下就+1的计数器让数数的过程轻松一点……最后填上ELF头,就得到了二进制的源码stage1-linked.hex了。
于是,我们最后还需要把我们得到的代码一个个的手敲进HEX编辑器,得到我们的二进制文件。所幸这样的体力劳动我们可以开挂:我们刚刚写完、debug完的程序刚好可以“借来”一用:
1 | ./a.out < stage1-linked.hex > out |
我们假装这个out就是我们手敲出来的二进制吧!当然,它也可能因为我们的汇编代码写错了而不工作,这也相当于帮我们验证汇编部分的逻辑是否正确。
这个时候其实就快搞定了,毕竟我们有gdb可以反汇编看我们翻译出来的机器码是不是按照预期工作,然后再多检查一下就好。不过既然都这样了,直接用objdump看,不是更方便?
1 | objdump -D -b binary -m i386 -M intel --adjust-vma=0x10000 out | less |
从头到底读一遍看看和自己写的汇编是否一致,尤其是跳转的地址。
基本上没问题了,让我们自举验证一下:
1 | ./out < stage1-linked.hex > out2 |
完全一致,看来我们完成了。
- 本文作者: Frankenstein
- 本文链接: https://salty-frankenstein.github.io/blog/2025/11/12/【笔记】从零开始手搓编程语言(一)/
-
版权声明:
本博客文章采用 CC BY-SA 4.0 协议进行许可。转载请注明作者与原文链接。