1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gtest/gtest.h"
18
19 #include <sys/mman.h>
20
21 #include <iterator>
22 #include <string>
23
24 #include "berberis/assembler/machine_code.h"
25 #include "berberis/assembler/x86_32.h"
26 #include "berberis/assembler/x86_64.h"
27 #include "berberis/base/bit_util.h"
28 #include "berberis/base/logging.h"
29 #include "berberis/test_utils/scoped_exec_region.h"
30
31 #if defined(__i386__)
32 using CodeEmitter = berberis::x86_32::Assembler;
33 #elif defined(__amd64__)
34 using CodeEmitter = berberis::x86_64::Assembler;
35 #else
36 #error "Unsupported platform"
37 #endif
38
39 namespace berberis {
40
Callee()41 int Callee() {
42 return 239;
43 }
44
FloatFunc(float f1,float f2)45 float FloatFunc(float f1, float f2) {
46 return f1 - f2;
47 }
48
CompareCode(const uint8_t * code_template_begin,const uint8_t * code_template_end,const MachineCode & code)49 inline bool CompareCode(const uint8_t* code_template_begin,
50 const uint8_t* code_template_end,
51 const MachineCode& code) {
52 if ((code_template_end - code_template_begin) != static_cast<intptr_t>(code.install_size())) {
53 ALOGE("Code size mismatch: %zd != %u",
54 code_template_end - code_template_begin,
55 code.install_size());
56 return false;
57 }
58
59 if (memcmp(code_template_begin, code.AddrAs<uint8_t>(0), code.install_size()) != 0) {
60 ALOGE("Code mismatch");
61 MachineCode code2;
62 code2.AddSequence(code_template_begin, code_template_end - code_template_begin);
63 std::string code_str1, code_str2;
64 code.AsString(&code_str1);
65 code2.AsString(&code_str2);
66 ALOGE("assembler generated\n%s\nshall be\n%s", code_str1.c_str(), code_str2.c_str());
67 return false;
68 }
69 return true;
70 }
71
72 #if defined(__i386__)
73
74 namespace x86_32 {
75
AssemblerTest()76 bool AssemblerTest() {
77 MachineCode code;
78 CodeEmitter assembler(&code);
79 assembler.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
80 assembler.CmpXchgl({.base = Assembler::esp, .disp = 4}, Assembler::eax);
81 assembler.Subl(Assembler::esp, 16);
82 assembler.Movl({.base = Assembler::esp}, Assembler::eax);
83 assembler.Push(Assembler::esp);
84 assembler.Push(0xcccccccc);
85 assembler.Pushl({.base = Assembler::esp, .disp = 0x428});
86 assembler.Popl({.base = Assembler::esp, .disp = 0x428});
87 assembler.Movl(Assembler::ecx, 0xcccccccc);
88 assembler.Call(Assembler::ecx);
89 assembler.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 8});
90 assembler.Addl(Assembler::esp, 24);
91 assembler.Ret();
92 assembler.Finalize();
93
94 // clang-format off
95 static const uint8_t code_template[] = {
96 0x8b, 0x44, 0x24, 0x04, // mov 0x4(%esp),%eax
97 0x0f, 0xb1, 0x44, 0x24, 0x04, // cmpxchg 0x4(%esp),%eax
98 0x83, 0xec, 0x10, // sub $16, %esp
99 0x89, 0x04, 0x24, // mov %eax,(%esp)
100 0x54, // push %esp
101 0x68, 0xcc, 0xcc, 0xcc, 0xcc, // push $cccccccc
102 0xff, 0xb4, 0x24, 0x28, 0x04, 0x00, 0x00, // pushl 0x428(%esp)
103 0x8f, 0x84, 0x24, 0x28, 0x04, 0x00, 0x00, // popl 0x428(%esp)
104 0xb9, 0xcc, 0xcc, 0xcc, 0xcc, // mov $cccccccc, %ecx
105 0xff, 0xd1, // call *%ecx
106 0x8b, 0x44, 0x24, 0x08, // mov 0x8(%esp),%eax
107 0x83, 0xc4, 0x18, // add $24, %esp
108 0xc3 // ret
109 };
110 // clang-format on
111
112 if (sizeof(code_template) != code.install_size()) {
113 ALOGE("Code size mismatch: %zu != %u", sizeof(code_template), code.install_size());
114 return false;
115 }
116
117 if (memcmp(code_template, code.AddrAs<uint8_t>(0), code.install_size()) != 0) {
118 ALOGE("Code mismatch");
119 MachineCode code2;
120 code2.Add(code_template);
121 std::string code_str1, code_str2;
122 code.AsString(&code_str1);
123 code2.AsString(&code_str2);
124 ALOGE("assembler generated\n%s\nshall be\n%s", code_str1.c_str(), code_str2.c_str());
125 return false;
126 }
127
128 return true;
129 }
130
LabelTest()131 bool LabelTest() {
132 MachineCode code;
133 CodeEmitter as(&code);
134 Assembler::Label skip, skip2, back, end;
135 as.Call(bit_cast<const void*>(&Callee));
136 as.Jmp(skip);
137 as.Movl(Assembler::eax, 2);
138 as.Bind(&skip);
139 as.Addl(Assembler::eax, 8);
140 as.Jmp(skip2);
141 as.Bind(&back);
142 as.Addl(Assembler::eax, 12);
143 as.Jmp(end);
144 as.Bind(&skip2);
145 as.Jmp(back);
146 as.Bind(&end);
147 as.Ret();
148 as.Finalize();
149
150 ScopedExecRegion exec(&code);
151
152 int result = exec.get<int()>()();
153 return result == 239 + 8 + 12;
154 }
155
CondTest1()156 bool CondTest1() {
157 MachineCode code;
158 CodeEmitter as(&code);
159 as.Movl(Assembler::eax, 0xcccccccc);
160 as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = 4}); // arg1.
161 as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8}); // arg2.
162 as.Cmpl(Assembler::edx, Assembler::ecx);
163 as.Setcc(Assembler::Condition::kEqual, Assembler::eax);
164 as.Ret();
165 as.Finalize();
166
167 ScopedExecRegion exec(&code);
168
169 using TestFunc = uint32_t(int, int);
170 auto target_func = exec.get<TestFunc>();
171 uint32_t result = target_func(1, 2);
172 if (result != 0xcccccc00) {
173 ALOGE("Bug in seteq(not equal): %x", result);
174 return false;
175 }
176 result = target_func(-1, -1);
177 if (result != 0xcccccc01) {
178 ALOGE("Bug in seteq(equal): %x", result);
179 return false;
180 }
181 return true;
182 }
183
CondTest2()184 bool CondTest2() {
185 MachineCode code;
186 CodeEmitter as(&code);
187 as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = 4}); // arg1.
188 as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8}); // arg2.
189 as.Xorl(Assembler::eax, Assembler::eax);
190 as.Testb(Assembler::edx, Assembler::ecx);
191 as.Setcc(Assembler::Condition::kNotZero, Assembler::eax);
192 as.Xchgl(Assembler::eax, Assembler::ecx);
193 as.Xchgl(Assembler::ecx, Assembler::eax);
194 as.Ret();
195 as.Finalize();
196
197 ScopedExecRegion exec(&code);
198
199 using TestFunc = uint32_t(int, int);
200 auto target_func = exec.get<TestFunc>();
201 uint32_t result = target_func(0x11, 1);
202 if (result != 0x1) {
203 ALOGE("Bug in testb(not zero): %x", result);
204 return false;
205 }
206 result = target_func(0x11, 0x8);
207 if (result != 0x0) {
208 ALOGE("Bug in testb(zero): %x", result);
209 return false;
210 }
211 return true;
212 }
213
JccTest()214 bool JccTest() {
215 MachineCode code;
216 CodeEmitter as(&code);
217 Assembler::Label equal, above, below, done;
218 as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = 4}); // arg1.
219 as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8}); // arg2.
220 as.Cmpl(Assembler::edx, Assembler::ecx);
221 as.Jcc(Assembler::Condition::kEqual, equal);
222 as.Jcc(Assembler::Condition::kBelow, below);
223 as.Jcc(Assembler::Condition::kAbove, above);
224
225 as.Movl(Assembler::eax, 13);
226 as.Jmp(done);
227
228 as.Bind(&equal);
229 as.Movl(Assembler::eax, 0u);
230 as.Jmp(done);
231
232 as.Bind(&below);
233 as.Movl(Assembler::eax, -1);
234 as.Jmp(done);
235
236 as.Bind(&above);
237 as.Movl(Assembler::eax, 1);
238 as.Jmp(done);
239
240 as.Bind(&done);
241 as.Ret();
242 as.Finalize();
243
244 ScopedExecRegion exec(&code);
245
246 using TestFunc = int(int, int);
247 auto target_func = exec.get<TestFunc>();
248 int result = target_func(1, 1);
249 if (result != 0) {
250 ALOGE("Bug in jcc(equal): %x", result);
251 return false;
252 }
253 result = target_func(1, 0);
254 if (result != 1) {
255 ALOGE("Bug in jcc(above): %x", result);
256 return false;
257 }
258 result = target_func(0, 1);
259 if (result != -1) {
260 ALOGE("Bug in jcc(below): %x", result);
261 return false;
262 }
263 return true;
264 }
265
ShiftTest()266 bool ShiftTest() {
267 MachineCode code;
268 CodeEmitter as(&code);
269 as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
270 as.Shll(Assembler::eax, int8_t{2});
271 as.Shrl(Assembler::eax, int8_t{1});
272 as.Movl(Assembler::ecx, 3);
273 as.ShllByCl(Assembler::eax);
274 as.Ret();
275 as.Finalize();
276
277 ScopedExecRegion exec(&code);
278
279 using TestFunc = uint32_t(uint32_t);
280 uint32_t result = exec.get<TestFunc>()(22);
281 return result == (22 << 4);
282 }
283
LogicTest()284 bool LogicTest() {
285 MachineCode code;
286 CodeEmitter as(&code);
287 as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
288 as.Movl(Assembler::ecx, 0x1);
289 as.Xorl(Assembler::eax, Assembler::ecx);
290 as.Movl(Assembler::ecx, 0xf);
291 as.Andl(Assembler::eax, Assembler::ecx);
292 as.Ret();
293 as.Finalize();
294
295 ScopedExecRegion exec(&code);
296
297 using TestFunc = uint32_t(uint32_t);
298 uint32_t result = exec.get<TestFunc>()(239);
299 return result == ((239 ^ 1) & 0xf);
300 }
301
BsrTest()302 bool BsrTest() {
303 MachineCode code;
304 CodeEmitter as(&code);
305
306 as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 4});
307 as.Movl(Assembler::edx, 239);
308 as.Bsrl(Assembler::eax, Assembler::ecx);
309 as.Cmovl(Assembler::Condition::kZero, Assembler::eax, Assembler::edx);
310 as.Ret();
311 as.Finalize();
312
313 ScopedExecRegion exec(&code);
314
315 using TestFunc = uint32_t(uint32_t arg);
316 auto func = exec.get<TestFunc>();
317 return func(0) == 239 && func(1 << 15) == 15;
318 }
319
CallFPTest()320 bool CallFPTest() {
321 MachineCode code;
322 CodeEmitter as(&code);
323 as.Push(0x3f800000);
324 as.Push(0x40000000);
325 as.Call(bit_cast<const void*>(&FloatFunc));
326 as.Fstps({.base = Assembler::esp});
327 as.Pop(Assembler::eax);
328 as.Addl(Assembler::esp, 4);
329 as.Ret();
330 as.Finalize();
331
332 ScopedExecRegion exec(&code);
333
334 using TestFunc = uint32_t();
335 uint32_t result = exec.get<TestFunc>()();
336 return result == 0x3f800000;
337 }
338
XmmTest()339 bool XmmTest() {
340 MachineCode code;
341 CodeEmitter as(&code);
342 as.Movl(Assembler::eax, 0x3f800000);
343 as.Movd(Assembler::xmm0, Assembler::eax);
344 as.Movl(Assembler::eax, 0x40000000);
345 as.Movd(Assembler::xmm5, Assembler::eax);
346 as.Addss(Assembler::xmm0, Assembler::xmm5);
347 as.Movd(Assembler::eax, Assembler::xmm0);
348 as.Ret();
349 as.Finalize();
350
351 ScopedExecRegion exec(&code);
352
353 using TestFunc = uint32_t();
354 uint32_t result = exec.get<TestFunc>()();
355 return result == 0x40400000;
356 }
357
ReadGlobalTest()358 bool ReadGlobalTest() {
359 MachineCode code;
360 CodeEmitter as(&code);
361 static const uint32_t kData[] __attribute__((aligned(16))) = // NOLINT
362 {0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff};
363 as.Movsd(Assembler::xmm0, {.disp = bit_cast<int32_t>(&kData)});
364 as.Movdqa(Assembler::xmm1, {.disp = bit_cast<int32_t>(&kData)});
365 as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
366 as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8});
367 as.Movsd({.base = Assembler::eax}, Assembler::xmm0);
368 as.Movdqu({.base = Assembler::ecx}, Assembler::xmm1);
369
370 as.Ret();
371 as.Finalize();
372
373 ScopedExecRegion exec(&code);
374
375 using TestFunc = void(void*, void*);
376 uint8_t res1[8];
377 uint8_t res2[16];
378 exec.get<TestFunc>()(res1, res2);
379
380 return (memcmp(res1, kData, 8) == 0) && (memcmp(res2, kData, 16) == 0);
381 }
382
383 } // namespace x86_32
384
385 #elif defined(__amd64__)
386
387 namespace x86_64 {
388
AssemblerTest()389 bool AssemblerTest() {
390 MachineCode code;
391 CodeEmitter assembler(&code);
392 assembler.Movq(Assembler::rax, Assembler::rdi);
393 assembler.Subq(Assembler::rsp, 16);
394 assembler.Movq({.base = Assembler::rsp}, Assembler::rax);
395 assembler.Movq({.base = Assembler::rsp, .disp = 8}, Assembler::rax);
396 assembler.Movl({.base = Assembler::rax, .disp = 16}, 239);
397 assembler.Movq(Assembler::r11, {.base = Assembler::rsp});
398 assembler.Addq(Assembler::rsp, 16);
399 assembler.Ret();
400 assembler.Finalize();
401
402 // clang-format off
403 static const uint8_t code_template[] = {
404 0x48, 0x89, 0xf8, // mov %rdi, %rax
405 0x48, 0x83, 0xec, 0x10, // sub $0x10, %rsp
406 0x48, 0x89, 0x04, 0x24, // mov rax, (%rsp)
407 0x48, 0x89, 0x44, 0x24, 0x08, // mov rax, 8(%rsp)
408 0xc7, 0x40, 0x10, 0xef, 0x00, // movl $239, 0x10(%rax)
409 0x00, 0x00,
410 0x4c, 0x8b, 0x1c, 0x24, // mov (%rsp), r11
411 0x48, 0x83, 0xc4, 0x10, // add $0x10, %rsp
412 0xc3 // ret
413 };
414 // clang-format on
415
416 if (sizeof(code_template) != code.install_size()) {
417 ALOGE("Code size mismatch: %zu != %u", sizeof(code_template), code.install_size());
418 return false;
419 }
420
421 if (memcmp(code_template, code.AddrAs<uint8_t>(0), code.install_size()) != 0) {
422 ALOGE("Code mismatch");
423 MachineCode code2;
424 code2.Add(code_template);
425 std::string code_str1, code_str2;
426 code.AsString(&code_str1);
427 code2.AsString(&code_str2);
428 ALOGE("assembler generated\n%s\nshall be\n%s", code_str1.c_str(), code_str2.c_str());
429 return false;
430 }
431 return true;
432 }
433
LabelTest()434 bool LabelTest() {
435 MachineCode code;
436 CodeEmitter as(&code);
437 Assembler::Label skip, skip2, back, end;
438 as.Call(bit_cast<const void*>(&Callee));
439 as.Jmp(skip);
440 as.Movl(Assembler::rax, 2);
441 as.Bind(&skip);
442 as.Addb(Assembler::rax, {end});
443 as.Jmp(skip2);
444 as.Bind(&back);
445 as.Addl(Assembler::rax, 12);
446 as.Jmp(end);
447 as.Bind(&skip2);
448 as.Jmp(back);
449 as.Bind(&end);
450 as.Ret();
451 as.Finalize();
452
453 ScopedExecRegion exec(&code);
454
455 using TestFunc = int();
456 int result = exec.get<TestFunc>()();
457 return result == uint8_t(239 + 0xc3) + 12;
458 }
459
CondTest1()460 bool CondTest1() {
461 MachineCode code;
462 CodeEmitter as(&code);
463 as.Movl(Assembler::rax, 0xcccccccc);
464 as.Cmpl(Assembler::rdi, Assembler::rsi);
465 as.Setcc(Assembler::Condition::kEqual, Assembler::rax);
466 as.Ret();
467 as.Finalize();
468
469 ScopedExecRegion exec(&code);
470
471 std::string code_str;
472 code.AsString(&code_str);
473 using TestFunc = uint32_t(int, int);
474 auto target_func = exec.get<TestFunc>();
475 uint32_t result;
476 result = target_func(1, 2);
477 if (result != 0xcccccc00) {
478 ALOGE("Bug in seteq(not equal): %x", result);
479 return false;
480 }
481 result = target_func(-1, -1);
482 if (result != 0xcccccc01) {
483 ALOGE("Bug in seteq(equal): %x", result);
484 return false;
485 }
486 return true;
487 }
488
CondTest2()489 bool CondTest2() {
490 MachineCode code;
491 CodeEmitter as(&code);
492 as.Movl(Assembler::rdx, Assembler::rdi); // arg1.
493 as.Movl(Assembler::rcx, Assembler::rsi); // arg2.
494 as.Xorl(Assembler::rax, Assembler::rax);
495 as.Testb(Assembler::rdx, Assembler::rcx);
496 as.Setcc(Assembler::Condition::kNotZero, Assembler::rax);
497 as.Xchgq(Assembler::rax, Assembler::rcx);
498 as.Xchgq(Assembler::rcx, Assembler::rax);
499 as.Xchgq(Assembler::rcx, Assembler::r11);
500 as.Xchgq(Assembler::r11, Assembler::rcx);
501 as.Ret();
502 as.Finalize();
503
504 ScopedExecRegion exec(&code);
505
506 using TestFunc = uint32_t(int, int);
507 auto target_func = exec.get<TestFunc>();
508 uint32_t result = target_func(0x11, 1);
509 if (result != 0x1) {
510 printf("Bug in testb(not zero): %x", result);
511 return false;
512 }
513 result = target_func(0x11, 0x8);
514 if (result != 0x0) {
515 printf("Bug in testb(zero): %x", result);
516 return false;
517 }
518 return true;
519 }
520
JccTest()521 bool JccTest() {
522 MachineCode code;
523 CodeEmitter as(&code);
524 Assembler::Label equal, above, below, done;
525 as.Cmpl(Assembler::rdi, Assembler::rsi);
526 as.Jcc(Assembler::Condition::kEqual, equal);
527 as.Jcc(Assembler::Condition::kBelow, below);
528 as.Jcc(Assembler::Condition::kAbove, above);
529
530 as.Movl(Assembler::rax, 13);
531 as.Jmp(done);
532
533 as.Bind(&equal);
534 as.Movq(Assembler::rax, 0);
535 as.Jmp(done);
536
537 as.Bind(&below);
538 as.Movl(Assembler::rax, -1);
539 as.Jmp(done);
540
541 as.Bind(&above);
542 as.Movl(Assembler::rax, 1);
543 as.Jmp(done);
544
545 as.Bind(&done);
546 as.Ret();
547 as.Finalize();
548
549 ScopedExecRegion exec(&code);
550
551 using TestFunc = int(int, int);
552 auto target_func = exec.get<TestFunc>();
553 int result;
554 result = target_func(1, 1);
555 if (result != 0) {
556 ALOGE("Bug in jcc(equal): %x", result);
557 return false;
558 }
559 result = target_func(1, 0);
560 if (result != 1) {
561 ALOGE("Bug in jcc(above): %x", result);
562 return false;
563 }
564 result = target_func(0, 1);
565 if (result != -1) {
566 ALOGE("Bug in jcc(below): %x", result);
567 return false;
568 }
569 return true;
570 }
571
ReadWriteTest()572 bool ReadWriteTest() {
573 MachineCode code;
574 CodeEmitter as(&code);
575
576 as.Movq(Assembler::rax, 0);
577 as.Movb(Assembler::rax, {.base = Assembler::rdi});
578 as.Movl(Assembler::rcx, {.base = Assembler::rsi});
579 as.Addl(Assembler::rax, Assembler::rcx);
580 as.Movl({.base = Assembler::rsi}, Assembler::rax);
581 as.Ret();
582 as.Finalize();
583
584 ScopedExecRegion exec(&code);
585
586 using TestFunc = uint32_t(uint8_t*, uint32_t*);
587 uint8_t p1[4] = {0x12, 0x34, 0x56, 0x78};
588 uint32_t p2 = 0x239;
589 uint32_t result = exec.get<TestFunc>()(p1, &p2);
590 return (result == 0x239 + 0x12) && (p2 == result);
591 }
592
CallFPTest()593 bool CallFPTest() {
594 MachineCode code;
595 CodeEmitter as(&code);
596 as.Movl(Assembler::rax, 0x40000000);
597 as.Movd(Assembler::xmm0, Assembler::rax);
598 as.Movl(Assembler::rax, 0x3f800000);
599 as.Movd(Assembler::xmm1, Assembler::rax);
600 as.Call(bit_cast<const void*>(&FloatFunc));
601 as.Movd(Assembler::rax, Assembler::xmm0);
602 as.Ret();
603 as.Finalize();
604
605 ScopedExecRegion exec(&code);
606
607 using TestFunc = uint32_t();
608 uint32_t result = exec.get<TestFunc>()();
609 return result == 0x3f800000;
610 }
611
XmmTest()612 bool XmmTest() {
613 MachineCode code;
614 CodeEmitter as(&code);
615 as.Movl(Assembler::rax, 0x40000000);
616 as.Movd(Assembler::xmm0, Assembler::rax);
617 as.Movl(Assembler::rax, 0x3f800000);
618 as.Movd(Assembler::xmm11, Assembler::rax);
619 as.Addss(Assembler::xmm0, Assembler::xmm11);
620 as.Movaps(Assembler::xmm12, Assembler::xmm0);
621 as.Addss(Assembler::xmm0, Assembler::xmm12);
622 as.Movapd(Assembler::xmm14, Assembler::xmm1);
623 as.Movd(Assembler::rax, Assembler::xmm0);
624 as.Ret();
625 as.Finalize();
626
627 ScopedExecRegion exec(&code);
628
629 using TestFunc = uint32_t();
630 uint32_t result = exec.get<TestFunc>()();
631 return result == 0x40c00000;
632 }
633
XmmMemTest()634 bool XmmMemTest() {
635 MachineCode code;
636 CodeEmitter as(&code);
637
638 as.Movsd(Assembler::xmm0, {.base = Assembler::rdi});
639 as.Movaps(Assembler::xmm12, Assembler::xmm0);
640 as.Addsd(Assembler::xmm12, Assembler::xmm12);
641 as.Movsd({.base = Assembler::rdi}, Assembler::xmm12);
642 as.Movq(Assembler::rax, Assembler::xmm0);
643 as.Ret();
644 as.Finalize();
645
646 ScopedExecRegion exec(&code);
647
648 double d = 239.0;
649 char bits[16], *p = bits + 5;
650 memcpy(p, &d, sizeof(d));
651
652 using TestFunc = uint64_t(char* p);
653 uint64_t result = exec.get<TestFunc>()(p);
654 uint64_t doubled = *reinterpret_cast<uint64_t*>(p);
655 return result == 0x406de00000000000ULL && doubled == 0x407de00000000000ULL;
656 }
657
MovsxblRexTest()658 bool MovsxblRexTest() {
659 MachineCode code;
660 CodeEmitter as(&code);
661
662 as.Xorl(Assembler::rdx, Assembler::rdx);
663 as.Movl(Assembler::rsi, 0xdeadff);
664 // CodeEmitter should use REX prefix to encode SIL.
665 // Without REX DH is used.
666 as.Movsxbl(Assembler::rax, Assembler::rsi);
667 as.Ret();
668 as.Finalize();
669
670 ScopedExecRegion exec(&code);
671
672 using TestFunc = uint32_t();
673 uint32_t result = exec.get<TestFunc>()();
674
675 return result == 0xffffffff;
676 }
677
MovzxblRexTest()678 bool MovzxblRexTest() {
679 MachineCode code;
680 CodeEmitter as(&code);
681
682 as.Xorl(Assembler::rdx, Assembler::rdx);
683 as.Movl(Assembler::rsi, 0xdeadff);
684 // CodeEmitter should use REX prefix to encode SIL.
685 // Without REX DH is used.
686 as.Movzxbl(Assembler::rax, Assembler::rsi);
687 as.Ret();
688 as.Finalize();
689
690 ScopedExecRegion exec(&code);
691
692 using TestFunc = uint32_t();
693 uint32_t result = exec.get<TestFunc>()();
694
695 return result == 0x000000ff;
696 }
697
ShldlRexTest()698 bool ShldlRexTest() {
699 MachineCode code;
700 CodeEmitter as(&code);
701
702 as.Movl(Assembler::rdx, 0x12345678);
703 // If most-significant bit is not encoded correctly with REX
704 // RAX can be used instead R8 and R10 can be used instead RDX.
705 // Init them all:
706 as.Xorl(Assembler::r8, Assembler::r8);
707 as.Movl(Assembler::rax, 0xdeadbeef);
708 as.Movl(Assembler::r10, 0xdeadbeef);
709
710 as.Shldl(Assembler::r8, Assembler::rdx, int8_t{8});
711 as.Movl(Assembler::rcx, 8);
712 as.ShldlByCl(Assembler::r8, Assembler::rdx);
713
714 as.Movl(Assembler::rax, Assembler::r8);
715
716 as.Ret();
717 as.Finalize();
718
719 ScopedExecRegion exec(&code);
720
721 using TestFunc = uint32_t();
722 uint32_t result = exec.get<TestFunc>()();
723
724 return result == 0x1212;
725 }
726
ShrdlRexTest()727 bool ShrdlRexTest() {
728 MachineCode code;
729 CodeEmitter as(&code);
730
731 as.Movl(Assembler::rdx, 0x12345678);
732 // If most-significant bit is not encoded correctly with REX
733 // RAX can be used instead R8 and R10 can be used instead RDX.
734 // Init them all:
735 as.Xorl(Assembler::r8, Assembler::r8);
736 as.Movl(Assembler::rax, 0xdeadbeef);
737 as.Movl(Assembler::r10, 0xdeadbeef);
738
739 as.Shrdl(Assembler::r8, Assembler::rdx, int8_t{8});
740 as.Movl(Assembler::rcx, 8);
741 as.ShrdlByCl(Assembler::r8, Assembler::rdx);
742
743 as.Movl(Assembler::rax, Assembler::r8);
744
745 as.Ret();
746 as.Finalize();
747
748 ScopedExecRegion exec(&code);
749
750 using TestFunc = uint32_t();
751 uint32_t result = exec.get<TestFunc>()();
752
753 return result == 0x78780000;
754 }
755
ReadGlobalTest()756 bool ReadGlobalTest() {
757 MachineCode code;
758 CodeEmitter as(&code);
759 static const uint32_t kData[] __attribute__((aligned(16))) = // NOLINT
760 {0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff};
761 // We couldn't read data from arbitrary address on x86_64, need address in first 2GiB.
762 void* data =
763 mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
764 // Copy our global there.
765 memcpy(data, kData, 16);
766 int32_t data_offset = static_cast<int32_t>(bit_cast<intptr_t>(data));
767 as.Movsd(Assembler::xmm0, {.disp = data_offset});
768 as.Movdqa(Assembler::xmm1, {.disp = data_offset});
769 as.Movsd({.base = Assembler::rdi}, Assembler::xmm0);
770 as.Movdqu({.base = Assembler::rsi}, Assembler::xmm1);
771
772 as.Ret();
773 as.Finalize();
774
775 ScopedExecRegion exec(&code);
776
777 using TestFunc = void(void*, void*);
778 uint8_t res1[8];
779 uint8_t res2[16];
780 exec.get<TestFunc>()(res1, res2);
781
782 munmap(data, 4096);
783
784 return (memcmp(res1, kData, 8) == 0) && (memcmp(res2, kData, 16) == 0);
785 }
786
MemShiftTest()787 bool MemShiftTest() {
788 MachineCode code;
789 CodeEmitter as(&code);
790
791 as.Push(Assembler::rdi);
792 as.Movl(Assembler::rcx, 1);
793 as.ShrlByCl({.base = Assembler::rsp});
794 as.Addl(Assembler::rcx, 1);
795 as.Movq(Assembler::rdi, Assembler::rsp);
796 as.ShllByCl({.base = Assembler::rdi});
797 as.Pop(Assembler::rax);
798
799 as.Ret();
800 as.Finalize();
801
802 ScopedExecRegion exec(&code);
803
804 using TestFunc = int(int x);
805 int result = exec.get<TestFunc>()(0x10);
806
807 return result == 0x20;
808 }
809
810 } // namespace x86_64
811
812 #endif
813
814 #if defined(__i386__) || defined(__amd64__)
815
816 #if defined(__i386__)
817
818 extern "C" const uint8_t berberis_gnu_as_output_start[] asm(
819 "berberis_gnu_as_output_start_x86_32");
820 extern "C" const uint8_t berberis_gnu_as_output_end[] asm(
821 "berberis_gnu_as_output_end_x86_32");
822
823 namespace x86_32 {
824 void GenInsnsCommon(CodeEmitter* as);
825 void GenInsnsArch(CodeEmitter* as);
826 } // namespace x86_32
827
828 #else
829
830 extern "C" const uint8_t berberis_gnu_as_output_start[] asm(
831 "berberis_gnu_as_output_start_x86_64");
832 extern "C" const uint8_t berberis_gnu_as_output_end[] asm(
833 "berberis_gnu_as_output_end_x86_64");
834
835 namespace x86_64 {
836 void GenInsnsCommon(CodeEmitter* as);
837 void GenInsnsArch(CodeEmitter* as);
838 } // namespace x86_64
839
840 #endif
841
ExhaustiveTest()842 bool ExhaustiveTest() {
843 MachineCode code;
844 CodeEmitter as(&code);
845
846 #if defined(__i386__)
847 berberis::x86_32::GenInsnsCommon(&as);
848 berberis::x86_32::GenInsnsArch(&as);
849 #else
850 berberis::x86_64::GenInsnsCommon(&as);
851 berberis::x86_64::GenInsnsArch(&as);
852 #endif
853 as.Finalize();
854
855 return CompareCode(berberis_gnu_as_output_start, berberis_gnu_as_output_end, code);
856 }
857
MixedAssembler()858 bool MixedAssembler() {
859 MachineCode code;
860 x86_32::Assembler as32(&code);
861 x86_64::Assembler as64(&code);
862 x86_32::Assembler::Label lbl32;
863 x86_64::Assembler::Label lbl64;
864
865 as32.Jmp(lbl32);
866 as32.Xchgl(x86_32::Assembler::eax, x86_32::Assembler::eax);
867 as64.Jmp(lbl64);
868 as64.Xchgl(x86_64::Assembler::rax, x86_64::Assembler::rax);
869 as32.Bind(&lbl32);
870 as32.Movl(x86_32::Assembler::eax, {.disp = 0});
871 as64.Bind(&lbl64);
872 as32.Finalize();
873 as64.Finalize();
874
875 // clang-format off
876 static const uint8_t code_template[] = {
877 0xe9, 0x08, 0x00, 0x00, 0x00, // jmp lbl32
878 0x90, // xchg %eax, %eax == nop
879 0xe9, 0x07, 0x00, 0x00, 0x00, // jmp lbl64
880 0x87, 0xc0, // xchg %eax, %eax != nop
881 // lbl32:
882 0xa1, 0x00, 0x00, 0x00, 0x00 // movabs %eax, 0x0
883 // lbl64:
884 };
885 // clang-format on
886
887 return CompareCode(std::begin(code_template), std::end(code_template), code);
888 }
889 #endif
890
891 } // namespace berberis
892
TEST(Assembler,AssemblerTest)893 TEST(Assembler, AssemblerTest) {
894 #if defined(__i386__)
895 EXPECT_TRUE(berberis::x86_32::AssemblerTest());
896 EXPECT_TRUE(berberis::x86_32::LabelTest());
897 EXPECT_TRUE(berberis::x86_32::CondTest1());
898 EXPECT_TRUE(berberis::x86_32::CondTest2());
899 EXPECT_TRUE(berberis::x86_32::JccTest());
900 EXPECT_TRUE(berberis::x86_32::ShiftTest());
901 EXPECT_TRUE(berberis::x86_32::LogicTest());
902 EXPECT_TRUE(berberis::x86_32::CallFPTest());
903 EXPECT_TRUE(berberis::x86_32::XmmTest());
904 EXPECT_TRUE(berberis::x86_32::BsrTest());
905 EXPECT_TRUE(berberis::x86_32::ReadGlobalTest());
906 EXPECT_TRUE(berberis::ExhaustiveTest());
907 EXPECT_TRUE(berberis::MixedAssembler());
908 #elif defined(__amd64__)
909 EXPECT_TRUE(berberis::x86_64::AssemblerTest());
910 EXPECT_TRUE(berberis::x86_64::LabelTest());
911 EXPECT_TRUE(berberis::x86_64::CondTest1());
912 EXPECT_TRUE(berberis::x86_64::CondTest2());
913 EXPECT_TRUE(berberis::x86_64::JccTest());
914 EXPECT_TRUE(berberis::x86_64::ReadWriteTest());
915 EXPECT_TRUE(berberis::x86_64::CallFPTest());
916 EXPECT_TRUE(berberis::x86_64::XmmTest());
917 EXPECT_TRUE(berberis::x86_64::XmmMemTest());
918 EXPECT_TRUE(berberis::x86_64::MovsxblRexTest());
919 EXPECT_TRUE(berberis::x86_64::MovzxblRexTest());
920 EXPECT_TRUE(berberis::x86_64::ShldlRexTest());
921 EXPECT_TRUE(berberis::x86_64::ShrdlRexTest());
922 EXPECT_TRUE(berberis::x86_64::ReadGlobalTest());
923 EXPECT_TRUE(berberis::x86_64::MemShiftTest());
924 EXPECT_TRUE(berberis::ExhaustiveTest());
925 EXPECT_TRUE(berberis::MixedAssembler());
926 #endif
927 }
928