我想在C语言中创建一个简单的汇编器。我应该从哪里开始?

13

我最近一直在尝试沉浸在汇编语言程序设计世界中,最终目标是创建自己的编程语言。我想让我的第一个真正的项目是用C语言编写的简单汇编器,它将能够组装x86机器语言的非常小的部分并创建Windows可执行文件,没有宏和连接器,只有汇编语言。

纸上来看,似乎足够简单。汇编代码输入,机器代码输出。

但是一想到所有细节,它突然变得非常令人望而生畏。操作系统需要哪些约定?如何对齐数据和计算跳转?可执行文件内部长什么样子?

我感到迷失了。我找不到任何关于这个的教程,查看流行汇编器的源代码也没有启发我(虽然我愿意再试一次)。

那么从这里应该怎么做呢?你会怎么做?这个主题有什么好的教程或文献吗?


1
还有一些需要考虑的事情:有限状态自动机用于检查用户是否使用了适当的指令,您还需要一个解析器来确保程序员编写的内容是正确的。尽管您需要担心很多系统方面的问题,但您也需要了解很多计算理论知识。 - Kiith Nabaal
1
也许你应该学习一下像NASM这样的软件包。 - Brett Hale
1
请参见https://dev59.com/R3E95IYBdhLWcg3wDpmg,了解如何制作汇编器。 - Martin Beckett
2
汇编代码输入,机器码输出。这并不意味着“在纸上”,就像原材料进去,汽车出来一样。但请忽略有关计算理论的无聊言论...您不必检查程序是否停止,甚至启动。 - Jim Balter
2
如果你不知道如何开始,也不能使用谷歌,那么你可能还不应该制作汇编器。 - user529758
显示剩余2条评论
3个回答

16
我写过一些汇编器和反汇编器,我不建议从x86开始学习。如果您已经了解x86或其他指令集,您可以在短时间内(一个晚上/下午)掌握并学习另一个指令集的语法,至少其中大部分语法。编写汇编器(或反汇编器)的行为将确实快速教授您一个指令集,并且相比于那些没有在该级别上检查微码的经验丰富的汇编程序员而言,您将更好地了解该指令集。msp430、pdp11和thumb(不是thumb2扩展)(或mips或openrisc)都是一个很好的起点,它们都没有太多的指令,也没有过度复杂等。
我建议先使用反汇编器,并选择一个固定长度指令集,例如arm、thumb、mips或openrisc等。如果不是这样,那么至少要使用反汇编器(一定要选择您已经拥有汇编器、链接器和反汇编器的指令集),并使用铅笔和纸来理解机器代码和汇编之间的关系,特别是分支,它们通常有一个或多个怪癖,比如偏移量加上时程序计数器会提前一两个指令,以获取另一个位,它们有时测量整个指令而不是字节。
用C程序暴力解析文本非常容易。更困难的任务可能是使用bison/flex来学习该编程语言,以允许这些工具创建(甚至更极端的)解析器,然后与您的代码接口,告诉您在哪里找到了什么。
汇编器本身相当直接,只需读取ASCII并设置机器码中的位即可。分支和其他pc相关指令可能会更痛苦,因为它们可能需要多次通过源/表进行完全解析。
  mov r0,r1
  mov r2 ,#1
汇编器开始解析文本行(定义为紧跟回车符0xD或换行符0xA后的字节),丢弃空格(空格和制表符),直到遇到非空格字符,然后使用已知助记符进行strncmp比较。如果匹配成功,则解析该指令的可能组合,在上面简单的示例中,在mov之后跳过空格到非空格,也许你所找到的第一件事必须是一个寄存器,然后是可选的空格,然后是逗号。移除空格和逗号并将其与字符串表进行比较,或者只需解析它即可。完成该寄存器后,继续查找逗号所在位置,假设找到的是另一个寄存器或立即数。如果是立即数,假设它必须有一个“#”符号;如果是寄存器,假设它必须以小写或大写字母“r”开头。解析该寄存器或立即数后,然后确保该行上没有不应出现在该行上的其他内容。为此指令构建机器代码或尽可能多地构建,然后继续下一行。解析ASCII可能会很繁琐,但并不困难...

最少你需要一个表/数组来累积创建的机器代码/数据,另外还需要一些方法来标记指令为不完整,以便在未来的通过中完成相对PC指令。同时还需要一个表/数组来收集找到的标签及其在发现时在机器代码表中的地址/偏移量。以及作为目的地或源使用的指令中使用的标签和它们所在的部分完成的指令所持有的表/数组中的偏移量。第一遍后,再次遍历这些表,直到将所有标签定义与用作源或目的地的标签匹配起来,使用标签定义地址/偏移量计算到所讨论的指令的距离,然后完成创建该指令的机器代码。(可能需要一些反汇编和/或使用某些其他方法来记住当你稍后返回它时什么类型的编码)。

下一步是允许多个源文件,如果您想要实现这个功能的话。现在必须有不会被汇编器解决的标签,因此必须在输出中留下占位符并创建某种最长跳转/分支指令,因为您不知道目标距离有多远,准备最坏情况。然后是选择创建/使用的输出文件格式,然后是链接程序,这大部分都很简单,但是您必须记住填写最终pc相对指令的机器代码,这与在汇编器本身中所需的相同。

请注意,编写汇编器并不一定涉及创建编程语言,然后为其编写编译器,这是两个不同的问题,存在不同的问题。实际上,如果您要制作一种新的编程语言,只需使用现有的基于现有指令集的汇编器即可。当然,这不是必需的,但是大多数教学和教程将使用bison / flex方法用于编程语言,并且有许多大学课程讲义/资源可用于入门编译器课程,您可以使用它们来开始,然后修改脚本以添加您的语言特性。中间和后端比前端更具挑战性。有很多关于这


4
您所需要的并不是教程或源代码,而是一个规范。请参考http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx
一旦您理解了可执行文件的规范,就编写程序来生成一个这样的文件。生成的可执行文件应尽可能简单。掌握了这个后,您就可以编写一个简单的基于行的解析器,读取指令名称和数值参数,生成一段代码块插入exe文件中。稍后你可以添加符号、分支、区段等任何你想要的东西,这就是http://www.davidsalomon.name/assem.advertis/asl.pdf这样的东西派上用场的地方。

附言:卡尔·诺伦在上面的评论中有一个很好的观点。如果你的目标是创建自己的编程语言,学习编写汇编程序是无关紧要的,也不是正确的开始方式(除非你想创建的语言是汇编语言)。已经有了可以从汇编源代码生成可执行文件的汇编器,因此你的编译器可以生成汇编源代码,并且可以避免重新创建汇编器的工作...而且你应该这样做。或者你可以使用像LLVM这样的工具,它将解决编译器构建的许多其他令人生畏的问题。你实际上很少会真正产生自己的编程语言,但如果你从头开始,几率就更小了,也没有必要。确定你的目标,并使用最好的可用工具来实现它。


4
你应该关注LLVM,LLVM是一个模块化编译器后端,最流行的前端是Clang,用于编译C/C++/Objective-C。LLVM的好处在于,你可以选择你感兴趣的编译器链的部分,并专注于此,忽略其他所有部分。如果你想创建自己的语言,请编写一个解析器,生成LLVM内部表示代码,免费获得中间层目标无关优化和编译到许多不同目标的功能。如果你有一些关于优化技术的想法,例如自动线程,可以编写一个处理LLVM中间代码的中间层。LLVM是一组库,而不是像GCC那样的独立二进制文件,因此非常容易在你自己的项目中使用。

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