如何编写X86_64 _assembler_?

时间:2010-06-19 01:43:23

标签: x86-64 assembly

目标:我想编写一个X86_64汇编程序。注意:标记为社区维基

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

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

  • 加载到注册表
  • 对寄存器进行算术运算(只需整数就可以了,不需要乱用FPU)
  • 条件
  • 跳到

只是一个基本的设置,使图灵完成。有人做过吗?建议/资源?

2 个答案:

答案 0 :(得分:8)

汇编程序与任何其他“编译器”一样,最好写成一个词法分析器,用于语言语法处理器。

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

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

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

最简单的方法是确保令牌易于区分。

这就是为什么我制定了标签必须以:开头的规则 - 它使得对线条的分析变得更加容易。处理生产线的过程是:

  • 剥离评论(字符串外的第一个;到行尾)。
  • 提取标签(如果有)。
  • 然后是第一个字。
  • 休息是操作数。

您可以轻松地坚持不同的操作数也有特殊的标记,让您的生活更轻松。所有这一切都假设您可以控制输入格式。如果您需要使用英特尔或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)。

答案 1 :(得分:6)

不要劝阻你,但已经many assemblers有各种各样的花里胡哨。请考虑为elftoolchain等现有的开源项目做出贡献。