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)) }) } }