Signed-off-by: Achille Roussel <achille.roussel@gmail.com> Co-authored-by: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com>
642 lines
20 KiB
Go
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))
|
|
})
|
|
}
|
|
}
|