x86
Version:
Generates x86_64 native code from assembly instructions
272 lines (271 loc) • 10.9 kB
JavaScript
"use strict";
var o = require('./operand');
var p = require('./parts');
var opcode_1 = require('./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.
var Operands = (function () {
function Operands(dst, src, imm) {
if (dst === void 0) { dst = null; }
if (src === void 0) { src = null; }
if (imm === void 0) { imm = null; }
this.dst = null; // Destination
this.src = null; // Source
this.imm = null; // Immediate
this.dst = dst;
this.src = src;
this.imm = imm;
}
return Operands;
}());
exports.Operands = Operands;
// ## 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.
var Instruction = (function () {
// constructor(code: Code, def: Definition, op: Operands) {
function Instruction(def, op) {
this.def = null;
this.op = null;
// Instruction parts.
this.prefixLock = null;
this.opcode = new p.Opcode; // required
this.modrm = null;
this.sib = null;
this.displacement = null;
this.immediate = null;
// Direction for register-to-register `MOV` operations, whether REG field of Mod-R/M byte is destination.
this.regToRegDirectionRegIsDst = true;
// Index where instruction was inserted in `Code`s buffer.
this.index = 0;
// Byte offset of the instruction in compiled machine code.
this.offset = 0;
this.def = def;
this.op = op;
var dstreg = null;
var dstmem = null;
var srcreg = null;
var srcmem = null;
// Destination
if (op.dst instanceof o.Register)
dstreg = op.dst;
else if (op.dst instanceof o.Memory)
dstmem = op.dst;
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;
else if (op.src instanceof o.Memory)
srcmem = op.src;
else if (!(op.src instanceof o.Constant))
throw TypeError("Source operand should be Register, Memory or Constant");
}
this.create(dstreg, dstmem, srcreg, srcmem);
}
Instruction.prototype.getMemoryOperand = function () {
if (this.op.dst instanceof o.Memory)
return this.op.dst;
if (this.op.src instanceof o.Memory)
return this.op.src;
return null;
};
Instruction.prototype.writePrefixes = function (arr) {
if (this.prefixLock)
this.prefixLock.write(arr);
// for(var pfx of this.prefixes) pfx.write(arr);
};
Instruction.prototype.write = function (arr) {
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;
};
Instruction.prototype.lock = function () {
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
Instruction.prototype.getOperandSize = function () {
};
Instruction.prototype.getAddressSize = function () {
};
Instruction.prototype.create = function (dstreg, dstmem, srcreg, srcmem) {
// Create instruction parts.
this.createPrefixes();
this.createOpcode(dstreg, srcreg);
this.createModrm();
this.createSib(dstmem, srcmem);
this.createDisplacement(dstmem, srcmem);
this.createImmediate();
};
Instruction.prototype.hasExtendedRegister = function () {
var _a = this.op, dst = _a.dst, src = _a.src;
if (dst && dst.reg() && dst.reg().isExtended())
return true;
if (src && src.reg() && src.reg().isExtended())
return true;
return false;
};
Instruction.prototype.hasRegisterOfSize = function (size) {
var _a = this.op, dst = _a.dst, src = _a.src;
if (dst && dst.reg() && (dst.reg().size === size))
return true;
if (src && src.reg() && (src.reg().size === size))
return true;
return false;
};
Instruction.prototype.createPrefixes = function () {
};
Instruction.prototype.createOpcode = function (dstreg, srcreg) {
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 == opcode_1.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;
};
Instruction.prototype.getModrmMod = function (mem) {
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;
};
Instruction.prototype.createModrm = function () {
// TODO: 2.2.1.6 RIP-Relative Addressing
var ops = 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()) {
mod = p.Modrm.MOD.REG_TO_REG;
rm = ops.dst.get3bitId();
}
else if (ops.dst.isMemory()) {
var mem = ops.dst;
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.');
else if (ops.dst.isRegister()) {
var dst = ops.dst;
reg = dst.get3bitId();
if (!ops.src) {
mod = p.Modrm.MOD.REG_TO_REG;
}
else if (ops.src) {
if (ops.src.isRegister()) {
mod = p.Modrm.MOD.REG_TO_REG;
rm = ops.src.get3bitId();
}
else if (ops.src.isMemory()) {
var mem = this.op.src;
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;
mod = p.Modrm.getMod(mem);
rm = p.Modrm.getRm(mem);
if (!ops.src) {
reg = 0; // TODO: ?!?!
}
else if (ops.src.isRegister()) {
reg = ops.src.get3bitId();
}
else if (ops.src.isMemory())
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);
}
};
Instruction.prototype.createSib = function (dstmem, srcmem) {
if (!this.modrm || (this.modrm.rm != p.Modrm.RM_NEEDS_SIB))
return;
var mem = 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);
};
Instruction.prototype.createDisplacement = function (dstmem, srcmem) {
var mem = dstmem || srcmem;
if (mem && mem.displacement) {
this.displacement = new p.Displacement(mem.displacement);
}
};
Instruction.prototype.createImmediate = function () {
if (this.op.imm) {
if (this.displacement && (this.displacement.value.size === 64 /* QUAD */))
throw TypeError("Cannot have Immediate with " + 64 /* QUAD */ + " bit Displacement.");
this.immediate = new p.Immediate(this.op.imm);
}
};
return Instruction;
}());
exports.Instruction = Instruction;