github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/asm/amd64/impl_staticconst_test.go (about) 1 package amd64 2 3 import ( 4 "encoding/hex" 5 "testing" 6 7 "github.com/wasilibs/wazerox/internal/asm" 8 "github.com/wasilibs/wazerox/internal/testing/require" 9 ) 10 11 func TestAssemblerImpl_CompileStaticConstToRegister(t *testing.T) { 12 a := NewAssembler() 13 t.Run("odd count of bytes", func(t *testing.T) { 14 err := a.CompileStaticConstToRegister(MOVDQU, asm.NewStaticConst([]byte{1}), RegAX) 15 require.Error(t, err) 16 }) 17 t.Run("ok", func(t *testing.T) { 18 cons := asm.NewStaticConst([]byte{1, 2, 3, 4}) 19 err := a.CompileStaticConstToRegister(MOVDQU, cons, RegAX) 20 require.NoError(t, err) 21 actualNode := a.current 22 require.Equal(t, MOVDQU, actualNode.instruction) 23 require.Equal(t, operandTypesStaticConstToRegister, actualNode.types) 24 require.Equal(t, cons, actualNode.staticConst) 25 }) 26 } 27 28 func TestAssemblerImpl_CompileRegisterToStaticConst(t *testing.T) { 29 a := NewAssembler() 30 t.Run("odd count of bytes", func(t *testing.T) { 31 err := a.CompileRegisterToStaticConst(MOVDQU, RegAX, asm.NewStaticConst([]byte{1})) 32 require.Error(t, err) 33 }) 34 t.Run("ok", func(t *testing.T) { 35 cons := asm.NewStaticConst([]byte{1, 2, 3, 4}) 36 err := a.CompileRegisterToStaticConst(MOVDQU, RegAX, cons) 37 require.NoError(t, err) 38 actualNode := a.current 39 require.Equal(t, MOVDQU, actualNode.instruction) 40 require.Equal(t, operandTypesRegisterToStaticConst, actualNode.types) 41 require.Equal(t, cons, actualNode.staticConst) 42 }) 43 } 44 45 func TestAssemblerImpl_maybeFlushConstants(t *testing.T) { 46 t.Run("no consts", func(t *testing.T) { 47 code := asm.CodeSegment{} 48 defer func() { require.NoError(t, code.Unmap()) }() 49 50 a := NewAssembler() 51 // Invoking maybeFlushConstants before encoding consts usage should not panic. 52 a.maybeFlushConstants(code.NextCodeSection(), false) 53 a.maybeFlushConstants(code.NextCodeSection(), true) 54 }) 55 56 largeData := make([]byte, 256) 57 58 tests := []struct { 59 name string 60 endOfFunction bool 61 dummyBodyBeforeFlush []byte 62 firstUseOffsetInBinary uint64 63 consts [][]byte 64 expectedOffsetForConsts []uint64 65 exp []byte 66 maxDisplacement int 67 }{ 68 { 69 name: "end of function", 70 endOfFunction: true, 71 dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, 72 consts: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}}, 73 expectedOffsetForConsts: []uint64{4, 4 + 8}, // 4 = len(dummyBodyBeforeFlush) 74 firstUseOffsetInBinary: 0, 75 exp: []byte{'?', '?', '?', '?', 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13}, 76 maxDisplacement: 1 << 31, // large displacement will emit the consts at the end of function. 77 }, 78 { 79 name: "not flush", 80 endOfFunction: false, 81 dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, 82 consts: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}}, 83 firstUseOffsetInBinary: 0, 84 exp: []byte{'?', '?', '?', '?'}, 85 maxDisplacement: 1 << 31, // large displacement will emit the consts at the end of function. 86 }, 87 { 88 name: "not end of function but flush - short jump", 89 endOfFunction: false, 90 dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, 91 consts: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}}, 92 expectedOffsetForConsts: []uint64{4 + 2, 4 + 2 + 8}, // 4 = len(dummyBodyBeforeFlush), 2 = the size of jump 93 firstUseOffsetInBinary: 0, 94 exp: []byte{ 95 '?', '?', '?', '?', 96 0xeb, 0x0c, // short jump with offset = len(consts[0]) + len(consts[1]) = 12 = 0xc. 97 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 98 }, 99 maxDisplacement: 0, // small displacement flushes the const immediately, not at the end of function. 100 }, 101 { 102 name: "not end of function but flush - long jump", 103 endOfFunction: false, 104 dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, 105 consts: [][]byte{largeData}, 106 expectedOffsetForConsts: []uint64{4 + 5}, // 4 = len(dummyBodyBeforeFlush), 5 = the size of jump 107 firstUseOffsetInBinary: 0, 108 exp: append([]byte{ 109 '?', '?', '?', '?', 110 0xe9, 0x0, 0x1, 0x0, 0x0, // short jump with offset = 256 = 0x0, 0x1, 0x0, 0x0 (in Little Endian). 111 }, largeData...), 112 maxDisplacement: 0, // small displacement flushes the const immediately, not at the end of function. 113 }, 114 } 115 116 for _, tc := range tests { 117 t.Run(tc.name, func(t *testing.T) { 118 code := asm.CodeSegment{} 119 defer func() { require.NoError(t, code.Unmap()) }() 120 121 a := NewAssembler() 122 a.MaxDisplacementForConstantPool = tc.maxDisplacement 123 124 buf := code.NextCodeSection() 125 buf.AppendBytes(tc.dummyBodyBeforeFlush) 126 127 for i, c := range tc.consts { 128 sc := asm.NewStaticConst(c) 129 a.pool.AddConst(sc, 100) 130 i := i 131 sc.AddOffsetFinalizedCallback(func(offsetOfConstInBinary uint64) { 132 require.Equal(t, tc.expectedOffsetForConsts[i], offsetOfConstInBinary) 133 }) 134 } 135 136 a.pool.FirstUseOffsetInBinary = tc.firstUseOffsetInBinary 137 a.maybeFlushConstants(buf, tc.endOfFunction) 138 139 require.Equal(t, tc.exp, buf.Bytes()) 140 }) 141 } 142 } 143 144 func TestAssemblerImpl_encodeRegisterToStaticConst(t *testing.T) { 145 tests := []struct { 146 name string 147 ins asm.Instruction 148 c []byte 149 reg asm.Register 150 ud2sBeforeConst int 151 exp []byte 152 }{ 153 { 154 name: "cmp r12d, dword ptr [rip + 0x14]", 155 ins: CMPL, 156 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 157 reg: RegR12, 158 ud2sBeforeConst: 10, 159 exp: []byte{ 160 // cmp r12d, dword ptr [rip + 0x14] 161 // where rip = 0x7, therefore [rip + 0x14] = [0x1b] 162 0x44, 0x3b, 0x25, 0x14, 0x0, 0x0, 0x0, 163 // UD2 * ud2sBeforeConst 164 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 165 // 0x1b: consts 166 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 167 }, 168 }, 169 { 170 name: "cmp eax, dword ptr [rip + 0x14]", 171 ins: CMPL, 172 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 173 reg: RegAX, 174 ud2sBeforeConst: 10, 175 exp: []byte{ 176 // cmp eax, dword ptr [rip + 0x14] 177 // where rip = 0x6, therefore [rip + 0x14] = [0x1a] 178 0x3b, 0x5, 0x14, 0x0, 0x0, 0x0, 179 // UD2 * ud2sBeforeConst 180 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 181 // 0x1a: consts 182 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 183 }, 184 }, 185 { 186 name: "cmp r12, qword ptr [rip]", 187 ins: CMPQ, 188 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 189 reg: RegR12, 190 ud2sBeforeConst: 0, 191 exp: []byte{ 192 // cmp r12, qword ptr [rip] 193 // where rip points to the end of this instruction == the const. 194 0x4c, 0x3b, 0x25, 0x0, 0x0, 0x0, 0x0, 195 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 196 }, 197 }, 198 { 199 name: "cmp rsp, qword ptr [rip + 0xa]", 200 ins: CMPQ, 201 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 202 reg: RegSP, 203 ud2sBeforeConst: 5, 204 exp: []byte{ 205 // cmp rsp, qword ptr [rip + 0xa] 206 // where rip = 0x6, therefore [rip + 0xa] = [0x11] 207 0x48, 0x3b, 0x25, 0xa, 0x0, 0x0, 0x0, 208 // UD2 * ud2sBeforeConst 209 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 210 // 0x11: 211 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 212 }, 213 }, 214 } 215 216 for _, tc := range tests { 217 tc := tc 218 t.Run(tc.name, func(t *testing.T) { 219 code := asm.CodeSegment{} 220 defer func() { require.NoError(t, code.Unmap()) }() 221 222 a := NewAssembler() 223 224 err := a.CompileRegisterToStaticConst(tc.ins, tc.reg, asm.NewStaticConst(tc.c)) 225 require.NoError(t, err) 226 227 for i := 0; i < tc.ud2sBeforeConst; i++ { 228 a.CompileStandAlone(UD2) 229 } 230 231 buf := code.NextCodeSection() 232 err = a.Assemble(buf) 233 require.NoError(t, err) 234 235 actual := buf.Bytes() 236 require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) 237 }) 238 } 239 } 240 241 func TestAssemblerImpl_encodeStaticConstToRegister(t *testing.T) { 242 tests := []struct { 243 name string 244 ins asm.Instruction 245 c []byte 246 reg asm.Register 247 ud2sBeforeConst int 248 exp []byte 249 }{ 250 { 251 name: "movdqu xmm14, xmmword ptr [rip + 0xa]", 252 ins: MOVDQU, 253 c: []byte{ 254 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 255 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 256 }, 257 reg: RegX14, 258 ud2sBeforeConst: 5, 259 exp: []byte{ 260 // movdqu xmm14, xmmword ptr [rip + 0xa] 261 // where rip = 0x9, therefore [rip + 0xa] = [0x13] 262 0xf3, 0x44, 0xf, 0x6f, 0x35, 0xa, 0x0, 0x0, 0x0, 263 // UD2 * ud2sBeforeConst 264 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 265 // 0x13: 266 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 267 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 268 }, 269 }, 270 { 271 name: "movupd xmm1, xmmword ptr [rip + 0xa]", 272 ins: MOVUPD, 273 c: []byte{ 274 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 275 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 276 }, 277 reg: RegX1, 278 ud2sBeforeConst: 5, 279 exp: []byte{ 280 // movdqu xmm14, xmmword ptr [rip + 0xa] 281 // where rip = 0x8, therefore [rip + 0xa] = [0x12] 282 0x66, 0xf, 0x10, 0xd, 0xa, 0x0, 0x0, 0x0, 283 // UD2 * ud2sBeforeConst 284 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 285 // 0x12: 286 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 287 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 288 }, 289 }, 290 { 291 name: "lea r11, [rip + 0x14]", 292 ins: LEAQ, 293 c: []byte{ 294 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 295 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 296 }, 297 reg: RegR11, 298 ud2sBeforeConst: 10, 299 exp: []byte{ 300 // lea r11, [rip + 0x14] 301 // where rip = 0x7, therefore [rip + 0x14] = [0x1b] 302 0x4c, 0x8d, 0x1d, 0x14, 0x0, 0x0, 0x0, 303 // UD2 * ud2sBeforeConst 304 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 305 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 306 // 0x1b: 307 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 308 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 309 }, 310 }, 311 { 312 name: "mov r11d, dword ptr [rip + 0x3c]", 313 ins: MOVL, 314 c: []byte{ 315 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 316 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 317 }, 318 reg: RegR11, 319 ud2sBeforeConst: 30, 320 exp: []byte{ 321 // mov r11d, dword ptr [rip + 0x3c] 322 // where rip = 0x7, therefore [rip + 0x3c] = [0x43] 323 0x44, 0x8b, 0x1d, 0x3c, 0x0, 0x0, 0x0, 324 // UD2 * ud2sBeforeConst 325 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 326 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 327 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 328 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 329 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 330 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 331 // 0x43: 332 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 333 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 334 }, 335 }, 336 { 337 name: "movd xmm14, dword ptr [rip + 0x3c]", 338 ins: MOVL, 339 c: []byte{ 340 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 341 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 342 }, 343 reg: RegX14, 344 ud2sBeforeConst: 30, 345 exp: []byte{ 346 // movd xmm14, dword ptr [rip + 0x3c] 347 // where rip = 0x9, therefore [rip + 0x3c] = [0x45] 348 0x66, 0x44, 0xf, 0x6e, 0x35, 0x3c, 0x0, 0x0, 0x0, 349 // UD2 * ud2sBeforeConst 350 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 351 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 352 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 353 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 354 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 355 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 356 // 0x45: 357 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 358 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 359 }, 360 }, 361 { 362 name: "mov rsp, qword ptr [rip + 0x3c]", 363 ins: MOVQ, 364 c: []byte{ 365 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 366 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 367 }, 368 reg: RegSP, 369 ud2sBeforeConst: 30, 370 exp: []byte{ 371 // mov rsp, qword ptr [rip + 0x3c] 372 // where rip = 0x7, therefore [rip + 0x3c] = [0x43] 373 0x48, 0x8b, 0x25, 0x3c, 0x0, 0x0, 0x0, 374 // UD2 * ud2sBeforeConst 375 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 376 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 377 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 378 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 379 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 380 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 381 // 0x43: 382 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 383 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 384 }, 385 }, 386 { 387 name: "movq xmm1, qword ptr [rip + 0x3c]", 388 ins: MOVQ, 389 c: []byte{ 390 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 391 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 392 }, 393 reg: RegX1, 394 ud2sBeforeConst: 30, 395 exp: []byte{ 396 // movq xmm1, qword ptr [rip + 0x3c] 397 // where rip = 0x8, therefore [rip + 0x3c] = [0x44] 398 0xf3, 0xf, 0x7e, 0xd, 0x3c, 0x0, 0x0, 0x0, 399 // UD2 * ud2sBeforeConst 400 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 401 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 402 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 403 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 404 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 405 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 406 // 0x44: 407 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 408 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 409 }, 410 }, 411 { 412 name: "ucomisd xmm15, qword ptr [rip + 6]", 413 ins: UCOMISD, 414 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 415 reg: RegX15, 416 ud2sBeforeConst: 3, 417 exp: []byte{ 418 // ucomisd xmm15, qword ptr [rip + 6] 419 // where rip = 0x9, therefore [rip + 6] = [0xf] 420 0x66, 0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0, 421 // UD2 * ud2sBeforeConst 422 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 423 // 0xf: 424 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 425 }, 426 }, 427 { 428 name: "ucomiss xmm15, dword ptr [rip + 6]", 429 ins: UCOMISS, 430 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 431 reg: RegX15, 432 ud2sBeforeConst: 3, 433 exp: []byte{ 434 // ucomiss xmm15, dword ptr [rip + 6] 435 // where rip = 0x8, therefore [rip + 6] = [0xe] 436 0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0, 437 // UD2 * ud2sBeforeConst 438 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 439 // 0xe: 440 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 441 }, 442 }, 443 { 444 name: "subss xmm13, dword ptr [rip + 0xa]", 445 ins: SUBSS, 446 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 447 reg: RegX13, 448 ud2sBeforeConst: 5, 449 exp: []byte{ 450 // subss xmm13, dword ptr [rip + 0xa] 451 // where rip = 0x9, therefore [rip + 0xa] = [0x13] 452 0xf3, 0x44, 0xf, 0x5c, 0x2d, 0xa, 0x0, 0x0, 0x0, 453 // UD2 * ud2sBeforeConst 454 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 455 // 0x12: 456 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 457 }, 458 }, 459 { 460 name: "subsd xmm1, qword ptr [rip + 0xa]", 461 ins: SUBSD, 462 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 463 reg: RegX1, 464 ud2sBeforeConst: 5, 465 exp: []byte{ 466 // subsd xmm1, qword ptr [rip + 0xa] 467 // where rip = 0x8, therefore [rip + 0xa] = [0x12] 468 0xf2, 0xf, 0x5c, 0xd, 0xa, 0x0, 0x0, 0x0, 469 // UD2 * ud2sBeforeConst 470 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 471 // 0x12: 472 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 473 }, 474 }, 475 { 476 name: "cmp dword ptr [rip + 0x14], r12d", 477 ins: CMPL, 478 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 479 reg: RegR12, 480 ud2sBeforeConst: 10, 481 exp: []byte{ 482 // cmp dword ptr [rip + 0x14], r12d 483 // where rip = 0x7, therefore [rip + 0x14] = [0x1b] 484 0x44, 0x39, 0x25, 0x14, 0x0, 0x0, 0x0, 485 // UD2 * ud2sBeforeConst 486 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 487 // 0x1b: consts 488 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 489 }, 490 }, 491 { 492 name: "cmp dword ptr [rip + 0x14], eax", 493 ins: CMPL, 494 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 495 reg: RegAX, 496 ud2sBeforeConst: 10, 497 exp: []byte{ 498 // cmp dword ptr [rip + 0x14], eax 499 // where rip = 0x6, therefore [rip + 0x14] = [0x1a] 500 0x39, 0x5, 0x14, 0x0, 0x0, 0x0, 501 // UD2 * ud2sBeforeConst 502 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 503 // 0x1a: consts 504 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 505 }, 506 }, 507 { 508 name: "cmp qword ptr [rip], r12", 509 ins: CMPQ, 510 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 511 reg: RegR12, 512 ud2sBeforeConst: 0, 513 exp: []byte{ 514 // cmp qword ptr [rip], r12 515 // where rip points to the end of this instruction == the const. 516 0x4c, 0x39, 0x25, 0x0, 0x0, 0x0, 0x0, 517 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 518 }, 519 }, 520 { 521 name: "cmp qword ptr [rip + 0xa], rsp", 522 ins: CMPQ, 523 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 524 reg: RegSP, 525 ud2sBeforeConst: 5, 526 exp: []byte{ 527 // cmp qword ptr [rip + 0xa], rsp 528 // where rip = 0x6, therefore [rip + 0xa] = [0x11] 529 0x48, 0x39, 0x25, 0xa, 0x0, 0x0, 0x0, 530 // UD2 * ud2sBeforeConst 531 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 532 // 0x11: 533 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 534 }, 535 }, 536 { 537 name: "ucomiss xmm15, dword ptr [rip + 6]", 538 ins: UCOMISS, 539 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 540 reg: RegX15, 541 ud2sBeforeConst: 3, 542 exp: []byte{ 543 // ucomiss xmm15, dword ptr [rip + 6] 544 // where rip = 0x8, therefore [rip + 6] = [0xe] 545 0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0, 546 // UD2 * ud2sBeforeConst 547 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 548 // 0xe: 549 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 550 }, 551 }, 552 { 553 name: "subss xmm13, dword ptr [rip + 0xa]", 554 ins: SUBSS, 555 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 556 reg: RegX13, 557 ud2sBeforeConst: 5, 558 exp: []byte{ 559 // subss xmm13, dword ptr [rip + 0xa] 560 // where rip = 0x9, therefore [rip + 0xa] = [0x13] 561 0xf3, 0x44, 0xf, 0x5c, 0x2d, 0xa, 0x0, 0x0, 0x0, 562 // UD2 * ud2sBeforeConst 563 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 564 // 0x12: 565 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 566 }, 567 }, 568 { 569 name: "subsd xmm1, qword ptr [rip + 0xa]", 570 ins: SUBSD, 571 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 572 reg: RegX1, 573 ud2sBeforeConst: 5, 574 exp: []byte{ 575 // subsd xmm1, qword ptr [rip + 0xa] 576 // where rip = 0x8, therefore [rip + 0xa] = [0x12] 577 0xf2, 0xf, 0x5c, 0xd, 0xa, 0x0, 0x0, 0x0, 578 // UD2 * ud2sBeforeConst 579 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 580 // 0x12: 581 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 582 }, 583 }, 584 { 585 name: "add eax, dword ptr [rip + 0xa]", 586 ins: ADDL, 587 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 588 reg: RegAX, 589 ud2sBeforeConst: 5, 590 exp: []byte{ 591 // add eax, dword ptr [rip + 0xa] 592 // where rip = 0x6, therefore [rip + 0xa] = [0x10] 593 0x3, 0x5, 0xa, 0x0, 0x0, 0x0, 594 // UD2 * ud2sBeforeConst 595 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 596 // 0x10: 597 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 598 }, 599 }, 600 { 601 name: "add rax, qword ptr [rip + 0xa]", 602 ins: ADDQ, 603 c: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 604 reg: RegAX, 605 ud2sBeforeConst: 5, 606 exp: []byte{ 607 // add rax, dword ptr [rip + 0xa] 608 // where rip = 0x7, therefore [rip + 0xa] = [0x11] 609 0x48, 0x3, 0x5, 0xa, 0x0, 0x0, 0x0, 610 // UD2 * ud2sBeforeConst 611 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 612 // 0x11: 613 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 614 }, 615 }, 616 } 617 618 for _, tc := range tests { 619 tc := tc 620 t.Run(tc.name, func(t *testing.T) { 621 code := asm.CodeSegment{} 622 defer func() { require.NoError(t, code.Unmap()) }() 623 624 a := NewAssembler() 625 626 err := a.CompileStaticConstToRegister(tc.ins, asm.NewStaticConst(tc.c), tc.reg) 627 require.NoError(t, err) 628 629 for i := 0; i < tc.ud2sBeforeConst; i++ { 630 a.CompileStandAlone(UD2) 631 } 632 633 buf := code.NextCodeSection() 634 err = a.Assemble(buf) 635 require.NoError(t, err) 636 637 actual := buf.Bytes() 638 require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) 639 }) 640 } 641 }