x86
Version:
Generates x86_64 native code from assembly instructions
287 lines (286 loc) • 9.58 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var o = require('./operand');
// # x86_64 Instruction
//
// Each CPU instruction is encoded in the following form, where only
// *Op-code* byte is required:
//
// |-------------------------------------------------|--------------------------------------------|
// | Instruction | Next instruction |
// |-------------------------------------------------|--------------------------------------------|
// |byte 1 |byte 2 |byte 3 |byte 4 |byte 5 |
// |---------|---------|---------|---------|---------| ...
// |REX |Op-code |Mod-R/M |SIB |Immediat | ...
// |---------|---------|---------|---------|---------| ...
// |optional |required |optional |optional |optional |
// |-------------------------------------------------|
var InstructionPart = (function () {
function InstructionPart() {
}
return InstructionPart;
}());
exports.InstructionPart = InstructionPart;
var Prefix = (function (_super) {
__extends(Prefix, _super);
function Prefix() {
_super.apply(this, arguments);
}
return Prefix;
}(InstructionPart));
exports.Prefix = Prefix;
// ## REX
//
// REX is an optional prefix used for two reasons:
//
// 1. For 64-bit instructions that require this prefix to be used.
// 2. When using extended registers: r8, r9, r10, etc..; r8d, r9d, r10d, etc...
//
// REX byte layout:
//
// 76543210
// .1..WRXB
// .......B <--- R/M field in Mod-R/M byte, or BASE field in SIB byte addresses one of the extended registers.
// ......X <---- INDEX field in SIB byte addresses one of the extended registers.
// .....R <----- REG field in Mod-R/M byte addresses one of the extended registers.
// ....W <------ Used instruction needs REX prefix.
// .1 <--------- 0x40 identifies the REX prefix.
var PrefixRex = (function (_super) {
__extends(PrefixRex, _super);
function PrefixRex(W, R, X, B) {
_super.call(this);
this.W = W;
this.R = R;
this.X = X;
this.B = B;
}
PrefixRex.prototype.write = function (arr) {
if (this.W || this.R || this.X || this.B)
arr.push(64 | (this.W << 3) | (this.R << 2) | (this.X << 1) | this.B);
return arr;
};
return PrefixRex;
}(Prefix));
exports.PrefixRex = PrefixRex;
// ## LOCK
//
// Prefix for performing atomic memory operations.
var PrefixLock = (function (_super) {
__extends(PrefixLock, _super);
function PrefixLock() {
_super.apply(this, arguments);
this.value = 0xF0;
}
PrefixLock.prototype.write = function (arr) {
arr.push(this.value);
return arr;
};
return PrefixLock;
}(Prefix));
exports.PrefixLock = PrefixLock;
// ## Op-code
//
// Primary op-code of the instruction. Often the lower 2 or 3 bits of the
// instruction op-code may be set independently.
//
// `d` and `s` bits, specify: d - direction of the instruction, and s - size of the instruction.
// - **s**
// - 1 -- word size
// - 0 -- byte size
// - **d**
// - 1 -- register is destination
// - 0 -- register is source
//
// 76543210
// ......ds
//
// Lower 3 bits may also be used to encode register for some instructions. We set
// `.regInOp = true` if that is the case.
//
// 76543210
// .....000 = RAX
var Opcode = (function (_super) {
__extends(Opcode, _super);
function Opcode() {
_super.apply(this, arguments);
// Main op-code value.
this.op = 0;
// Whether lower 3 bits of op-code should hold register address.
this.regInOp = false;
// Whether register is destination of this instruction, on false register is
// the source, basically this specifies the `d` bit in op-code.
this.regIsDest = true;
// `s` bit encoding in op-code, which tells whether instruction operates on "words" or "bytes".
this.isSizeWord = true;
}
Opcode.prototype.write = function (arr) {
// Op-code can be up to 3 bytes long.
var op = this.op;
if (op > 0xFFFF)
arr.push((op & 0xFF0000) >> 16);
if (op > 0xFF)
arr.push((op & 0xFF00) >> 8);
arr.push(op & 0xFF);
return arr;
};
/* Now we support up to 3 byte instructions */
Opcode.MASK_SIZE = 16777214; // `s` bit
Opcode.MASK_DIRECTION = 16777213; // `d` bit
Opcode.MASK_OP = 16777208; // When register is encoded into op-code.
Opcode.SIZE = {
BYTE: 0,
WORD: 1,
};
Opcode.DIRECTION = {
REG_IS_SRC: 0,
REG_IS_DST: 2,
};
return Opcode;
}(InstructionPart));
exports.Opcode = Opcode;
// ## Mod-R/M
//
// Mod-R/M is an optional byte after the op-code that specifies the direction
// of operation or extends the op-code.
//
// 76543210
// .....XXX <--- R/M field: Register or Memory
// ..XXX <------ REG field: Register or op-code extension
// XX <--------- MOD field: mode of operation
var Modrm = (function (_super) {
__extends(Modrm, _super);
function Modrm(mod, reg, rm) {
_super.call(this);
this.mod = 0;
this.reg = 0;
this.rm = 0;
this.mod = mod;
this.reg = reg;
this.rm = rm;
}
Modrm.getMod = function (mem) {
if (!mem.displacement)
return Modrm.MOD.INDIRECT;
else if (mem.displacement.size === o.DisplacementValue.SIZE.DISP8)
return Modrm.MOD.DISP8;
else
return Modrm.MOD.DISP32;
};
Modrm.getRm = function (mem) {
return mem.base ? mem.base.get3bitId() : Modrm.RM_NEEDS_SIB;
};
Modrm.prototype.write = function (arr) {
if (arr === void 0) { arr = []; }
arr.push((this.mod << 6) | (this.reg << 3) | this.rm);
return arr;
};
// Two bits of `MOD` field in `Mod-R/M` byte.
Modrm.MOD = {
INDIRECT: 0,
DISP8: 1,
DISP32: 2,
REG_TO_REG: 3,
};
// When this value is encoded in R/M field, SIB byte has to follow Mod-R/M byte.
Modrm.RM_NEEDS_SIB = 4;
return Modrm;
}(InstructionPart));
exports.Modrm = Modrm;
// ## SIB
//
// SIB (scale-index-base) is optional byte used when dereferencing memory
// with complex offset, like when you do:
//
// mov rax, [rbp + rdx * 8]
//
// The above operation in SIB byte is encoded as follows:
//
// rbp + rdx * 8 = BASE + INDEX * USERSCALE
//
// Where `USERSCALE` can only be 1, 2, 4 or 8; and is encoded as follows:
//
// USERSCALE (decimal) | SCALE (binary)
// ------------------- | --------------
// 1 | 00
// 2 | 01
// 4 | 10
// 8 | 11
//
// The layout of SIB byte:
//
// 76543210
// .....XXX <--- BASE field: base register address
// ..XXX <------ INDEX field: address of register used as scale
// XX <--------- SCALE field: specifies multiple of INDEX: USERSCALE * INDEX
var Sib = (function (_super) {
__extends(Sib, _super);
function Sib(userscale, I, B) {
_super.call(this);
this.S = 0;
this.I = 0;
this.B = 0;
this.setScale(userscale);
this.I = I;
this.B = B;
}
Sib.prototype.setScale = function (userscale) {
switch (userscale) {
case 1:
this.S = 0;
break;
case 2:
this.S = 1;
break;
case 4:
this.S = 2;
break;
case 8:
this.S = 3;
break;
default: throw TypeError("User scale must be on of [1, 2, 4, 8], given: " + userscale + ".");
}
};
Sib.prototype.write = function (arr) {
if (arr === void 0) { arr = []; }
arr.push((this.S << 6) | (this.I << 3) | this.B);
return arr;
};
return Sib;
}(InstructionPart));
exports.Sib = Sib;
// ## Displacement
var Displacement = (function (_super) {
__extends(Displacement, _super);
function Displacement(value) {
_super.call(this);
this.value = value;
}
Displacement.prototype.write = function (arr) {
if (arr === void 0) { arr = []; }
this.value.octets.forEach(function (octet) { arr.push(octet); });
return arr;
};
return Displacement;
}(InstructionPart));
exports.Displacement = Displacement;
// ## Immediate
//
// Immediate constant value that follows other instruction bytes.
var Immediate = (function (_super) {
__extends(Immediate, _super);
function Immediate(value) {
_super.call(this);
this.value = value;
}
Immediate.prototype.write = function (arr) {
if (arr === void 0) { arr = []; }
this.value.octets.forEach(function (octet) { arr.push(octet); });
return arr;
};
return Immediate;
}(InstructionPart));
exports.Immediate = Immediate;