blob: 4780c4bd86671aba2303f1bd285e2444bf4a7f96 [file] [log] [blame]
// 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
}