12

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

在纸面上,这似乎很简单。汇编代码进来,机器代码出来。

但是当我想到所有的细节时,它突然变得非常令人生畏。操作系统需要什么约定?如何对齐数据并计算跳跃?可执行文件的内部是什么样的?

我感觉很失落。我找不到任何有关这方面的教程,并且查看流行汇编程序的源代码并没有启发性(不过,我愿意再试一次)。

我从这里去哪里?你会怎么做?有没有关于这个主题的好的教程或文献?

4

3 回答 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 方法来编程语言,并且有许多大学课程讲义/资源可用于开始编译器类,您可以使用它们来帮助您开始然后修改添加您的语言功能的脚本。中后端是比前端更大的挑战。有很多关于这个主题的书籍和许多在线资源。

于 2013-04-13T04:50:08.163 回答
4

您要查找的不是教程或源代码,而是规范。请参阅http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx

一旦你了解了可执行文件的规范,就可以编写一个程序来生成一个。您构建的可执行文件应该尽可能简单。一旦你掌握了这一点,你可以编写一个简单的面向行的解析器,它读取指令名称和数字参数来生成一个代码块来插入到 exe 中。稍后您可以添加符号、分支、部分,无论您想要什么,这就是http://www.davidsalomon.name/assem.advertis/asl.pdf之类的内容。

PS Carl Norum 在上面的评论中有一个很好的观点。如果您的目标是创建自己的编程语言,那么学习编写汇编程序是无关紧要的,也不是正确的开始方式(除非您要创建的语言是汇编语言)。已经有汇编器从汇编器源代码生成可执行文件,因此您的编译器可以生成汇编器源代码,您可以避免重新创建汇编器的工作......而且您应该这样做。或者你可以使用 LLVM 之类的东西,它可以解决编译器构建的许多其他令人生畏的问题。您实际编写自己的编程语言的可能性非常小,但是如果您从头开始并且没有必要,它们的可能性就会小得多。确定您的目标是什么,并使用可用的最佳工具来实现它。

于 2013-04-13T04:23:00.637 回答
4

你应该看看 LLVM,llvm 是一个模块化编译器后端,最流行的前端是用于编译 C/C++/Objective-C 的 Clang。LLVM 的好处是您可以选择编译器链中您感兴趣的部分并专注于该部分,而忽略所有其他部分。您想创建自己的语言,编写生成 LLVM 内部表示代码的解析器,并且免费获得所有中间层目标独立优化并编译到许多不同的目标。对某些奇异 CPU 的编译器感兴趣,编写一个编译器后端,它采用 LLVM 中间代码并生成您的汇编。对优化技术有一些想法,也许是自动线程,编写一个处理 LLVM 中间代码的中间层。LLVM 是库的集合,而不是像 GCC 那样的独立二进制文件,

于 2013-04-13T04:45:53.400 回答