// Copyright 2017 syzkaller project authors. All rights reserved.
|
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
|
// See Intel Software Developer’s Manual Volume 2: Instruction Set Reference
|
// and AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions
|
// for details of instruction encoding.
|
|
package ifuzz
|
|
import (
|
"math/rand"
|
)
|
|
// nolint: gocyclo
|
func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
|
if !insn.isCompatible(cfg) {
|
panic("instruction is not suitable for this mode")
|
}
|
if insn.Pseudo {
|
return insn.generator(cfg, r)
|
}
|
|
var operSize, immSize, dispSize, addrSize int
|
switch cfg.Mode {
|
case ModeLong64:
|
operSize, immSize, dispSize, addrSize = 4, 4, 4, 8
|
case ModeProt32:
|
operSize, immSize, dispSize, addrSize = 4, 4, 4, 4
|
case ModeProt16, ModeReal16:
|
operSize, immSize, dispSize, addrSize = 2, 2, 2, 2
|
default:
|
panic("bad mode")
|
}
|
|
var code []byte
|
|
rexR := false
|
var vvvv, vexR, vexX, vexB byte
|
|
// LEGACY PREFIXES
|
if insn.Vex == 0 {
|
for r.Intn(3) == 0 {
|
// LOCK 0xF0 is always added to insn.Prefix
|
prefixes := []byte{
|
0x2E, // CS
|
0x3E, // DS
|
0x26, // ES
|
0x64, // FS
|
0x65, // GS
|
0x36, // SS
|
}
|
if !insn.No66Prefix {
|
prefixes = append(prefixes, 0x66) // operand size
|
}
|
if cfg.Mode == ModeLong64 || !insn.Mem32 {
|
prefixes = append(prefixes, 0x67) // address size
|
}
|
if !insn.NoRepPrefix {
|
prefixes = append(prefixes,
|
0xF3, // REP
|
0xF2, // REPNE
|
)
|
}
|
pref := prefixes[r.Intn(len(prefixes))]
|
code = append(code, pref)
|
}
|
|
code = append(code, insn.Prefix...)
|
|
// REX
|
var rex byte
|
if cfg.Mode == ModeLong64 && r.Intn(2) == 0 {
|
// bit 0 - B
|
// bit 1 - X
|
// bit 2 - R
|
// bit 3 - W
|
rex = byte(0x40 | r.Intn(16))
|
if insn.Rexw == 1 {
|
rex |= 1 << 3
|
} else {
|
rex &^= 1 << 3
|
}
|
rexR = rex&0x4 != 0
|
code = append(code, rex)
|
}
|
|
operSize1, immSize1, dispSize1, addrSize1 := operSize, immSize, dispSize, addrSize
|
for _, pref := range code {
|
switch pref {
|
case 0x66:
|
if immSize == 4 {
|
immSize1 = 2
|
operSize1 = 2
|
} else if immSize == 2 {
|
immSize1 = 4
|
operSize1 = 4
|
}
|
case 0x67:
|
if addrSize == 8 {
|
addrSize1 = 4
|
} else if addrSize == 4 {
|
dispSize1 = 2
|
addrSize1 = 2
|
} else if addrSize == 2 {
|
dispSize1 = 4
|
addrSize1 = 4
|
}
|
}
|
if rex&(1<<3) != 0 {
|
operSize1 = 8
|
immSize1 = 4
|
}
|
}
|
operSize, immSize, dispSize, addrSize = operSize1, immSize1, dispSize1, addrSize1
|
} else {
|
// VEX/VOP
|
code = append(code, insn.Vex)
|
vexR = byte(1)
|
vexX = byte(1)
|
if cfg.Mode == ModeLong64 {
|
vexR = byte(r.Intn(2))
|
vexX = byte(r.Intn(2))
|
}
|
vexB = byte(r.Intn(2))
|
W := byte(r.Intn(2))
|
if insn.Rexw == 1 {
|
W = 1
|
} else if insn.Rexw == -1 {
|
W = 0
|
}
|
L := byte(r.Intn(2))
|
if insn.VexL == 1 {
|
L = 1
|
} else if insn.VexL == -1 {
|
L = 0
|
}
|
pp := byte(r.Intn(4))
|
if insn.VexP != -1 {
|
pp = byte(insn.VexP)
|
}
|
vvvv = 15
|
if !insn.VexNoR {
|
vvvv = byte(r.Intn(16))
|
}
|
code = append(code, vexR<<7|vexX<<6|vexB<<5|insn.VexMap)
|
code = append(code, W<<7|vvvv<<3|L<<2|pp)
|
// TODO: short encoding
|
if cfg.Mode != ModeLong64 {
|
vvvv |= 8
|
}
|
}
|
|
// OPCODE
|
code = append(code, insn.Opcode...)
|
|
if insn.Srm {
|
rm := byte(insn.Rm)
|
if insn.Rm == -1 {
|
rm = byte(r.Intn(8))
|
}
|
code[len(code)-1] |= rm
|
} else if insn.Modrm {
|
// MODRM
|
var mod byte
|
switch insn.Mod {
|
case 0, 1, 2, 3:
|
mod = byte(insn.Mod)
|
case -1:
|
mod = byte(r.Intn(4))
|
case -3:
|
mod = byte(r.Intn(3))
|
}
|
|
reg := byte(insn.Reg)
|
if insn.Reg == -1 {
|
reg = byte(r.Intn(8))
|
} else if insn.Reg == -6 {
|
reg = byte(r.Intn(6)) // segment register
|
} else if insn.Reg == -8 {
|
if rexR {
|
reg = 0 // CR8
|
} else {
|
crs := []byte{0, 2, 3, 4}
|
reg = crs[r.Intn(len(crs))]
|
}
|
}
|
if insn.Avx2Gather {
|
if reg|(1-vexR)<<3 == vvvv^0xf {
|
reg = (reg + 1) & 7
|
}
|
}
|
|
rm := byte(insn.Rm)
|
if insn.Rm == -1 {
|
rm = byte(r.Intn(8))
|
}
|
|
modrm := mod<<6 | reg<<3 | rm
|
code = append(code, modrm)
|
|
if !insn.NoSibDisp {
|
if addrSize == 2 {
|
if mod == 1 {
|
// disp8
|
code = append(code, generateArg(cfg, r, 1)...)
|
} else if mod == 2 || mod == 0 && rm == 6 {
|
// disp16
|
code = append(code, generateArg(cfg, r, 2)...)
|
}
|
} else {
|
var sibbase byte
|
if mod != 3 && rm == 4 {
|
// SIB
|
scale := byte(r.Intn(4))
|
index := byte(r.Intn(8))
|
sibbase = byte(r.Intn(8))
|
if insn.Avx2Gather {
|
rrrr := reg | (1-vexR)<<3
|
for {
|
iiii := index | (1-vexX)<<3
|
if iiii != vvvv^0xf && iiii != rrrr {
|
break
|
}
|
index = (index + 1) & 7
|
}
|
}
|
sib := scale<<6 | index<<3 | sibbase
|
code = append(code, sib)
|
}
|
|
if mod == 1 {
|
// disp8
|
code = append(code, generateArg(cfg, r, 1)...)
|
} else if mod == 2 || mod == 0 && rm == 5 || mod == 0 && sibbase == 5 {
|
// disp16/32
|
code = append(code, generateArg(cfg, r, dispSize)...)
|
}
|
}
|
}
|
}
|
|
addImm := func(imm int) {
|
if imm == -1 {
|
imm = immSize
|
} else if imm == -2 {
|
imm = addrSize
|
} else if imm == -3 {
|
imm = operSize
|
}
|
if imm != 0 {
|
code = append(code, generateArg(cfg, r, imm)...)
|
}
|
}
|
addImm(int(insn.Imm))
|
addImm(int(insn.Imm2))
|
|
code = append(code, insn.Suffix...)
|
return code
|
}
|