Comparing version 1.0.3 to 1.0.4
22
abi.js
@@ -7,6 +7,6 @@ "use strict"; | ||
this.abi = null; | ||
this.lbl = null; | ||
this.clobbered = []; | ||
this.stackFrame = false; | ||
this.locals = 0; | ||
this.lbl = null; // Label is created for every function. | ||
this.clobbered = []; // Clobbered registers. | ||
this.stackFrame = false; // Whether to create a new stack frame. | ||
this.locals = 0; // Stack size reserved for function local variables. | ||
} | ||
@@ -18,4 +18,7 @@ Function.prototype._ = function (bodyCallback) { | ||
this.abi.code.insert(this.lbl); | ||
// Prologue | ||
this.abi.prologue(this.stackFrame, this.clobbered, this.locals); | ||
// Function body | ||
bodyCallback(); | ||
// Epilogue | ||
this.abi.epilogue(this.stackFrame, this.clobbered, this.locals); | ||
@@ -27,9 +30,16 @@ return this; | ||
exports.Function = Function; | ||
// Implements platform and architecture specific ABI conventions, for example, | ||
// calls the right syscall instruction, be it `syscall`, `sysenter`, `int 0x80` or anything else; | ||
// `push`es and `pop`s function arguments to stack according to calling conventions, etc. | ||
var Abi = (function () { | ||
function Abi(code) { | ||
this.FunctionClass = Function; | ||
// rax, rdi, rsi, rdx, r10, r8, r9 | ||
this.syscallArgs = [o.rax, o.rdi, o.rsi, o.rdx, o.r10, o.r8, o.r9]; | ||
this.notSyscallArgs = [o.rbx, o.rcx, o.r11, o.r12, o.r13, o.r14, o.r15]; | ||
// args: rdi, rsi, rdx, rcx, r8, r9 + stack | ||
this.callArgs = [o.rdi, o.rsi, o.rdx, o.rcx, o.r8, o.r9]; | ||
// scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 | ||
this.scratchRegisters = [o.rax, o.rdi, o.rsi, o.rdx, o.rcx, o.r8, o.r9, o.r10, o.r11]; | ||
// preserved: rbx, rsp, rbp, r12, r13, r14, r15 | ||
this.preservedRegisters = [o.rbx, o.rsp, o.rbp, o.r12, o.r13, o.r14, o.r15]; | ||
@@ -74,2 +84,3 @@ this.code = code; | ||
if (stackFrame || locals) { | ||
// this.code._('enter', [locals, 0]); | ||
this.code._('push', o.rbp); | ||
@@ -98,2 +109,3 @@ this.code._('mov', [o.rbp, o.rsp]); | ||
if (stackFrame || locals) { | ||
// this.code._('leave'); | ||
this.code._('mov', [o.rsp, o.rbp]); | ||
@@ -107,2 +119,3 @@ this.code._('pop', o.rbp); | ||
if (preserve === void 0) { preserve = this.scratchRegisters; } | ||
// Save registers. | ||
for (var j = 0; j < preserve.length; j++) { | ||
@@ -133,2 +146,3 @@ var reg = preserve[j]; | ||
this.code._('call', expr); | ||
// Restore registers. | ||
for (var j = preserve.length - 1; j > -1; j--) { | ||
@@ -135,0 +149,0 @@ var reg = preserve[j]; |
@@ -28,3 +28,11 @@ "use strict"; | ||
function Instruction() { | ||
// 3322222222221111111111 | ||
// 10987654321098765432109876543210 | ||
this.tpl = 0; | ||
// |||||||||||||||||||||||||||||||| | ||
// ||||||IPUBWS | ||
// |||||| L UAL | ||
// |||||| N | ||
// ||||00 | ||
// XXXX ---> Condition | ||
this.cond = COND.AL; | ||
@@ -66,2 +74,4 @@ this.A = 0; | ||
_super.apply(this, arguments); | ||
// 3322222222221111111111 | ||
// 10987654321098765432109876543210 | ||
this.tpl = 8388608; | ||
@@ -68,0 +78,0 @@ } |
36
code.js
@@ -11,4 +11,4 @@ "use strict"; | ||
this.expr = []; | ||
this.operandSize = operand_1.SIZE.D; | ||
this.addressSize = operand_1.SIZE.D; | ||
this.operandSize = operand_1.SIZE.D; // Default operand size. | ||
this.addressSize = operand_1.SIZE.D; // Default address size. | ||
this.ClassInstruction = i.Instruction; | ||
@@ -18,3 +18,3 @@ this.ClassInstructionSet = i.InstructionSet; | ||
this.AlignExpression = i.Align; | ||
this.littleEndian = true; | ||
this.littleEndian = true; // Which way to encode constants by default. | ||
this.label(start); | ||
@@ -160,3 +160,3 @@ } | ||
this.expr[index] = expr; | ||
expr.calcOffsetMaxAndOffset(); | ||
expr.calcOffsetMaxAndOffset(); // 1st pass | ||
return expr; | ||
@@ -240,2 +240,3 @@ }; | ||
var encoding = typeof b === 'string' ? b : 'ascii'; | ||
// var buf = Buffer.from(a, encoding); | ||
var buf = new Buffer(a, encoding); | ||
@@ -348,7 +349,26 @@ octets = Array.prototype.slice.call(buf, 0); | ||
}; | ||
// Expressions are compiled in 3 passes: | ||
// | ||
// - *1st pass* -- maximum offset `maxOffset` for each expression is computed, some expression might not know | ||
// their size jet, not all expressions are known, future references. First pass is when user performs insertion of commands. | ||
// - *2nd pass* -- all expressions known now, each expression should pick its right size, exact `offset` is computed for each expression. | ||
// - *3rd pass* -- now we know exact `offset` of each expression, so in this pass we fill in the addresses. | ||
Code.prototype.compile = function () { | ||
// 1st pass is performed as instructions are `insert`ed, `.offsetMax` is calculated, and possibly `.offset`. | ||
// Instructions without size can now determine their size based on `.offsetMax` and | ||
// calculate their real `.offset`. | ||
this.do2ndPass(); | ||
// Offsets are now know, here we evaluate references. | ||
return this.do3rdPass(); | ||
}; | ||
Code.prototype.do2ndPass = function () { | ||
// We probably cannot skip this 2nd pass, as instructions might change their sizes after inserted, | ||
// for example, when `.lock()` prefix is added. | ||
// var last = this.expr[this.expr.length - 1]; | ||
// var all_offsets_known = last.offset >= 0; | ||
// | ||
// Edge case when only the last Expression has variable size. | ||
// var all_sizes_known = last.bytes() >= 0; | ||
// | ||
// if(all_offsets_known && all_sizes_known) return; // Skip 2nd pass. | ||
var prev = this.expr[0]; | ||
@@ -363,2 +383,8 @@ prev.offset = 0; | ||
} | ||
// var bytes = prev.bytes(); | ||
// if(bytes === i.SIZE_UNKNOWN) | ||
// throw Error(`Instruction [${j}] does not have size.`); | ||
// ins.offset = prev.offset + bytes; | ||
// Need to call method, as `InstructionSet` contains multiple `Instruction`s, | ||
// that all need offset updated of picked instruction. | ||
ins.calcOffset(); | ||
@@ -374,3 +400,3 @@ prev = ins; | ||
ins.evaluate(); | ||
code = ins.write(code); | ||
code = ins.write(code); // 3rd pass | ||
} | ||
@@ -377,0 +403,0 @@ return code; |
25
def.js
@@ -15,2 +15,3 @@ "use strict"; | ||
this.operandSize = def.s; | ||
// Operand template. | ||
this.operands = []; | ||
@@ -24,2 +25,3 @@ if (def.ops && def.ops.length) { | ||
var flattened = operand.reduce(function (a, b) { | ||
// Determine operand size from o.Register operands | ||
var cur_size = o.SIZE.NONE; | ||
@@ -66,4 +68,7 @@ if (b instanceof o.Register) { | ||
else if (typeof tpl === 'function') { | ||
var OperandClass = tpl; | ||
var OperandClass = tpl; // as typeof o.Operand; | ||
if (OperandClass.name.indexOf('Relative') === 0) { | ||
// Here we cannot yet check any sizes even cannot check if number | ||
// fits the immediate size because we will have to rebase the o.Relative | ||
// to the currenct instruction Expression. | ||
if (o.isTnumber(operand)) | ||
@@ -84,3 +89,3 @@ return OperandClass; | ||
else | ||
throw TypeError('Invalid operand definition.'); | ||
throw TypeError('Invalid operand definition.'); // Should never happen. | ||
}; | ||
@@ -163,3 +168,3 @@ Def.prototype.matchOperandTemplates = function (templates, operand) { | ||
opcode: this.opcode, | ||
opcodeHex: this.opcode.toString(16), | ||
opcodeHex: this.opcode.toString(16) | ||
}; | ||
@@ -207,4 +212,7 @@ if (this.operandSize) | ||
var group_defaults = defs[0], definitions = defs.slice(1); | ||
// If only one object provided, we treat it as instruction definition rather then | ||
// as group defaults. | ||
if (!definitions.length) | ||
definitions = [group_defaults]; | ||
// Mnemonic. | ||
if (!group_defaults.mn) | ||
@@ -236,3 +244,3 @@ group_defaults.mn = this.mnemonic; | ||
mnemonic: this.mnemonic, | ||
definitions: instructions, | ||
definitions: instructions | ||
}; | ||
@@ -285,2 +293,10 @@ }; | ||
}; | ||
// create(table: t.TableDefinition, defaults: t.Definition): this { | ||
// for(var mnemonic in table) { | ||
// var group = new this.DefGroupClass(this, mnemonic); | ||
// group.createDefinitions(table[mnemonic], defaults); | ||
// this.groups[mnemonic] = group; | ||
// } | ||
// return this; | ||
// } | ||
DefTable.prototype.toJson = function () { | ||
@@ -322,2 +338,3 @@ var json = {}; | ||
if (tpl) { | ||
// If registers are 5-bit wide, we can encode them only with EVEX, not VEX. | ||
if (def.vex && ops.has5bitRegister()) | ||
@@ -324,0 +341,0 @@ return; |
@@ -14,5 +14,8 @@ "use strict"; | ||
function Expression() { | ||
// Index where instruction was inserted in `Code`s buffer. | ||
this.index = 0; | ||
this.length = exports.SIZE_UNKNOWN; | ||
// Byte offset of the instruction in compiled machine code. | ||
this.offset = exports.OFFSET_UNKNOWN; | ||
// Same as `offset` but for instructions that we don't know byte size yet we assume `MAX_SIZE`. | ||
this.offsetMax = exports.OFFSET_UNKNOWN; | ||
@@ -24,2 +27,3 @@ this.code = null; | ||
}; | ||
// Size in bytes of the instruction. | ||
Expression.prototype.bytes = function () { | ||
@@ -36,7 +40,10 @@ return this.length; | ||
}; | ||
// Whether the size of this `Expression` is determined. | ||
Expression.prototype.hasSize = function () { | ||
return this.bytes() !== exports.SIZE_UNKNOWN; | ||
}; | ||
// Called after new expression is inserted into `Code`. | ||
Expression.prototype.build = function () { | ||
}; | ||
// Calculated during the first pass, when expressions are inserted into `Code` block. | ||
Expression.prototype.calcOffsetMaxAndOffset = function () { | ||
@@ -68,2 +75,3 @@ if (this.index === 0) { | ||
}; | ||
// Calculated during the second pass, when all expressions determine their final size. | ||
Expression.prototype.calcOffset = function () { | ||
@@ -78,2 +86,3 @@ if (this.offset !== exports.OFFSET_UNKNOWN) | ||
if (offset === exports.OFFSET_UNKNOWN) | ||
// this.offset = OFFSET_UNKNOWN; | ||
throw Error("Instruction [" + (this.index - 1) + "] does not have offset."); | ||
@@ -83,2 +92,3 @@ else { | ||
if (bytes === exports.SIZE_UNKNOWN) | ||
// this.offset = OFFSET_UNKNOWN; | ||
throw Error("Instruction [" + (this.index - 1) + "] does not have size."); | ||
@@ -135,2 +145,4 @@ else | ||
this.length = 0; | ||
// if((typeof name !== 'string') || !name) | ||
// throw TypeError('Label name must be a non-empty string.'); | ||
this.symbol = new o.Symbol(this, 0, name); | ||
@@ -283,2 +295,3 @@ } | ||
exports.Data = Data; | ||
// A pre-filled template with some binary data. | ||
var Template = (function (_super) { | ||
@@ -305,2 +318,4 @@ __extends(Template, _super); | ||
exports.Template = Template; | ||
// Expressions that have operands, operands might reference (in case of `Relative`) other expressions, which | ||
// have not been insert into code yet, so we might not know the how those operands evaluate on first two passes. | ||
var ExpressionVariable = (function (_super) { | ||
@@ -311,6 +326,19 @@ __extends(ExpressionVariable, _super); | ||
_super.call(this); | ||
this.ops = null; | ||
this.ops = null; // Operands provided by user. | ||
this.isEvaluated = false; | ||
this.ops = ops; | ||
} | ||
// canEvaluate() { | ||
// for(var op of this.ops.list) { | ||
// if(op instanceof Relative) { | ||
// var rel = op as Relative; | ||
// if(rel.target.offset === OFFSET_UNKNOWN) return false; | ||
// } | ||
// } | ||
// return true; | ||
// } | ||
// Whether this Expression is ready to be written to binary buffer. | ||
// canEvaluate() { | ||
// return true; | ||
// } | ||
ExpressionVariable.prototype.evaluate = function () { | ||
@@ -347,2 +375,4 @@ this.isEvaluated = true; | ||
var rel = op; | ||
// num = list[j] = rel.rebaseOffset(this); | ||
// num = rel.rebaseOffset(this); | ||
num = rel.evaluate(this); | ||
@@ -394,2 +424,3 @@ } | ||
exports.DataVariable = DataVariable; | ||
// Expression which, not only has variable operands, but which may evaluate to different sizes. | ||
var ExpressionVolatile = (function (_super) { | ||
@@ -404,2 +435,3 @@ __extends(ExpressionVolatile, _super); | ||
}; | ||
// If `Expression` can generate different size machine code this method forces it to pick one. | ||
ExpressionVolatile.prototype.getFixedSizeExpression = function () { | ||
@@ -411,2 +443,3 @@ return this; | ||
exports.ExpressionVolatile = ExpressionVolatile; | ||
// Aligns data to some byte boundary. | ||
var Align = (function (_super) { | ||
@@ -417,2 +450,3 @@ __extends(Align, _super); | ||
this.length = exports.SIZE_UNKNOWN; | ||
// Different size templates we use to fill in the empty bytes, templates grow sequentially by one byte in size. | ||
this.templates = [ | ||
@@ -480,4 +514,4 @@ [0x00], | ||
_super.apply(this, arguments); | ||
this.def = null; | ||
this.opts = null; | ||
this.def = null; // Definition on how to construct this instruction. | ||
this.opts = null; // Instruction options provided by user. | ||
} | ||
@@ -509,3 +543,3 @@ Instruction.prototype.build = function () { | ||
}); | ||
cmt = "0x" + octets.join(', 0x') + (" " + this.bytes() + " bytes"); | ||
cmt = "0x" + octets.join(', 0x') + (" " + this.bytes() + " bytes"); // + ' / ' + this.def.toString(); | ||
} | ||
@@ -517,2 +551,4 @@ return this.formatToString(margin, expression, cmt); | ||
exports.Instruction = Instruction; | ||
// Wrapper around multiple instructions when different machine instructions can be used to perform the same task. | ||
// For example, `jmp` with `rel8` or `rel32` immediate, or when multiple instruction definitions match provided operands. | ||
var InstructionSet = (function (_super) { | ||
@@ -524,4 +560,4 @@ __extends(InstructionSet, _super); | ||
this.insn = []; | ||
this.picked = -1; | ||
this.opts = null; | ||
this.picked = -1; // Index of instruction that was eventually chosen. | ||
this.opts = null; // Instruction options provided by user. | ||
this.matches = matches; | ||
@@ -546,3 +582,3 @@ this.opts = opts; | ||
var ins = this.insn[j]; | ||
var rel = ins.ops.list[m]; | ||
var rel = ins.ops.list[m]; // Relative of instruction. | ||
var success = rel.canHoldMaxOffset(this); | ||
@@ -600,2 +636,3 @@ if (success) { | ||
return null; | ||
// Pick the shortest instruction if we know all instruction sizes, otherwise don't pick any. | ||
var size = exports.SIZE_UNKNOWN; | ||
@@ -639,2 +676,4 @@ var isize = 0; | ||
if (typeof tpl === 'number') { | ||
// Skip number | ||
// `int 3`, for example, is just `0xCC` instruction. | ||
ops.list[j] = null; | ||
@@ -693,2 +732,6 @@ } | ||
var lines = []; | ||
// for(var j = 0; j < this.insn.length; j++) { | ||
// if(this.insn[j].ops) lines.push(this.insn[j].toString(margin, hex)); | ||
// else lines.push(' ' + this.matches.list[j].def.toString()); | ||
// } | ||
for (var _i = 0, _a = this.matches.list; _i < _a.length; _i++) { | ||
@@ -695,0 +738,0 @@ var match = _a[_i]; |
@@ -60,2 +60,3 @@ "use strict"; | ||
} | ||
// export type Tnumber = number|number64|number128|number256|number512|number1024|number2048; | ||
function isTnumber(num) { | ||
@@ -70,6 +71,9 @@ if (typeof num === 'number') | ||
exports.isTnumber = isTnumber; | ||
// General operand used in our assembly "language". | ||
var Operand = (function () { | ||
function Operand() { | ||
// Size in bits. | ||
this.size = SIZE.ANY; | ||
} | ||
// Convenience method to get `Register` associated with `Register` or `Memory`. | ||
Operand.prototype.reg = function () { | ||
@@ -93,2 +97,5 @@ return null; | ||
exports.Operand = Operand; | ||
// ## Constant | ||
// | ||
// Constants are everything where we directly type in a `number` value. | ||
var Constant = (function (_super) { | ||
@@ -101,2 +108,3 @@ __extends(Constant, _super); | ||
this.value = 0; | ||
// Each byte as a `number` in reverse order. | ||
this.octets = []; | ||
@@ -131,2 +139,3 @@ this.signed = true; | ||
var clazz = this.signed ? Constant.sizeClass(value) : Constant.sizeClassUnsigned(value); | ||
/* JS integers are 53-bit, so split here `number`s over 32 bits into [number, number]. */ | ||
if (clazz === SIZE.Q) | ||
@@ -205,2 +214,5 @@ this.setValue64([util_1.UInt64.lo(value), util_1.UInt64.hi(value)]); | ||
throw Error("Already larger than " + size + " bits, cannot zero-extend."); | ||
// TODO: Make it work with 128-bit numbers too, below. | ||
// We know it is not number64, because we don't deal with number larger than 64-bit, | ||
// and if it was 64-bit already there would be nothing to extend. | ||
var value = this.value; | ||
@@ -378,2 +390,5 @@ if (size === SIZE.Q) { | ||
exports.ImmediateUnsigned64 = ImmediateUnsigned64; | ||
// ## Registers | ||
// | ||
// `Register` represents one of `%rax`, `%rbx`, etc. registers. | ||
var Register = (function (_super) { | ||
@@ -383,3 +398,3 @@ __extends(Register, _super); | ||
_super.call(this); | ||
this.id = 0; | ||
this.id = 0; // Number value of register. | ||
this.name = 'reg'; | ||
@@ -419,2 +434,5 @@ this.id = id; | ||
exports.Register = Register; | ||
// ## Memory | ||
// | ||
// `Memory` is RAM addresses which `Register`s can *dereference*. | ||
var Memory = (function (_super) { | ||
@@ -448,2 +466,3 @@ __extends(Memory, _super); | ||
exports.Memory = Memory; | ||
// Operand which needs `evaluation`, it may be that it cannot evaluate on first two passes. | ||
var Variable = (function (_super) { | ||
@@ -453,3 +472,3 @@ __extends(Variable, _super); | ||
_super.apply(this, arguments); | ||
this.result = null; | ||
this.result = null; // Result of evaluation. | ||
} | ||
@@ -462,2 +481,3 @@ Variable.prototype.canEvaluate = function (owner) { | ||
}; | ||
// Evaluate approximately during 2nd pass. | ||
Variable.prototype.evaluatePreliminary = function (owner) { | ||
@@ -476,2 +496,3 @@ return 0; | ||
exports.isTvariable = isTvariable; | ||
// Relative jump targets for jump instructions. | ||
var Relative = (function (_super) { | ||
@@ -498,2 +519,3 @@ __extends(Relative, _super); | ||
return this.result = this.rebaseOffset(owner) - owner.bytes(); | ||
// return this.result = this.rebaseOffset(owner); | ||
}; | ||
@@ -512,6 +534,10 @@ Relative.prototype.evaluatePreliminary = function (owner) { | ||
Relative.prototype.cast = function (RelativeClass) { | ||
// cast(RelativeClass: typeof Relative) { | ||
this.size = RelativeClass.size; | ||
// return new RelativeClass(this.target, this.offset); | ||
return this; | ||
}; | ||
Relative.prototype.rebaseOffset = function (new_target) { | ||
// if(expr.code !== this.expr.code) | ||
// throw Error('Rebase from different code blocks not implemented yet.'); | ||
if (new_target.offset === -1) | ||
@@ -521,3 +547,6 @@ throw Error('Expression has no offset, cannot rebase.'); | ||
}; | ||
// Recalculate relative offset given a different Expression. | ||
// rebase(target: Expression): Relative { | ||
Relative.prototype.rebase = function (target) { | ||
// return new Relative(target, this.rebaseOffset(expr)); | ||
this.offset = this.rebaseOffset(target); | ||
@@ -593,2 +622,3 @@ this.target = target; | ||
exports.Symbol = Symbol; | ||
// Collection of operands an `Expression` might have. | ||
var Operands = (function () { | ||
@@ -599,3 +629,3 @@ function Operands(list, size) { | ||
this.list = []; | ||
this.size = SIZE.ANY; | ||
this.size = SIZE.ANY; // Size of each operand. | ||
this.size = size; | ||
@@ -614,2 +644,3 @@ this.list = list; | ||
var i = require('./instruction'); | ||
// Wrap `Expression` into `Relative`. | ||
for (var j = 0; j < ops.length; j++) { | ||
@@ -631,2 +662,3 @@ if (ops[j] instanceof instruction_1.Expression) { | ||
}; | ||
// Wrap `Expression` into `Relative`. | ||
Operands.prototype.normalizeExpressionToRelative = function () { | ||
@@ -642,4 +674,6 @@ var i = require('./instruction'); | ||
Operands.prototype.validateSize = function () { | ||
// Verify operand sizes. | ||
for (var _i = 0, _a = this.list; _i < _a.length; _i++) { | ||
var op = _a[_i]; | ||
// We can determine operand size only by Register; Memory and Immediate and others don't tell us the right size. | ||
if (op instanceof Register) { | ||
@@ -737,2 +771,4 @@ if (this.size !== SIZE.ANY) { | ||
}; | ||
// EVEX may encode up to 4 operands, 32 registers, so register can be up to 5-bits wide, | ||
// we need to check for that because in that case we cannot use VEX. | ||
Operands.prototype.has5bitRegister = function () { | ||
@@ -739,0 +775,0 @@ for (var j = 0; j < 4; j++) { |
{ | ||
"name": "ass-js", | ||
"description": "Assembler.js", | ||
"version": "1.0.3", | ||
"version": "1.0.4", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "x86", |
"use strict"; | ||
var operand_1 = require('./operand'); | ||
exports.S = operand_1.SIZE; | ||
// Operands | ||
exports.r = operand_1.Register; | ||
@@ -20,2 +21,3 @@ exports.m = operand_1.Memory; | ||
exports.rel32 = operand_1.Relative32; | ||
// Global defaults | ||
exports.defaults = { o: 0x00, mn: '', s: exports.S.NONE, ops: null }; |
@@ -9,1 +9,2 @@ "use strict"; | ||
console.log(bin); | ||
// console.log(ass.x86.x64.Code.table.toString()); |
@@ -34,2 +34,4 @@ "use strict"; | ||
UInt64.joinToNumber = function (hi, lo) { | ||
// if ((lo !== lo|0) && (lo !== (lo|0) + 4294967296)) throw new Error ("lo out of range: "+lo); | ||
// if ((hi !== hi|0) && hi >= 1048576) throw new Error ("hi out of range: "+hi); | ||
if (lo < 0) | ||
@@ -36,0 +38,0 @@ lo += 4294967296; |
@@ -28,2 +28,3 @@ "use strict"; | ||
var bySize = group.groupBySize(); | ||
// Create methods with size postfix, like: pushq, pushd, pushw, etc.. | ||
var _loop_2 = function(s) { | ||
@@ -44,2 +45,3 @@ var size = parseInt(s); | ||
} | ||
// Create general method where we determine operand size from profided operands. | ||
ctx[mnemonic] = function () { | ||
@@ -95,2 +97,3 @@ var ui_ops = []; | ||
var def = matches.list[j].def; | ||
// Check mode of CPU. | ||
if (!(this.mode & def.mode)) { | ||
@@ -100,2 +103,4 @@ matches.list.splice(j, 1); | ||
} | ||
// If EVEX-specific options provided by user, | ||
// remove instruction definition matches that don't have EVEX prefix. | ||
var needs_evex = opts.mask || (typeof opts.z !== 'undefined'); | ||
@@ -110,2 +115,9 @@ if (needs_evex) { | ||
}; | ||
// Displacement is up to 4 bytes in size, and 8 bytes for some specific MOV instructions, AMD64 Vol.2 p.24: | ||
// | ||
// > The size of a displacement is 1, 2, or 4 bytes. | ||
// | ||
// > Also, in 64-bit mode, support is provided for some 64-bit displacement | ||
// > and immediate forms of the MOV instruction. See “Immediate Operand Size” in Volume 1 for more | ||
// > information on this. | ||
Code.prototype.mem = function (disp) { | ||
@@ -112,0 +124,0 @@ if (typeof disp === 'number') |
@@ -39,2 +39,3 @@ "use strict"; | ||
} | ||
// 256.66.0F3A.W0 => {L: 1, pp: 1, mmmmm: 1, W: 0} | ||
Def.parseVexString = function (vstr) { | ||
@@ -47,4 +48,5 @@ var vdef = { | ||
W: 1, | ||
WIG: false, | ||
WIG: false | ||
}; | ||
// vvvv: NDS, NDD, DDS | ||
if (vstr.indexOf('NDS') > -1) | ||
@@ -56,6 +58,8 @@ vdef.vvvv = 'NDS'; | ||
vdef.vvvv = 'DDS'; | ||
// L: 128, 256, LIG, LZ | ||
if (vstr.indexOf('256') > -1) | ||
vdef.L = 1; | ||
else if (vstr.indexOf('512') > -1) | ||
vdef.L = 2; | ||
vdef.L = 2; // EVEX | ||
// pp: 66, F2, F3 | ||
if (vstr.indexOf('.66.') > -1) | ||
@@ -67,2 +71,3 @@ vdef.pp = 1; | ||
vdef.pp = 2; | ||
// mmmmm: 0F, 0F3A, 0F38 | ||
if (vstr.indexOf('0F38') > -1) | ||
@@ -73,5 +78,7 @@ vdef.mmmmm = 2; | ||
else if (vstr.indexOf('0F') > -1) | ||
vdef.mmmmm = 1; | ||
vdef.mmmmm = 1; // Could still be 2-byte VEX prefix | ||
// W: W0, W1 | ||
if (vstr.indexOf('W0') > -1) | ||
vdef.W = 0; | ||
// WIG | ||
if (vstr.indexOf('WIG') > -1) | ||
@@ -85,3 +92,3 @@ vdef.WIG = true; | ||
Def.prototype.matchOperandTemplate = function (tpl, operand) { | ||
var OperandClass = tpl; | ||
var OperandClass = tpl; // as typeof o.Operand; | ||
if ((typeof OperandClass === 'function') && (OperandClass.name.indexOf('Immediate') === 0)) { | ||
@@ -88,0 +95,0 @@ if (!operand_1.isTnumber(operand)) |
@@ -64,2 +64,7 @@ "use strict"; | ||
exports.Align = Align; | ||
// ## 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 (_super) { | ||
@@ -69,2 +74,3 @@ __extends(Instruction, _super); | ||
_super.apply(this, arguments); | ||
// Instruction parts. | ||
this.pfxOpSize = null; | ||
@@ -77,4 +83,4 @@ this.pfxAddrSize = null; | ||
this.prefixes = []; | ||
this.pfxEx = null; | ||
this.opcode = new p.Opcode; | ||
this.pfxEx = null; // One of REX, VEX, EVEX prefixes, only one allowed. | ||
this.opcode = new p.Opcode; // required | ||
this.modrm = null; | ||
@@ -84,2 +90,4 @@ this.sib = null; | ||
this.immediates = []; | ||
// Direction for register-to-register `MOV` operations, whether REG field of Mod-R/M byte is destination. | ||
// We set this to `false` to be compatible with GAS assembly, which we use for testing. | ||
this.regToRegDirectionRegIsDst = false; | ||
@@ -97,3 +105,3 @@ } | ||
this.pfxEx = null; | ||
this.opcode = new p.Opcode; | ||
this.opcode = new p.Opcode; // required | ||
this.modrm = null; | ||
@@ -161,2 +169,3 @@ this.sib = null; | ||
Instruction.prototype.getFixedSizeExpression = function () { | ||
// Determine size of displacement | ||
this.fixDisplacementSize(); | ||
@@ -167,3 +176,3 @@ return _super.prototype.getFixedSizeExpression.call(this); | ||
this.ops.evaluate(this); | ||
var max = 2; | ||
var max = 2; // Up to 2 immediates. | ||
for (var j = 0; j < max; j++) { | ||
@@ -173,5 +182,7 @@ var rel = this.ops.getRelative(j); | ||
var res = rel.result; | ||
// var res = (rel.result as number) - this.bytes(); | ||
this.immediates[j].value.setValue(res); | ||
} | ||
} | ||
// Evaluate displacement variable. | ||
if (this.displacement && this.displacement.value.variable) { | ||
@@ -295,2 +306,3 @@ var value = this.displacement.value; | ||
this.createEvexPrefix(); | ||
// Mandatory prefixes required by op-code. | ||
if (this.def.prefixes) { | ||
@@ -306,2 +318,3 @@ for (var _i = 0, _a = this.def.prefixes; _i < _a.length; _i++) { | ||
Instruction.prototype.createVexPrefix = function () { | ||
// These bits in VEX are inverted, so they actually all mean "0" zeros. | ||
var R = 1, X = 1, B = 1, vvvv = 15; | ||
@@ -313,3 +326,3 @@ var pos = this.def.opEncoding.indexOf('v'); | ||
throw Error("Could not find Register operand at position " + pos + " to encode VEX.vvvv"); | ||
vvvv = (~reg.get4bitId()) & 15; | ||
vvvv = (~reg.get4bitId()) & 15; // Inverted | ||
} | ||
@@ -322,3 +335,3 @@ pos = this.def.opEncoding.indexOf('r'); | ||
if (reg.idSize() > 3) | ||
R = 0; | ||
R = 0; // Inverted | ||
} | ||
@@ -329,3 +342,3 @@ pos = this.def.opEncoding.indexOf('m'); | ||
if (reg && (reg.idSize() > 3)) | ||
B = 0; | ||
B = 0; // Inverted | ||
} | ||
@@ -352,4 +365,4 @@ var mem = this.ops.getMemoryOperand(); | ||
throw Error("Could not find Register operand at position " + pos + " to encode EVEX.vvvv"); | ||
evex.vvvv = (~reg.get4bitId()) & 15; | ||
evex.Vp = reg.id & 16 ? 0 : 1; | ||
evex.vvvv = (~reg.get4bitId()) & 15; // Inverted | ||
evex.Vp = reg.id & 16 ? 0 : 1; // Inverted | ||
} | ||
@@ -363,5 +376,5 @@ pos = this.def.opEncoding.indexOf('r'); | ||
if (id_size > 3) | ||
evex.R = 0; | ||
evex.R = 0; // Inverted | ||
if (id_size > 4) { | ||
evex.Rp = 0; | ||
evex.Rp = 0; // Inverted | ||
if (reg.id & 8) | ||
@@ -378,5 +391,5 @@ evex.R = 0; | ||
if (reg.idSize() > 3) | ||
evex.B = 0; | ||
evex.B = 0; // Inverted | ||
if (reg.idSize() > 4) { | ||
evex.X = 0; | ||
evex.X = 0; // Inverted | ||
if (reg.id & 8) | ||
@@ -392,5 +405,5 @@ evex.B = 0; | ||
if (mem.base && (mem.base.idSize() > 3)) | ||
evex.B = 0; | ||
evex.B = 0; // Inverted | ||
if (mem.index && (mem.index.idSize() > 3)) | ||
evex.X = 0; | ||
evex.X = 0; // Inverted | ||
} | ||
@@ -402,2 +415,3 @@ if (this.opts.mask) | ||
}; | ||
// Set mask register for `EVEX` instructions. | ||
Instruction.prototype.mask = function (k) { | ||
@@ -411,2 +425,3 @@ if (!(this.pfxEx instanceof p.PrefixEvex)) | ||
}; | ||
// Set `z` bit for `EVEX` instructions. | ||
Instruction.prototype.z = function (value) { | ||
@@ -425,2 +440,3 @@ if (value === void 0) { value = 1; } | ||
if (def.regInOp) { | ||
// We have register encoded in op-code here. | ||
if (!dst || (!dst.isRegister())) | ||
@@ -431,2 +447,3 @@ throw TypeError("Operation needs destination Register."); | ||
else { | ||
// Direction bit `d` | ||
if (this.def.opcodeDirectionBit) { | ||
@@ -437,2 +454,3 @@ var direction = p.Opcode.DIRECTION.REG_IS_DST; | ||
} | ||
// *reg-to-reg* operation | ||
if ((dst instanceof o.Register) && (src instanceof o.Register)) { | ||
@@ -460,6 +478,8 @@ if (this.regToRegDirectionRegIsDst) | ||
var has_opreg = (this.def.opreg > -1); | ||
var dst_in_modrm = !this.def.regInOp && !!dst; | ||
var dst_in_modrm = !this.def.regInOp && !!dst; // Destination operand is NOT encoded in main op-code byte. | ||
if (has_opreg || dst_in_modrm) { | ||
// var reg_is_dst = !!(this.opcode.op & p.Opcode.DIRECTION.REG_IS_DST); | ||
var reg_is_dst = this.def.opEncoding[0] !== 'm' ? true : false; | ||
if (has_opreg) { | ||
// If we have `opreg`, then instruction has up to one operand. | ||
reg = this.def.opreg; | ||
@@ -477,2 +497,3 @@ var r = this.ops.getRegisterOperand(); | ||
else { | ||
// Reg-to-reg instruction; | ||
if ((encoding.length === 2) && (dst instanceof o.Register) && (src instanceof o.Register)) { | ||
@@ -495,2 +516,3 @@ mod = p.Modrm.MOD.REG_TO_REG; | ||
else { | ||
// var r: o.Register = this.op.getRegisterOperand(this.regToRegDirectionRegIsDst); | ||
var r = this.ops.getRegisterOperand(this.regToRegDirectionRegIsDst ? 0 : 1); | ||
@@ -529,2 +551,6 @@ if (!r) | ||
} | ||
// `o.Memory` class makes sure that ESP cannot be a SIB index register and | ||
// that EBP always has displacement value even if 0x00. | ||
// Memory operand can be encoded in only one way (Modrm.rm + SIB) so we | ||
// ignore here `def.opEncoding` field. | ||
var m = this.ops.getMemoryOperand(); | ||
@@ -541,6 +567,9 @@ if (!m) { | ||
throw TypeError('Memory Index reference needs Scale factor.'); | ||
// dispX | ||
// We use `disp32` with SIB byte version because the version without SIB byte | ||
// will be used for RIP-relative addressing. | ||
if (!m.base && !m.index && m.displacement) { | ||
m.displacement.signExtend(o.DisplacementValue.SIZE.DISP32); | ||
mod = p.Modrm.MOD.INDIRECT; | ||
rm = p.Modrm.RM.NEEDS_SIB; | ||
rm = p.Modrm.RM.NEEDS_SIB; // SIB byte follows | ||
this.modrm = new p.Modrm(mod, reg, rm); | ||
@@ -551,2 +580,6 @@ this.length++; | ||
} | ||
// [BASE] | ||
// [BASE] + dispX | ||
// `o.Memory` class makes sure that EBP always has displacement value even if 0x00, | ||
// so EBP will not appear here. | ||
if (m.base && !m.index) { | ||
@@ -556,2 +589,4 @@ mod = p.Modrm.getModDispSize(m); | ||
m.displacement.signExtend(o.DisplacementValue.SIZE.DISP32); | ||
// SIB byte follows in `[RSP]` case, and `[RBP]` is impossible as RBP | ||
// always has a displacement, [RBP] case is used for RIP-relative addressing. | ||
rm = m.base.get3bitId(); | ||
@@ -563,2 +598,3 @@ this.modrm = new p.Modrm(mod, reg, rm); | ||
} | ||
// [BASE + INDEX x SCALE] + dispX | ||
if (m.base || m.index) { | ||
@@ -569,3 +605,3 @@ mod = p.Modrm.getModDispSize(m); | ||
m.displacement.signExtend(o.DisplacementValue.SIZE.DISP32); | ||
rm = p.Modrm.RM.NEEDS_SIB; | ||
rm = p.Modrm.RM.NEEDS_SIB; // SIB byte follows | ||
this.modrm = new p.Modrm(mod, reg, rm); | ||
@@ -594,2 +630,4 @@ this.length++; | ||
I = m.index.get3bitId(); | ||
// RSP register cannot be used as index, `o.Memory` class already ensures it | ||
// if used in normal way. | ||
if (I === p.Sib.INDEX_NONE) | ||
@@ -615,3 +653,5 @@ throw Error("Register " + m.index.toString() + " cannot be used as SIB index."); | ||
if (m.displacement.variable) { | ||
this.lengthMax += o.DisplacementValue.SIZE.DISP32 / 8; | ||
// Displacement will be at least 1 byte, | ||
// but we skip `this.length` for now. | ||
this.lengthMax += o.DisplacementValue.SIZE.DISP32 / 8; // max 4 bytes | ||
} | ||
@@ -625,2 +665,4 @@ else { | ||
else if (this.modrm && this.sib && (this.sib.B === p.Sib.BASE_NONE)) { | ||
// Some SIB byte encodings require displacement, if we don't have displacement yet | ||
// add zero displacement. | ||
var disp = null; | ||
@@ -649,3 +691,3 @@ switch (this.modrm.mod) { | ||
Instruction.prototype.createImmediates = function () { | ||
var max = 2; | ||
var max = 2; // Up to 2 immediates. | ||
for (var j = 0; j < max; j++) { | ||
@@ -655,2 +697,14 @@ var imm = this.ops.getImmediate(j); | ||
if (imm) { | ||
// If immediate does not have concrete size, use the size of instruction operands. | ||
// if(imm.constructor === o.Immediate) { | ||
// var ImmediateClass = this.def.getImmediateClass(); | ||
// if(ImmediateClass) imm = new ImmediateClass(imm.value, imm.signed); | ||
// else { | ||
// var size = this.op.size; | ||
// imm = o.Immediate.factory(size, imm.value, imm.signed); | ||
// imm.extend(size); | ||
// } | ||
// } | ||
// if (this.displacement && (this.displacement.value.size === SIZE.Q)) | ||
// throw TypeError(`Cannot have Immediate with ${SIZE.Q} bit Displacement.`); | ||
immp = new p.Immediate(imm); | ||
@@ -678,2 +732,4 @@ this.immediates[j] = immp; | ||
exports.Instruction = Instruction; | ||
// Wrapper around multiple instructions when different machine instructions can be used to perform the same task. | ||
// For example, `jmp` with `rel8` or `rel32` immediate, or when multiple instruction definitions match provided operands. | ||
var InstructionSet = (function (_super) { | ||
@@ -680,0 +736,0 @@ __extends(InstructionSet, _super); |
@@ -35,6 +35,8 @@ "use strict"; | ||
_super.prototype.setValue32.call(this, value); | ||
/* Make sure `Displacement` is 1 or 4 bytes, not 2. */ | ||
// if(this.size > DisplacementValue.SIZE.DISP8) this.zeroExtend(DisplacementValue.SIZE.DISP32); | ||
}; | ||
DisplacementValue.SIZE = { | ||
DISP8: operand_1.SIZE.B, | ||
DISP32: operand_1.SIZE.D, | ||
DISP32: operand_1.SIZE.D | ||
}; | ||
@@ -44,2 +46,5 @@ return DisplacementValue; | ||
exports.DisplacementValue = DisplacementValue; | ||
// ## Registers | ||
// | ||
// `Register` represents one of `%rax`, `%rbx`, etc. registers. | ||
var Register = (function (_super) { | ||
@@ -78,2 +83,3 @@ __extends(Register, _super); | ||
}; | ||
// Whether the register is one of `%r8`, `%r9`, etc. extended registers. | ||
Register.prototype.isExtended = function () { | ||
@@ -270,2 +276,5 @@ return this.id > 7; | ||
exports.RegisterDr = RegisterDr; | ||
// # Scale | ||
// | ||
// `Scale` used in SIB byte in two bit `SCALE` field. | ||
var Scale = (function (_super) { | ||
@@ -287,2 +296,5 @@ __extends(Scale, _super); | ||
exports.Scale = Scale; | ||
// ## Memory | ||
// | ||
// `Memory` is RAM addresses which `Register`s can *dereference*. | ||
var Memory = (function (_super) { | ||
@@ -306,2 +318,3 @@ __extends(Memory, _super); | ||
}; | ||
// Case memory to some size. | ||
Memory.prototype.cast = function (size) { | ||
@@ -320,2 +333,3 @@ var mem = Memory.factory(size); | ||
return this.index; | ||
// throw Error('No backing register.'); | ||
return null; | ||
@@ -331,2 +345,3 @@ }; | ||
} | ||
// RBP, EBP etc.. always need displacement for ModRM and SIB bytes. | ||
var is_ebp = (regfile_1.R64.RBP & 7) === base.get3bitId(); | ||
@@ -437,2 +452,3 @@ if (is_ebp && !this.displacement) | ||
exports.Memory512 = Memory512; | ||
// Collection of operands an instruction might have. | ||
var Operands = (function (_super) { | ||
@@ -451,2 +467,16 @@ __extends(Operands, _super); | ||
}; | ||
// getRegisterOperand(dst_first = true): Register { | ||
// var [dst, src] = this.list; | ||
// var first, second; | ||
// if(dst_first) { | ||
// first = dst; | ||
// second = src; | ||
// } else { | ||
// first = src; | ||
// second = dst; | ||
// } | ||
// if(first instanceof Register) return first as Register; | ||
// if(second instanceof Register) return second as Register; | ||
// return null; | ||
// } | ||
Operands.prototype.hasImmediate = function () { | ||
@@ -475,2 +505,3 @@ return !!this.getImmediate(); | ||
exports.Operands = Operands; | ||
// ## Export Registers | ||
function validateRegId(id, min, max, Clazz) { | ||
@@ -477,0 +508,0 @@ if (typeof id !== 'number') |
214
x86/parts.js
@@ -9,2 +9,16 @@ "use strict"; | ||
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 () { | ||
@@ -42,2 +56,3 @@ function InstructionPart() { | ||
var PREFIX = exports.PREFIX; | ||
// Prefixes that consist of a single static byte. | ||
var PrefixStatic = (function (_super) { | ||
@@ -89,2 +104,3 @@ __extends(PrefixStatic, _super); | ||
} | ||
// static supported = ['cmps', 'cmpsb', 'cmpbd', 'cmpsw', 'scas', 'scasb', 'scasd', 'scasw']; | ||
PrefixRepe.supported = ['cmps', 'scas']; | ||
@@ -99,2 +115,3 @@ return PrefixRepe; | ||
} | ||
// static supported = ['cmps', 'cmpsb', 'cmpsd', 'cmpsw', 'scas', 'scasb', 'scasd', 'scasw']; | ||
PrefixRepne.supported = ['cmps', 'scas']; | ||
@@ -104,2 +121,3 @@ return PrefixRepne; | ||
exports.PrefixRepne = PrefixRepne; | ||
// Lock prefix for performing atomic memory operations. | ||
var PrefixLock = (function (_super) { | ||
@@ -115,2 +133,18 @@ __extends(PrefixLock, _super); | ||
exports.PrefixLock = PrefixLock; | ||
// ## 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) { | ||
@@ -132,2 +166,27 @@ __extends(PrefixRex, _super); | ||
exports.PrefixRex = PrefixRex; | ||
// ### 2-byte VEX: | ||
// 76543210 | ||
// 11000100 | ||
// | ||
// 76543210 | ||
// ||||||pp ---> pp | ||
// |||||L -----> L | ||
// |vvvv ------> vvvv | ||
// R ----------> R | ||
// | ||
// ### 3-byte VEX: | ||
// 76543210 | ||
// 11000101 | ||
// | ||
// 76543210 | ||
// |||mmmmm ---> mmmmm | ||
// ||B --------> B | ||
// |X ---------> X | ||
// R ----------> R | ||
// | ||
// 76543210 | ||
// ||||||pp ---> pp | ||
// |||||L -----> L | ||
// |vvvv ------> vvvv | ||
// W ----------> W | ||
var PrefixVex = (function (_super) { | ||
@@ -141,8 +200,9 @@ __extends(PrefixVex, _super); | ||
_super.call(this); | ||
this.bytes = 2; | ||
this.R = 1; | ||
this.X = 1; | ||
this.bytes = 2; // VEX can be either 2 or 3 bytes. | ||
// R, X, B, W and vvvv are inverted. | ||
this.R = 1; // Must be 1, if not used, otherwise wrong instruction. | ||
this.X = 1; // Must be 1, if not used, otherwise wrong instruction. | ||
this.B = 1; | ||
this.W = 1; | ||
this.vvvv = 15; | ||
this.vvvv = 15; // must be 0b1111, if not used, otherwise CPU will #UD | ||
this.mmmmm = 0; | ||
@@ -156,3 +216,3 @@ this.L = 0; | ||
if (vexdef.WIG) | ||
this.W = 0; | ||
this.W = 0; // When WIG "W ignored", set to "0" to make compatible with GAS. | ||
this.R = R; | ||
@@ -172,7 +232,7 @@ this.X = X; | ||
if (this.bytes === 2) { | ||
arr.push(197); | ||
arr.push(197); // 0xC5 | ||
arr.push((this.R << 7) | (this.vvvv << 3) | (this.L << 2) | this.pp); | ||
} | ||
else { | ||
arr.push(196); | ||
arr.push(196); // 0xC4 | ||
arr.push((this.R << 7) | (this.X << 6) | (this.B << 5) | this.mmmmm); | ||
@@ -186,3 +246,3 @@ arr.push((this.W << 7) | (this.vvvv << 3) | (this.L << 2) | this.pp); | ||
xF2: 3, | ||
xF3: 2, | ||
xF3: 2 | ||
}; | ||
@@ -192,3 +252,3 @@ PrefixVex.MMMMM = { | ||
x0F3A: 3, | ||
x0F: 1, | ||
x0F: 1 | ||
}; | ||
@@ -198,2 +258,25 @@ return PrefixVex; | ||
exports.PrefixVex = PrefixVex; | ||
// EVEX is 4 bytes: | ||
// 62H | ||
// | ||
// 76543210 | ||
// ||||||mm ---> mm | ||
// ||||00 -----> always 00 | ||
// |||~ -------> R-prime = Rp | ||
// ||B --------> B | ||
// |X ---------> X | ||
// R ----------> R | ||
// | ||
// 76543210 | ||
// ||||||pp ---> pp | ||
// |||||1 -----> always 1 | ||
// |vvvv-------> vvvv | ||
// W ----------> W | ||
// | ||
// 76543210 | ||
// |||||aaa ---> aaa | ||
// ||||~ ------> V-prime = Vp | ||
// |||b -------> b | ||
// |LL --------> LL | ||
// z ----------> z | ||
var PrefixEvex = (function (_super) { | ||
@@ -203,15 +286,17 @@ __extends(PrefixEvex, _super); | ||
_super.call(this); | ||
this.R = 1; | ||
this.X = 1; | ||
this.B = 1; | ||
this.W = 1; | ||
this.vvvv = 15; | ||
this.pp = 0; | ||
this.mm = 0; | ||
this.Rp = 1; | ||
this.z = 0; | ||
this.LL = 0; | ||
this.b = 0; | ||
this.Vp = 1; | ||
this.aaa = 0; | ||
// VEX includes | ||
this.R = 1; // VEX.R - Inverted | ||
this.X = 1; // VEX.X - Inverted | ||
this.B = 1; // VEX.B - Inverted | ||
this.W = 1; // VEX.W - Inverted | ||
this.vvvv = 15; // VEX.vvvv - Inverted | ||
this.pp = 0; // VEX.pp | ||
this.mm = 0; // Low 2 bits of VEX.mmmmm | ||
// New in EVEX | ||
this.Rp = 1; // REX.R extension - Inverted | ||
this.z = 0; // Zeroing/merging | ||
this.LL = 0; // Like VEX.L but extended to 2 bits. | ||
this.b = 0; // Broadcast/RC/SAE context | ||
this.Vp = 1; // VEX.vvvv exntension - Inverted | ||
this.aaa = 0; // Opmask register ID | ||
this.LL = evexdef.L; | ||
@@ -232,2 +317,23 @@ this.mm = evexdef.mmmmm & 3; | ||
exports.PrefixEvex = PrefixEvex; | ||
// ## 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) { | ||
@@ -237,2 +343,3 @@ __extends(Opcode, _super); | ||
_super.apply(this, arguments); | ||
// Main op-code value. | ||
this.op = 0; | ||
@@ -248,2 +355,3 @@ } | ||
Opcode.prototype.write = function (arr) { | ||
// Op-code can be up to 3 bytes long. | ||
var op = this.op; | ||
@@ -257,12 +365,13 @@ if (op > 0xFFFF) | ||
}; | ||
Opcode.MASK_SIZE = 16777214; | ||
Opcode.MASK_DIRECTION = 16777213; | ||
Opcode.MASK_OP = 16777208; | ||
/* 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_OR_DOUBLE: 1, | ||
WORD_OR_DOUBLE: 1 | ||
}; | ||
Opcode.DIRECTION = { | ||
REG_IS_SRC: 0, | ||
REG_IS_DST: 2, | ||
REG_IS_DST: 2 | ||
}; | ||
@@ -272,2 +381,11 @@ return Opcode; | ||
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) { | ||
@@ -299,2 +417,3 @@ __extends(Modrm, _super); | ||
}; | ||
// Two bits of `MOD` field in `Mod-R/M` byte. | ||
Modrm.MOD = { | ||
@@ -304,7 +423,11 @@ INDIRECT: 0, | ||
DISP32: 2, | ||
REG_TO_REG: 3, | ||
REG_TO_REG: 3 | ||
}; | ||
Modrm.RM = { | ||
// When this value is encoded in R/M field, SIB byte has to follow Mod-R/M byte. | ||
NEEDS_SIB: regfile_1.R64.RSP & 7, | ||
INDIRECT_DISP: regfile_1.R64.RBP & 7, | ||
// When this value is encoded in R/M field, and MOD is 0b00 = INDIRECT, | ||
// disp32 bytes have to follow Mod-R/M byte. But not in long-mode, | ||
// in long-mode it is used for RIP-relative adressing. | ||
INDIRECT_DISP: regfile_1.R64.RBP & 7 | ||
}; | ||
@@ -314,2 +437,28 @@ return Modrm; | ||
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) { | ||
@@ -348,3 +497,6 @@ __extends(Sib, _super); | ||
}; | ||
// When index set to 0b100 it means INDEX = 0 and SCALE = 0. | ||
Sib.INDEX_NONE = regfile_1.R64.RSP & 7; | ||
// If Modrm.mod = 0b00, BASE = 0b101, means no BASE. | ||
// if Modrm.mod is 0b01 or 0b10, use RBP + disp8 or RBP + disp32, respectively. | ||
Sib.BASE_NONE = regfile_1.R64.RBP & 7; | ||
@@ -354,2 +506,3 @@ return Sib; | ||
exports.Sib = Sib; | ||
// ## Displacement | ||
var Displacement = (function (_super) { | ||
@@ -369,2 +522,5 @@ __extends(Displacement, _super); | ||
exports.Displacement = Displacement; | ||
// ## Immediate | ||
// | ||
// Immediate constant value that follows other instruction bytes. | ||
var Immediate = (function (_super) { | ||
@@ -371,0 +527,0 @@ __extends(Immediate, _super); |
@@ -18,2 +18,3 @@ "use strict"; | ||
var MODE = exports.MODE; | ||
// Instructins | ||
(function (INS) { | ||
@@ -27,2 +28,3 @@ INS[INS["NONE"] = 0] = "NONE"; | ||
var INS = exports.INS; | ||
// Extensions | ||
(function (EXT) { | ||
@@ -72,2 +74,3 @@ EXT[EXT["NONE"] = 0] = "NONE"; | ||
exports.M = MODE; | ||
// Operands | ||
exports.r = operand_1.Register; | ||
@@ -108,10 +111,17 @@ exports.r8 = operand_1.Register8; | ||
exports.zmm_zmm_zmmm = [exports.zmm, exports.zmm, exports.zmmm]; | ||
// x86 global defaults | ||
exports.defaults = util_1.extend({}, t.defaults, { ds: table_1.S.D, lock: false, or: -1, i: null, r: false, dbit: false, rex: null, mr: true, rep: false, repne: false, | ||
pfx: null, vex: null, evex: null, en: 'rm', mod: exports.M.ALL, ext: null }); | ||
// Instruction are divided in groups, each group consists of list | ||
// of possible instructions. The first object is NOT an instruction | ||
// but defaults for the group. | ||
exports.table = { | ||
cpuid: [{ o: 0x0FA2 }], | ||
// INT Software interrupt | ||
int: [{}, | ||
// CC INT 3 NP Valid Valid Interrupt 3—trap to debugger. | ||
{ o: 0xCC, ops: [3] }, | ||
// CD ib INT imm8 I Valid Valid Interrupt vector specified by immediate byte. | ||
{ o: 0xCD, ops: [table_1.immu8] }, | ||
], | ||
] | ||
}; |
@@ -17,2 +17,3 @@ "use strict"; | ||
Instruction.prototype.needs32To64OperandSizeChange = function () { | ||
// Default operand size in x64 mode is 32 bits. | ||
return this.def.operandSize === operand_1.SIZE.Q; | ||
@@ -22,3 +23,3 @@ }; | ||
if (this.pfxEx) | ||
return false; | ||
return false; // VEX or EVEX already set | ||
if (this.def.rex) | ||
@@ -28,5 +29,9 @@ return true; | ||
return false; | ||
// if(!this.ops.hasRegisterOrMemory()) return false; | ||
if (this.ops.hasExtendedRegister()) | ||
return true; | ||
var _a = this.ops.list, dst = _a[0], src = _a[1]; | ||
// sil, dil, spl, bpl | ||
// if(((dst instanceof o.Register8) && !(dst instanceof o.Register8High) && (dst.id >= r.R8.SPL) && (dst.id <= r.R8.DIL)) || | ||
// ((src instanceof o.Register8) && !(src instanceof o.Register8High) && (src.id >= r.R8.SPL) && (src.id <= r.R8.DIL))) return true; | ||
if ((dst === o.sil) || (dst === o.dil) || (dst === o.spl) || (dst === o.bpl) || | ||
@@ -55,3 +60,3 @@ (src === o.sil) || (src === o.dil) || (src === o.spl) || (src === o.bpl)) | ||
if (pos > -1) { | ||
var m = this.ops.getMemoryOperand(); | ||
var m = this.ops.getMemoryOperand(); // Memory operand is only one. | ||
if (m) { | ||
@@ -85,2 +90,9 @@ if (m.base && (m.base.idSize() > 3)) | ||
}; | ||
// Adding RIP-relative addressing in long mode. | ||
// | ||
// > In the 64-bit mode, any instruction that uses ModRM addressing can use RIP-relative addressing. | ||
// | ||
// > Without RIP-relative addressing, ModRM instructions address memory relative to zero. With RIP-relative | ||
// > addressing, ModRM instructions can address memory relative to the 64-bit RIP using a signed | ||
// > 32-bit displacement. | ||
Instruction.prototype.createModrm = function () { | ||
@@ -91,2 +103,3 @@ var mem = this.ops.getMemoryOperand(); | ||
throw TypeError('RIP-relative addressing does not support index and scale addressing.'); | ||
// Encode `Modrm.reg` field. | ||
var reg = 0; | ||
@@ -118,2 +131,3 @@ if (this.def.opreg > -1) { | ||
if (mem && (typeof mem == 'object') && (mem.base instanceof o.RegisterRip)) { | ||
// RIP-relative addressing has always 4-byte displacement. | ||
if (!mem.displacement) | ||
@@ -120,0 +134,0 @@ mem.disp(0); |
Sorry, the diff of this file is too big to display
582185
13734