x86
Version:
Generates x86_64 native code from assembly instructions
289 lines (236 loc) • 10.8 kB
text/typescript
import * as o from './operand';
import * as p from './parts';
import {Definition} from './def';
import {OP} from './opcode';
// Collection of operands an instruction might have. It might
// have *destination* and *source* operands and a possible *immediate* constant.
//
// Each x86 instruction can have up to up to 5 operands: 3 registers, displacement and immediate.
// 3 registers means: 1 register, and 2 registers that specify the base and index for memory
// dereferencing, however all operands necessary for memory dereferencing are held in `o.Memory`
// class so we need only these three operands.
export class Operands {
dst: o.Register|o.Memory = null; // Destination
src: o.Register|o.Memory = null; // Source
imm: o.Constant = null; // Immediate
constructor(dst: o.Register|o.Memory = null, src: o.Register|o.Memory = null, imm: o.Constant = null) {
this.dst = dst;
this.src = src;
this.imm = imm;
}
}
export interface InstructionUserInterface {
/* Adds `LOCK` prefix to instructoins, throws `TypeError` on error. */
lock(): this;
}
// ## x86_64 `Instruction`
//
// `Instruction` object is created using instruction `Definition` and `Operands` provided by the user,
// out of those `Instruction` generates `InstructionPart`s, which then can be packaged into machine
// code using `.write()` method.
export class Instruction implements InstructionUserInterface{
def: Definition = null;
op: Operands = null;
// Instruction parts.
prefixLock: p.PrefixLock = null;
opcode: p.Opcode = new p.Opcode; // required
modrm: p.Modrm = null;
sib: p.Sib = null;
displacement: p.Displacement = null;
immediate: p.Immediate = null;
// Direction for register-to-register `MOV` operations, whether REG field of Mod-R/M byte is destination.
protected regToRegDirectionRegIsDst: boolean = true;
// Index where instruction was inserted in `Code`s buffer.
index: number = 0;
// Byte offset of the instruction in compiled machine code.
offset: number = 0;
// constructor(code: Code, def: Definition, op: Operands) {
constructor(def: Definition, op: Operands) {
this.def = def;
this.op = op;
var dstreg: o.Register = null;
var dstmem: o.Memory = null;
var srcreg: o.Register = null;
var srcmem: o.Memory = null;
// Destination
if(op.dst instanceof o.Register) dstreg = op.dst as o.Register;
else if(op.dst instanceof o.Memory) dstmem = op.dst as o.Memory;
else if(op.dst) throw TypeError(`Destination operand should be Register or Memory; given: ${op.dst.toString()}`);
// Source
if(op.src) {
if (op.src instanceof o.Register) srcreg = op.src as o.Register;
else if (op.src instanceof o.Memory) srcmem = op.src as o.Memory;
else if (!(op.src instanceof o.Constant))
throw TypeError(`Source operand should be Register, Memory or Constant`);
}
this.create(dstreg, dstmem, srcreg, srcmem);
}
getMemoryOperand(): o.Memory {
if(this.op.dst instanceof o.Memory) return this.op.dst;
if(this.op.src instanceof o.Memory) return this.op.src;
return null;
}
protected writePrefixes(arr: number[]) {
if(this.prefixLock) this.prefixLock.write(arr);
// for(var pfx of this.prefixes) pfx.write(arr);
}
write(arr: number[]): number[] {
this.writePrefixes(arr);
this.opcode.write(arr);
if(this.modrm) this.modrm.write(arr);
if(this.sib) this.sib.write(arr);
if(this.displacement) this.displacement.write(arr);
if(this.immediate) this.immediate.write(arr);
return arr;
}
lock(): this {
this.prefixLock = new p.PrefixLock;
// TODO: check LOCK is allowed for this instruction.
return this;
}
// http://wiki.osdev.org/X86-64_Instruction_Encoding#Operand-size_and_address-size_override_prefix
getOperandSize() {
}
getAddressSize() {
}
protected create(dstreg: o.Register, dstmem: o.Memory, srcreg: o.Register, srcmem: o.Memory) {
// Create instruction parts.
this.createPrefixes();
this.createOpcode(dstreg, srcreg);
this.createModrm();
this.createSib(dstmem, srcmem);
this.createDisplacement(dstmem, srcmem);
this.createImmediate();
}
protected hasExtendedRegister(): boolean {
var {dst, src} = this.op;
if(dst && dst.reg() && (dst.reg() as o.Register).isExtended()) return true;
if(src && src.reg() && (src.reg() as o.Register).isExtended()) return true;
return false;
}
protected hasRegisterOfSize(size: o.SIZE): boolean {
var {dst, src} = this.op;
if(dst && dst.reg() && ((dst.reg() as o.Register).size === size)) return true;
if(src && src.reg() && ((src.reg() as o.Register).size === size)) return true;
return false;
}
protected createPrefixes() {
}
protected createOpcode(dstreg: o.Register, srcreg: o.Register) {
var def = this.def;
var opcode = this.opcode;
opcode.op = def.op;
if(def.regInOp) {
// We have register encoded in op-code here.
if(!dstreg) throw TypeError(`Operation needs destination register.`);
opcode.op = (opcode.op & p.Opcode.MASK_OP) | dstreg.get3bitId();
} else {
// Direction bit `d`
var direction = p.Opcode.DIRECTION.REG_IS_SRC;
if(dstreg) {
direction = p.Opcode.DIRECTION.REG_IS_DST;
// *reg-to-reg* `MOV` operation
if(srcreg && (opcode.op == OP.MOV)) {
if(this.regToRegDirectionRegIsDst) direction = p.Opcode.DIRECTION.REG_IS_DST;
else direction = p.Opcode.DIRECTION.REG_IS_SRC;
}
}
opcode.op = (opcode.op & p.Opcode.MASK_DIRECTION) | direction;
// Size bit `s`
opcode.op = (opcode.op & p.Opcode.MASK_SIZE) | (p.Opcode.SIZE.WORD);
}
opcode.regIsDest = def.regIsDest;
opcode.isSizeWord = def.isSizeWord;
opcode.regInOp = def.regInOp;
}
protected getModrmMod(mem: o.Memory) {
if(!mem.displacement) return p.Modrm.MOD.INDIRECT;
else if(mem.displacement.size === o.DisplacementValue.SIZE.DISP8) return p.Modrm.MOD.DISP8;
else return p.Modrm.MOD.DISP32;
}
protected createModrm() {
// TODO: 2.2.1.6 RIP-Relative Addressing
var ops: Operands = this.op;
var has_opreg = (this.def.opreg > -1);
var dst_in_modrm = !this.def.regInOp && ops.dst;
if(has_opreg || ops.src || dst_in_modrm) {
var mod = 0, reg = 0, rm = 0;
// opreg reg
// opreg mem
if(has_opreg) {
reg = this.def.opreg;
if(ops.dst.isRegister()) { // opreg reg
mod = p.Modrm.MOD.REG_TO_REG;
rm = (ops.dst as o.Register).get3bitId();
} else if(ops.dst.isMemory()) { // opreg mem
var mem = ops.dst as o.Memory;
mod = p.Modrm.getMod(mem);
rm = p.Modrm.getRm(mem);
} else {
throw TypeError('Destination must be Register or Memory.');
}
}
else if(!ops.dst) throw TypeError('No destination operand.');
// reg
// mem
// reg reg
// reg mem
// mem reg
else if(ops.dst.isRegister()) {
var dst = ops.dst as o.Register;
reg = dst.get3bitId();
if(!ops.src) { // reg
mod = p.Modrm.MOD.REG_TO_REG;
} else if(ops.src) {
if(ops.src.isRegister()) { // reg reg
mod = p.Modrm.MOD.REG_TO_REG;
rm = (ops.src as o.Register).get3bitId();
} else if(ops.src.isMemory()) { // reg mem
var mem = this.op.src as o.Memory;
mod = p.Modrm.getMod(mem);
rm = p.Modrm.getRm(mem);
} else
throw TypeError('Source must be Register or Memory.');
}
} else if(ops.dst.isMemory()) {
var mem = ops.dst as o.Memory;
mod = p.Modrm.getMod(mem);
rm = p.Modrm.getRm(mem);
if(!ops.src) { // mem
reg = 0; // TODO: ?!?!
} else if(ops.src.isRegister()) { // mem reg
reg = (ops.src as o.Register).get3bitId();
} else if(ops.src.isMemory()) // mem mem
throw TypeError('Cannot do Memory to Memory operation.');
else {
// TODO: other operand = o.Constant
throw TypeError('Not supported yet.');
}
}
this.modrm = new p.Modrm(mod, reg, rm);
}
}
protected createSib(dstmem: o.Memory, srcmem: o.Memory) {
if(!this.modrm || (this.modrm.rm != p.Modrm.RM_NEEDS_SIB)) return;
var mem: o.Memory = srcmem || dstmem;
if(!mem) return; // Could be that we have Mod-R/M byte because of `opreg`, but no SIB needed.
var userscale = 0, I = 0, B = 0;
if(mem.scale) userscale = mem.scale.value; // TODO: what about 0?
if(mem.index) I = mem.index.get3bitId();
if(mem.base) B = mem.base.get3bitId();
this.sib = new p.Sib(userscale, I, B);
}
protected createDisplacement(dstmem: o.Memory, srcmem: o.Memory) {
var mem: o.Memory = dstmem || srcmem;
if(mem && mem.displacement) {
this.displacement = new p.Displacement(mem.displacement);
}
}
protected createImmediate() {
if(this.op.imm) {
if (this.displacement && (this.displacement.value.size === o.SIZE.QUAD))
throw TypeError(`Cannot have Immediate with ${o.SIZE.QUAD} bit Displacement.`);
this.immediate = new p.Immediate(this.op.imm);
}
}
}