Files
wazero/internal/asm/amd64/impl_staticconst_test.go
2023-05-19 07:06:30 +02:00

642 lines
20 KiB
Go

package amd64
import (
"encoding/hex"
"testing"
"github.com/tetratelabs/wazero/internal/asm"
"github.com/tetratelabs/wazero/internal/testing/require"
)
func TestAssemblerImpl_CompileStaticConstToRegister(t *testing.T) {
a := NewAssembler()
t.Run("odd count of bytes", func(t *testing.T) {
err := a.CompileStaticConstToRegister(MOVDQU, asm.NewStaticConst([]byte{1}), RegAX)
require.Error(t, err)
})
t.Run("ok", func(t *testing.T) {
cons := asm.NewStaticConst([]byte{1, 2, 3, 4})
err := a.CompileStaticConstToRegister(MOVDQU, cons, RegAX)
require.NoError(t, err)
actualNode := a.current
require.Equal(t, MOVDQU, actualNode.instruction)
require.Equal(t, operandTypesStaticConstToRegister, actualNode.types)
require.Equal(t, cons, actualNode.staticConst)
})
}
func TestAssemblerImpl_CompileRegisterToStaticConst(t *testing.T) {
a := NewAssembler()
t.Run("odd count of bytes", func(t *testing.T) {
err := a.CompileRegisterToStaticConst(MOVDQU, RegAX, asm.NewStaticConst([]byte{1}))
require.Error(t, err)
})
t.Run("ok", func(t *testing.T) {
cons := asm.NewStaticConst([]byte{1, 2, 3, 4})
err := a.CompileRegisterToStaticConst(MOVDQU, RegAX, cons)
require.NoError(t, err)
actualNode := a.current
require.Equal(t, MOVDQU, actualNode.instruction)
require.Equal(t, operandTypesRegisterToStaticConst, actualNode.types)
require.Equal(t, cons, actualNode.staticConst)
})
}
func TestAssemblerImpl_maybeFlushConstants(t *testing.T) {
t.Run("no consts", func(t *testing.T) {
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
a := NewAssembler()
// Invoking maybeFlushConstants before encoding consts usage should not panic.
a.maybeFlushConstants(code.NextCodeSection(), false)
a.maybeFlushConstants(code.NextCodeSection(), true)
})
largeData := make([]byte, 256)
tests := []struct {
name string
endOfFunction bool
dummyBodyBeforeFlush []byte
firstUseOffsetInBinary uint64
consts [][]byte
expectedOffsetForConsts []uint64
exp []byte
maxDisplacement int
}{
{
name: "end of function",
endOfFunction: true,
dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'},
consts: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}},
expectedOffsetForConsts: []uint64{4, 4 + 8}, // 4 = len(dummyBodyBeforeFlush)
firstUseOffsetInBinary: 0,
exp: []byte{'?', '?', '?', '?', 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13},
maxDisplacement: 1 << 31, // large displacement will emit the consts at the end of function.
},
{
name: "not flush",
endOfFunction: false,
dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'},
consts: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}},
firstUseOffsetInBinary: 0,
exp: []byte{'?', '?', '?', '?'},
maxDisplacement: 1 << 31, // large displacement will emit the consts at the end of function.
},
{
name: "not end of function but flush - short jump",
endOfFunction: false,
dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'},
consts: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}},
expectedOffsetForConsts: []uint64{4 + 2, 4 + 2 + 8}, // 4 = len(dummyBodyBeforeFlush), 2 = the size of jump
firstUseOffsetInBinary: 0,
exp: []byte{
'?', '?', '?', '?',
0xeb, 0x0c, // short jump with offset = len(consts[0]) + len(consts[1]) = 12 = 0xc.
1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13,
},
maxDisplacement: 0, // small displacement flushes the const immediately, not at the end of function.
},
{
name: "not end of function but flush - long jump",
endOfFunction: false,
dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'},
consts: [][]byte{largeData},
expectedOffsetForConsts: []uint64{4 + 5}, // 4 = len(dummyBodyBeforeFlush), 5 = the size of jump
firstUseOffsetInBinary: 0,
exp: append([]byte{
'?', '?', '?', '?',
0xe9, 0x0, 0x1, 0x0, 0x0, // short jump with offset = 256 = 0x0, 0x1, 0x0, 0x0 (in Little Endian).
}, largeData...),
maxDisplacement: 0, // small displacement flushes the const immediately, not at the end of function.
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
a := NewAssembler()
a.MaxDisplacementForConstantPool = tc.maxDisplacement
buf := code.NextCodeSection()
buf.AppendBytes(tc.dummyBodyBeforeFlush)
for i, c := range tc.consts {
sc := asm.NewStaticConst(c)
a.pool.AddConst(sc, 100)
i := i
sc.AddOffsetFinalizedCallback(func(offsetOfConstInBinary uint64) {
require.Equal(t, tc.expectedOffsetForConsts[i], offsetOfConstInBinary)
})
}
a.pool.FirstUseOffsetInBinary = tc.firstUseOffsetInBinary
a.maybeFlushConstants(buf, tc.endOfFunction)
require.Equal(t, tc.exp, buf.Bytes())
})
}
}
func TestAssemblerImpl_encodeRegisterToStaticConst(t *testing.T) {
tests := []struct {
name string
ins asm.Instruction
c []byte
reg asm.Register
ud2sBeforeConst int
exp []byte
}{
{
name: "cmp r12d, dword ptr [rip + 0x14]",
ins: CMPL,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegR12,
ud2sBeforeConst: 10,
exp: []byte{
// cmp r12d, dword ptr [rip + 0x14]
// where rip = 0x7, therefore [rip + 0x14] = [0x1b]
0x44, 0x3b, 0x25, 0x14, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x1b: consts
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp eax, dword ptr [rip + 0x14]",
ins: CMPL,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegAX,
ud2sBeforeConst: 10,
exp: []byte{
// cmp eax, dword ptr [rip + 0x14]
// where rip = 0x6, therefore [rip + 0x14] = [0x1a]
0x3b, 0x5, 0x14, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x1a: consts
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp r12, qword ptr [rip]",
ins: CMPQ,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegR12,
ud2sBeforeConst: 0,
exp: []byte{
// cmp r12, qword ptr [rip]
// where rip points to the end of this instruction == the const.
0x4c, 0x3b, 0x25, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp rsp, qword ptr [rip + 0xa]",
ins: CMPQ,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegSP,
ud2sBeforeConst: 5,
exp: []byte{
// cmp rsp, qword ptr [rip + 0xa]
// where rip = 0x6, therefore [rip + 0xa] = [0x11]
0x48, 0x3b, 0x25, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x11:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
a := NewAssembler()
err := a.CompileRegisterToStaticConst(tc.ins, tc.reg, asm.NewStaticConst(tc.c))
require.NoError(t, err)
for i := 0; i < tc.ud2sBeforeConst; i++ {
a.CompileStandAlone(UD2)
}
buf := code.NextCodeSection()
err = a.Assemble(buf)
require.NoError(t, err)
actual := buf.Bytes()
require.Equal(t, tc.exp, actual, hex.EncodeToString(actual))
})
}
}
func TestAssemblerImpl_encodeStaticConstToRegister(t *testing.T) {
tests := []struct {
name string
ins asm.Instruction
c []byte
reg asm.Register
ud2sBeforeConst int
exp []byte
}{
{
name: "movdqu xmm14, xmmword ptr [rip + 0xa]",
ins: MOVDQU,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegX14,
ud2sBeforeConst: 5,
exp: []byte{
// movdqu xmm14, xmmword ptr [rip + 0xa]
// where rip = 0x9, therefore [rip + 0xa] = [0x13]
0xf3, 0x44, 0xf, 0x6f, 0x35, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x13:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "movupd xmm1, xmmword ptr [rip + 0xa]",
ins: MOVUPD,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegX1,
ud2sBeforeConst: 5,
exp: []byte{
// movdqu xmm14, xmmword ptr [rip + 0xa]
// where rip = 0x8, therefore [rip + 0xa] = [0x12]
0x66, 0xf, 0x10, 0xd, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x12:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "lea r11, [rip + 0x14]",
ins: LEAQ,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegR11,
ud2sBeforeConst: 10,
exp: []byte{
// lea r11, [rip + 0x14]
// where rip = 0x7, therefore [rip + 0x14] = [0x1b]
0x4c, 0x8d, 0x1d, 0x14, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x1b:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "mov r11d, dword ptr [rip + 0x3c]",
ins: MOVL,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegR11,
ud2sBeforeConst: 30,
exp: []byte{
// mov r11d, dword ptr [rip + 0x3c]
// where rip = 0x7, therefore [rip + 0x3c] = [0x43]
0x44, 0x8b, 0x1d, 0x3c, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x43:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "movd xmm14, dword ptr [rip + 0x3c]",
ins: MOVL,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegX14,
ud2sBeforeConst: 30,
exp: []byte{
// movd xmm14, dword ptr [rip + 0x3c]
// where rip = 0x9, therefore [rip + 0x3c] = [0x45]
0x66, 0x44, 0xf, 0x6e, 0x35, 0x3c, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x45:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "mov rsp, qword ptr [rip + 0x3c]",
ins: MOVQ,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegSP,
ud2sBeforeConst: 30,
exp: []byte{
// mov rsp, qword ptr [rip + 0x3c]
// where rip = 0x7, therefore [rip + 0x3c] = [0x43]
0x48, 0x8b, 0x25, 0x3c, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x43:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "movq xmm1, qword ptr [rip + 0x3c]",
ins: MOVQ,
c: []byte{
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
reg: RegX1,
ud2sBeforeConst: 30,
exp: []byte{
// movq xmm1, qword ptr [rip + 0x3c]
// where rip = 0x8, therefore [rip + 0x3c] = [0x44]
0xf3, 0xf, 0x7e, 0xd, 0x3c, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x44:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
},
},
{
name: "ucomisd xmm15, qword ptr [rip + 6]",
ins: UCOMISD,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX15,
ud2sBeforeConst: 3,
exp: []byte{
// ucomisd xmm15, qword ptr [rip + 6]
// where rip = 0x9, therefore [rip + 6] = [0xf]
0x66, 0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0xf:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "ucomiss xmm15, dword ptr [rip + 6]",
ins: UCOMISS,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX15,
ud2sBeforeConst: 3,
exp: []byte{
// ucomiss xmm15, dword ptr [rip + 6]
// where rip = 0x8, therefore [rip + 6] = [0xe]
0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0xe:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "subss xmm13, dword ptr [rip + 0xa]",
ins: SUBSS,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX13,
ud2sBeforeConst: 5,
exp: []byte{
// subss xmm13, dword ptr [rip + 0xa]
// where rip = 0x9, therefore [rip + 0xa] = [0x13]
0xf3, 0x44, 0xf, 0x5c, 0x2d, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x12:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "subsd xmm1, qword ptr [rip + 0xa]",
ins: SUBSD,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX1,
ud2sBeforeConst: 5,
exp: []byte{
// subsd xmm1, qword ptr [rip + 0xa]
// where rip = 0x8, therefore [rip + 0xa] = [0x12]
0xf2, 0xf, 0x5c, 0xd, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x12:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp dword ptr [rip + 0x14], r12d",
ins: CMPL,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegR12,
ud2sBeforeConst: 10,
exp: []byte{
// cmp dword ptr [rip + 0x14], r12d
// where rip = 0x7, therefore [rip + 0x14] = [0x1b]
0x44, 0x39, 0x25, 0x14, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x1b: consts
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp dword ptr [rip + 0x14], eax",
ins: CMPL,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegAX,
ud2sBeforeConst: 10,
exp: []byte{
// cmp dword ptr [rip + 0x14], eax
// where rip = 0x6, therefore [rip + 0x14] = [0x1a]
0x39, 0x5, 0x14, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x1a: consts
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp qword ptr [rip], r12",
ins: CMPQ,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegR12,
ud2sBeforeConst: 0,
exp: []byte{
// cmp qword ptr [rip], r12
// where rip points to the end of this instruction == the const.
0x4c, 0x39, 0x25, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "cmp qword ptr [rip + 0xa], rsp",
ins: CMPQ,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegSP,
ud2sBeforeConst: 5,
exp: []byte{
// cmp qword ptr [rip + 0xa], rsp
// where rip = 0x6, therefore [rip + 0xa] = [0x11]
0x48, 0x39, 0x25, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x11:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "ucomiss xmm15, dword ptr [rip + 6]",
ins: UCOMISS,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX15,
ud2sBeforeConst: 3,
exp: []byte{
// ucomiss xmm15, dword ptr [rip + 6]
// where rip = 0x8, therefore [rip + 6] = [0xe]
0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0xe:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "subss xmm13, dword ptr [rip + 0xa]",
ins: SUBSS,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX13,
ud2sBeforeConst: 5,
exp: []byte{
// subss xmm13, dword ptr [rip + 0xa]
// where rip = 0x9, therefore [rip + 0xa] = [0x13]
0xf3, 0x44, 0xf, 0x5c, 0x2d, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x12:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "subsd xmm1, qword ptr [rip + 0xa]",
ins: SUBSD,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegX1,
ud2sBeforeConst: 5,
exp: []byte{
// subsd xmm1, qword ptr [rip + 0xa]
// where rip = 0x8, therefore [rip + 0xa] = [0x12]
0xf2, 0xf, 0x5c, 0xd, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x12:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "add eax, dword ptr [rip + 0xa]",
ins: ADDL,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegAX,
ud2sBeforeConst: 5,
exp: []byte{
// add eax, dword ptr [rip + 0xa]
// where rip = 0x6, therefore [rip + 0xa] = [0x10]
0x3, 0x5, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x10:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
{
name: "add rax, qword ptr [rip + 0xa]",
ins: ADDQ,
c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
reg: RegAX,
ud2sBeforeConst: 5,
exp: []byte{
// add rax, dword ptr [rip + 0xa]
// where rip = 0x7, therefore [rip + 0xa] = [0x11]
0x48, 0x3, 0x5, 0xa, 0x0, 0x0, 0x0,
// UD2 * ud2sBeforeConst
0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
// 0x11:
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
},
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
a := NewAssembler()
err := a.CompileStaticConstToRegister(tc.ins, asm.NewStaticConst(tc.c), tc.reg)
require.NoError(t, err)
for i := 0; i < tc.ud2sBeforeConst; i++ {
a.CompileStandAlone(UD2)
}
buf := code.NextCodeSection()
err = a.Assemble(buf)
require.NoError(t, err)
actual := buf.Bytes()
require.Equal(t, tc.exp, actual, hex.EncodeToString(actual))
})
}
}