6

目标:我想写一个 X86_64 汇编器。注意:标记为社区 wiki

背景:我熟悉C。我以前写过MIPS汇编。我写了一些 x86 程序集。但是,我想编写一个 x86_64 汇编器——它应该输出我可以跳转到并开始执行的机器代码(就像在 JIT 中一样)。

问题是:解决这个问题的最佳方法是什么?我意识到这个问题看起来有点大。我想从一个基本的最小集开始:

  • 加载到寄存器
  • 寄存器上的算术运算(只需要整数就可以了,还不需要弄乱 FPU)
  • 条件句
  • 跳跃

只是一个基本的集合,使其图灵完备。有人做过吗?建议/资源?

4

2 回答 2

8

像任何其他“编译器”一样,汇编器最好写成词法分析器,输入到语言语法处理器中。

汇编语言通常比常规编译语言更容易,因为您无需担心构造跨越行边界并且格式通常是固定的。

大约两年前,出于教育目的,我为(虚构的)CPU 编写了一个汇编程序,它基本上将每一行视为:

  • 可选标签(例如,:loop)。
  • 操作(例如,mov)。
  • 操作数(例如,ax,$1)。

最简单的方法是确保标记易于区分。

这就是为什么我制定了标签必须从头开始的规则:——它使对线条的分析变得如此容易。处理一条线的过程是:

  • 去掉注释(首先;在字符串之外到行尾)。
  • 提取标签(如果存在)。
  • 第一个词是操作。
  • 其余是操作数。

您可以轻松地坚持不同的操作数也具有特殊标记,以使您的生活更轻松。所有这一切都假设您可以控制输入格式。如果您需要使用 Intel 或 AT&T 格式,那就有点困难了。

我处理它的方法是调用一个简单的按操作函数(例如,、、、doJmpdoCalldoRet该函数决定操作数中允许的内容。

例如,doCall只允许数字或标签,doRet不允许任何内容。

例如,这是该encInstr函数的代码段:

private static MultiRet encInstr(
    boolean ignoreVars,
    String opcode,
    String operands)
{
    if (opcode.length() == 0) return hlprNone(ignoreVars);
    if (opcode.equals("defb"))  return hlprByte(ignoreVars,operands);
    if (opcode.equals("defbr")) return hlprByteR(ignoreVars,operands);
    if (opcode.equals("defs"))  return hlprString(ignoreVars,operands);
    if (opcode.equals("defw"))  return hlprWord(ignoreVars,operands);
    if (opcode.equals("defwr")) return hlprWordR(ignoreVars,operands);
    if (opcode.equals("equ"))   return hlprNone(ignoreVars);
    if (opcode.equals("org"))   return hlprNone(ignoreVars);

    if (opcode.equals("adc"))   return hlprTwoReg(ignoreVars,0x0a,operands);
    if (opcode.equals("add"))   return hlprTwoReg(ignoreVars,0x09,operands);
    if (opcode.equals("and"))   return hlprTwoReg(ignoreVars,0x0d,operands);

这些hlpr...函数只是获取操作数并返回一个包含指令的字节数组。当许多操作具有相似的操作数要求时,它们很有用,例如 adc ,add和and` 在上述情况下都需要两个寄存器操作数(第二个参数控制为指令返回的操作码)。

通过使操作数的类型易于区分,您可以检查提供了哪些操作数、它们是否合法以及要生成哪些字节序列。将操作分离到它们自己的功能中提供了一个很好的逻辑结构。

此外,大多数 CPU 遵循从操作码到操作的合理逻辑转换(以使芯片设计人员的工作更轻松),因此所有操作码上都会有非常相似的计算,例如,允许索引寻址。

为了在允许可变长度指令的 CPU 中正确创建代码,最好分两遍进行。

在第一遍中,不生成代码,只生成指令的长度。这允许您在遇到所有标签时为其分配值。第二遍将生成代码并可以填写对这些标签的引用,因为它们的值是已知的。上面的ignoreVars代码段中的 用于此目的(返回代码的字节序列,因此我们可以知道长度,但对符号的任何引用仅使用 0)。

于 2010-06-19T01:52:54.610 回答
6

不是要劝阻你,而是已经有很多组装机,各种花里胡哨。请考虑为现有的开源项目做出贡献,例如elftoolchain

于 2010-06-19T02:49:42.417 回答