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

时间:2013-04-13 03:41:04

标签: c assembly x86 x86-64 low-level

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

在纸面上,它似乎很简单。装配代码进来,机器代码出来。

但是一旦我思考所有的细节,它就会突然变得非常艰巨。操作系统需要哪些约定?如何对齐数据并计算跳跃?可执行文件的内部甚至看起来像什么?

我感到迷茫。我找不到任何关于这方面的教程,看看流行汇编程序的源代码并不鼓舞人心(我愿意再试一次)。

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

3 个答案:

答案 0 :(得分:12)

我自己编写了一些(汇编和反汇编程序),我不会从x86开始。如果你知道x86或任何其他指令集,你可以在短时间内(傍晚/下午)学习另一个指令集的语法,至少狮子会分享它。编写汇编程序(或反汇编程序)的行为肯定会教你一个快速的指令集,并且你会知道该指令集比那些没有检查过该级别微代码的指令集的许多经验丰富的汇编程序员更好。 msp430,pdp11和thumb(不是thumb2扩展)(或mips或openrisc)都是很好的起点,不是很多指令,也不是过于复杂,等等。

我首先推荐一个反汇编程序,并使用一个固定长度的指令集,如arm或thumb或mips或openrisc等。如果没有,那么至少使用一个反汇编程序(肯定选择一个已经有汇编程序的指令集,链接器和反汇编程序)并且用铅笔和纸理解机器代码和程序集之间的关系,特别是分支,它们通常有一个或多个怪癖,比如程序计数器是一个指令,或者当添加偏移量时提前两个指令,到他们有时会在整个指令中测量而不是字节。

使用C程序强制解析文本以阅读说明非常容易。一个更艰巨的任务,但也许是教育,将使用bison / flex并学习编程语言,以允许这些工具创建(一个更极端的暴力)解析器,然后接口到您的代码,告诉你在哪里找到了什么。 / p>

汇编程序本身非常简单,只需读取ascii并设置机器代码中的位。分支和其他pc相关指令有点痛苦,因为它们可以通过源/表进行多次传递来完全解决。

  mov r0,r1
  mov r2 ,#1

汇编程序开始解析一行的文本(定义为回车符0xD或换行符0xA后面的字节),丢弃空格(空格和制表符),直到找到非空白区域,然后strncmp用已知的助记符。如果你点击一个然后解析该指令的可能组合,在上面简单的情况下,在mov跳过白色空格到非白色空间之后,你可能发现的第一件事就是寄存器,然后是可选的空格,然后是逗号。删除空格和逗号,并将其与字符串表进行比较,或者只是解析它。完成该注册后,再过去发现逗号的位置,然后说它是另一个寄存器或立即注册。如果立刻让我们说它必须有一个#符号,如果寄存器让我们说它必须从一个小写或大写开始' r'。在解析该寄存器或立即解析之后,请确保该行上没有其他内容不应该在该行上。为这个指令构建机器代码或者至少尽可能多地构建机器代码,然后继续下一行。这可能很乏味但是解析ascii并不困难......

至少你需要一个表/数组,它在创建时累积机器代码/数据,加上一些标记指令不完整的方法,以及将来通过时完成的pc相关指令。您还需要一个表/数组来收集找到的标签以及找到的机器代码表中的地址/偏移量。除了指令中用作目标/源的标签以及表/数组中的偏移量,它们还包含部分完整的指令。在第一次传递之后,然后返回这些表,直到您将所有标签定义与用作源或目标的标签匹配,使用标签定义地址/偏移量来计算与相关指令的距离,然后完成创建该指令的机器代码。 (可能需要进行一些反汇编和/或使用其他一些方法来记住以后为了完成构建机器代码而返回它时的编码类型。)

下一步是允许多个源文件,如果这是您想要允许的内容。现在你必须拥有汇编程序无法解析的标签,所以你必须在输出中留下占位符并制作最长的跳转/分支指令,因为你不知道目的地有多远,期望更糟。然后有你选择创建/使用的输出文件格式,那么链接器大部分都很简单,但是你必须记住为最终的pc相关指令填写机器代码,没有比汇编程序更难的了。本身。

注意,编写汇编程序不一定与创建编程语言,然后为它编写编译器,单独的东西,不同的问题有关。实际上,如果您想要创建一种新的编程语言,只需使用现有的汇编程序来处理现有的指令集。当然不需要,但大多数教学和教程都将使用bison / flex方法编写语言,并且有很多大学课程讲义/资源用于开始编译器类,您可以使用它们来启动然后修改用于添加语言功能的脚本。中端和后端比前端更具挑战性。有很多关于这个主题的书籍以及许多在线资源。正如在另一个答案中所提到的,llvm并不是一个创建新编程语言的好地方,中间和后端都是为你完成的,你只需要专注于编程语言本身,即前端。

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:4)

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