UNPKG

x86

Version:

Generates x86_64 native code from assembly instructions

289 lines (236 loc) 10.8 kB
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); } } }