目标:我想编写一个X86_64汇编程序。注意:标记为社区维基
背景:我熟悉C.我以前写过MIPS汇编。我写了一些x86程序集。但是,我想编写一个x86_64汇编程序 - 它应该输出我可以跳转到并开始执行的机器代码(就像在JIT中一样)。
问题是:解决这个问题的最佳方法是什么?我意识到这个问题看起来有点大。我想从一个基本的最小集开始:
只是一个基本的设置,使图灵完成。有人做过吗?建议/资源?
答案 0 :(得分:8)
汇编程序与任何其他“编译器”一样,最好写成一个词法分析器,用于语言语法处理器。
汇编语言通常比常规编译语言更容易,因为您不需要担心跨越行边界的构造,并且格式通常是固定的。
两年前,我为一个(虚构的)CPU编写了一个汇编程序用于教育目的,它基本上将每一行视为:
:loop
)。mov
)。ax,$1
)。最简单的方法是确保令牌易于区分。
这就是为什么我制定了标签必须以:
开头的规则 - 它使得对线条的分析变得更加容易。处理生产线的过程是:
;
到行尾)。您可以轻松地坚持不同的操作数也有特殊的标记,让您的生活更轻松。所有这一切都假设您可以控制输入格式。如果您需要使用英特尔或AT& T格式,那就更难了。
我接近它的方式是有一个简单的每个操作函数被调用(例如,doJmp
,doCall
,doRet
)并且该函数决定了什么是允许的在操作数中。
例如,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等现有的开源项目做出贡献。