两个月前,一位开发者正在折腾一个支持64位的实模式操作系统。加密协议、系统调用、手动内存管理——这些汇编语言的日常操作让他疲惫不堪。 endless typing nasm ..., ld ..., babysitting flags, and cleaning up object file garbage,他厌倦了这种重复劳动。

于是他决定自己动手写一个构建工具。先用 Node.js 快速原型验证,现在正在用 Go 全面重写。选择 Go 不是因为流行,而是因为系统工具需要快速、原生,而不是拖着一个 100 MB 的运行时。

打开网易新闻 查看精彩图片

这个工具的核心用法极其简单:运行 node index.js main.asm,就能得到可执行文件。工具会自动递归查找所有 .asm(或 .s、.fasm)文件,调用正确的汇编器并传入合适的参数,根据操作系统选择对应的链接器(Linux 用 ld,Windows 用 gcc,macOS 也用 ld 但参数不同),按需链接库文件,最后还能选择清理中间生成的目标文件。

与大多数只锁定单一汇编器构建工具不同,这个工具同时支持三种主流汇编器。NASM(x86 标准语法)直接运行即可;GAS(GNU 汇编器,AT&T 语法)需要加 --assembler gas 参数;FASM(flat assembler,自定义语法)则用 --assembler fasm。开发者无需切换工具,一个构建器统一管理。三种汇编器各有自己的参数怪癖——NASM 需要 -f elf64,GAS 只需要 as,FASM 则要求在源码里写 format ELF64——但工具把这些细节都隐藏了。--format 参数对 NASM 有效,对 FASM 也"几乎有效"。

跨平台链接是另一个痛点。Linux 的链接器是 ld,Windows 是 gcc(通过 MinGW),macOS 也是 ld 但默认参数不同。工具会自动检测平台:Linux 下添加 -dynamic-linker /lib64/ld-linux-x86-64.so.2;Windows 下调用 gcc,因为 ld 处理不好 PE 格式;macOS 则使用带原生默认参数的 ld。用户只需运行 ./program,不用在脑子里写 #ifdef。

调试功能也没有被忽视。没有符号表的汇编调试是痛苦的体验,所以工具提供了 --debug 参数。运行 node index.js --debug factorial.asm 后,可以用 gdb 设置断点、观察寄存器。工具会在支持的汇编器上加 -g 参数,链接器也加 -g。FASM 比较麻烦,需要用 -d DEBUG=1 做 hack,但能用。

代码结构按功能拆分为 args.js、assembler.js、linker.js、builder.js、logger.js。这不是为了好看——用户可以只拿链接器模块或参数解析器用在自己的项目里,也可以自己添加新的汇编器支持(YASM、LLVM 等)。

工具还内置了防呆设计。开发者曾经不小心把输出文件命名得和源文件一样,结果编译好的二进制覆盖了源代码。现在工具会检查:如果输出文件名和输入文件名相同,就会阻止操作。