Commit | Line | Data |
---|---|---|
4f51c22a PL |
1 | /* This testcase is part of GDB, the GNU debugger. |
2 | ||
618f726f | 3 | Copyright 2015-2016 Free Software Foundation, Inc. |
4f51c22a PL |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #include <stddef.h> | |
19 | #include <stdint.h> | |
20 | ||
21 | typedef void (*testcase_ftype)(void); | |
22 | ||
23 | /* Each function checks the correctness of the instruction being | |
24 | relocated due to a fast tracepoint. Call function pass if it is | |
25 | correct, otherwise call function fail. GDB sets a breakpoints on | |
26 | pass and fail in order to check the correctness. */ | |
27 | ||
28 | static void | |
29 | pass (void) | |
30 | { | |
31 | } | |
32 | ||
33 | static void | |
34 | fail (void) | |
35 | { | |
36 | } | |
37 | ||
38 | #if (defined __x86_64__ || defined __i386__) | |
39 | ||
40 | #ifdef SYMBOL_PREFIX | |
41 | #define SYMBOL(str) SYMBOL_PREFIX #str | |
42 | #else | |
43 | #define SYMBOL(str) #str | |
44 | #endif | |
45 | ||
46 | /* Make sure we can relocate a CALL instruction. CALL instructions are | |
47 | 5 bytes long so we can always set a fast tracepoints on them. | |
48 | ||
49 | JMP set_point0 | |
50 | f: | |
51 | MOV $1, %[ok] | |
52 | JMP end | |
53 | set_point0: | |
54 | CALL f ; tracepoint here. | |
55 | end: | |
56 | ||
57 | */ | |
58 | ||
59 | static void | |
60 | can_relocate_call (void) | |
61 | { | |
62 | int ok = 0; | |
63 | ||
64 | asm (" .global " SYMBOL (set_point0) "\n" | |
65 | " jmp " SYMBOL (set_point0) "\n" | |
66 | "0:\n" | |
67 | " mov $1, %[ok]\n" | |
68 | " jmp 1f\n" | |
69 | SYMBOL (set_point0) ":\n" | |
70 | " call 0b\n" | |
71 | "1:\n" | |
72 | : [ok] "=r" (ok)); | |
73 | ||
74 | if (ok == 1) | |
75 | pass (); | |
76 | else | |
77 | fail (); | |
78 | } | |
79 | ||
80 | /* Make sure we can relocate a JMP instruction. We need the JMP | |
81 | instruction to be 5 bytes long in order to set a fast tracepoint on | |
82 | it. To do this, we emit the opcode directly. | |
83 | ||
84 | JMP next ; tracepoint here. | |
85 | next: | |
86 | MOV $1, %[ok] | |
87 | ||
88 | */ | |
89 | ||
90 | static void | |
91 | can_relocate_jump (void) | |
92 | { | |
93 | int ok = 0; | |
94 | ||
95 | asm (" .global " SYMBOL (set_point1) "\n" | |
96 | SYMBOL (set_point1) ":\n" | |
97 | ".byte 0xe9\n" /* jmp */ | |
98 | ".byte 0x00\n" | |
99 | ".byte 0x00\n" | |
100 | ".byte 0x00\n" | |
101 | ".byte 0x00\n" | |
102 | " mov $1, %[ok]\n" | |
103 | : [ok] "=r" (ok)); | |
104 | ||
105 | if (ok == 1) | |
106 | pass (); | |
107 | else | |
108 | fail (); | |
109 | } | |
110 | #elif (defined __aarch64__) | |
111 | ||
112 | /* Make sure we can relocate a B instruction. | |
113 | ||
114 | B set_point0 | |
115 | set_ok: | |
116 | MOV %[ok], #1 | |
117 | B end | |
118 | set_point0: | |
119 | B set_ok ; tracepoint here. | |
120 | MOV %[ok], #0 | |
121 | end | |
122 | ||
123 | */ | |
124 | ||
125 | static void | |
126 | can_relocate_b (void) | |
127 | { | |
128 | int ok = 0; | |
129 | ||
130 | asm (" b set_point0\n" | |
131 | "0:\n" | |
132 | " mov %[ok], #1\n" | |
133 | " b 1f\n" | |
134 | "set_point0:\n" | |
135 | " b 0b\n" | |
136 | " mov %[ok], #0\n" | |
137 | "1:\n" | |
138 | : [ok] "=r" (ok)); | |
139 | ||
140 | if (ok == 1) | |
141 | pass (); | |
142 | else | |
143 | fail (); | |
144 | } | |
145 | ||
146 | /* Make sure we can relocate a B.cond instruction. | |
147 | ||
148 | MOV x0, #8 | |
149 | TST x0, #8 ; Clear the Z flag. | |
150 | B set_point1 | |
151 | set_ok: | |
152 | MOV %[ok], #1 | |
153 | B end | |
154 | set_point1: | |
155 | B.NE set_ok ; tracepoint here. | |
156 | MOV %[ok], #0 | |
157 | end | |
158 | ||
159 | */ | |
160 | ||
161 | static void | |
8240f442 | 162 | can_relocate_bcond_true (void) |
4f51c22a PL |
163 | { |
164 | int ok = 0; | |
165 | ||
166 | asm (" mov x0, #8\n" | |
167 | " tst x0, #8\n" | |
168 | " b set_point1\n" | |
169 | "0:\n" | |
170 | " mov %[ok], #1\n" | |
171 | " b 1f\n" | |
172 | "set_point1:\n" | |
173 | " b.ne 0b\n" | |
174 | " mov %[ok], #0\n" | |
175 | "1:\n" | |
176 | : [ok] "=r" (ok) | |
177 | : | |
178 | : "0", "cc"); | |
179 | ||
180 | if (ok == 1) | |
181 | pass (); | |
182 | else | |
183 | fail (); | |
184 | } | |
185 | ||
186 | /* Make sure we can relocate a CBZ instruction. | |
187 | ||
188 | MOV x0, #0 | |
189 | B set_point2 | |
190 | set_ok: | |
191 | MOV %[ok], #1 | |
192 | B end | |
193 | set_point2: | |
194 | CBZ x0, set_ok ; tracepoint here. | |
195 | MOV %[ok], #0 | |
196 | end | |
197 | ||
198 | */ | |
199 | ||
200 | static void | |
201 | can_relocate_cbz (void) | |
202 | { | |
203 | int ok = 0; | |
204 | ||
205 | asm (" mov x0, #0\n" | |
206 | " b set_point2\n" | |
207 | "0:\n" | |
208 | " mov %[ok], #1\n" | |
209 | " b 1f\n" | |
210 | "set_point2:\n" | |
211 | " cbz x0, 0b\n" | |
212 | " mov %[ok], #0\n" | |
213 | "1:\n" | |
214 | : [ok] "=r" (ok) | |
215 | : | |
216 | : "0"); | |
217 | ||
218 | if (ok == 1) | |
219 | pass (); | |
220 | else | |
221 | fail (); | |
222 | } | |
223 | ||
224 | /* Make sure we can relocate a CBNZ instruction. | |
225 | ||
226 | MOV x0, #8 | |
227 | B set_point3 | |
228 | set_ok: | |
229 | MOV %[ok], #1 | |
230 | B end | |
231 | set_point3: | |
232 | CBNZ x0, set_ok ; tracepoint here. | |
233 | MOV %[ok], #0 | |
234 | end | |
235 | ||
236 | */ | |
237 | ||
238 | static void | |
239 | can_relocate_cbnz (void) | |
240 | { | |
241 | int ok = 0; | |
242 | ||
243 | asm (" mov x0, #8\n" | |
244 | " b set_point3\n" | |
245 | "0:\n" | |
246 | " mov %[ok], #1\n" | |
247 | " b 1f\n" | |
248 | "set_point3:\n" | |
249 | " cbnz x0, 0b\n" | |
250 | " mov %[ok], #0\n" | |
251 | "1:\n" | |
252 | : [ok] "=r" (ok) | |
253 | : | |
254 | : "0"); | |
255 | ||
256 | if (ok == 1) | |
257 | pass (); | |
258 | else | |
259 | fail (); | |
260 | } | |
261 | ||
262 | /* Make sure we can relocate a TBZ instruction. | |
263 | ||
264 | MOV x0, #8 | |
265 | MVN x0, x0 ; Clear bit 3. | |
266 | B set_point4 | |
267 | set_ok: | |
268 | MOV %[ok], #1 | |
269 | B end | |
270 | set_point4: | |
271 | TBZ x0, #3, set_ok ; tracepoint here. | |
272 | MOV %[ok], #0 | |
273 | end | |
274 | ||
275 | */ | |
276 | ||
277 | static void | |
278 | can_relocate_tbz (void) | |
279 | { | |
280 | int ok = 0; | |
281 | ||
282 | asm (" mov x0, #8\n" | |
283 | " mvn x0, x0\n" | |
284 | " b set_point4\n" | |
285 | "0:\n" | |
286 | " mov %[ok], #1\n" | |
287 | " b 1f\n" | |
288 | "set_point4:\n" | |
289 | " tbz x0, #3, 0b\n" | |
290 | " mov %[ok], #0\n" | |
291 | "1:\n" | |
292 | : [ok] "=r" (ok) | |
293 | : | |
294 | : "0"); | |
295 | ||
296 | if (ok == 1) | |
297 | pass (); | |
298 | else | |
299 | fail (); | |
300 | } | |
301 | ||
302 | /* Make sure we can relocate a TBNZ instruction. | |
303 | ||
304 | MOV x0, #8 ; Set bit 3. | |
305 | B set_point5 | |
306 | set_ok: | |
307 | MOV %[ok], #1 | |
308 | B end | |
309 | set_point5: | |
310 | TBNZ x0, #3, set_ok ; tracepoint here. | |
311 | MOV %[ok], #0 | |
312 | end | |
313 | ||
314 | */ | |
315 | ||
316 | static void | |
317 | can_relocate_tbnz (void) | |
318 | { | |
319 | int ok = 0; | |
320 | ||
321 | asm (" mov x0, #8\n" | |
322 | " b set_point5\n" | |
323 | "0:\n" | |
324 | " mov %[ok], #1\n" | |
325 | " b 1f\n" | |
326 | "set_point5:\n" | |
327 | " tbnz x0, #3, 0b\n" | |
328 | " mov %[ok], #0\n" | |
329 | "1:\n" | |
330 | : [ok] "=r" (ok) | |
331 | : | |
332 | : "0"); | |
333 | ||
334 | if (ok == 1) | |
335 | pass (); | |
336 | else | |
337 | fail (); | |
338 | } | |
339 | ||
340 | /* Make sure we can relocate an ADR instruction with a positive offset. | |
341 | ||
342 | set_point6: | |
343 | ADR x0, target ; tracepoint here. | |
344 | BR x0 ; jump to target | |
345 | MOV %[ok], #0 | |
346 | B end | |
347 | target: | |
348 | MOV %[ok], #1 | |
349 | end | |
350 | ||
351 | */ | |
352 | ||
353 | static void | |
354 | can_relocate_adr_forward (void) | |
355 | { | |
356 | int ok = 0; | |
357 | ||
358 | asm ("set_point6:\n" | |
359 | " adr x0, 0f\n" | |
360 | " br x0\n" | |
361 | " mov %[ok], #0\n" | |
362 | " b 1f\n" | |
363 | "0:\n" | |
364 | " mov %[ok], #1\n" | |
365 | "1:\n" | |
366 | : [ok] "=r" (ok) | |
367 | : | |
368 | : "0"); | |
369 | ||
370 | if (ok == 1) | |
371 | pass (); | |
372 | else | |
373 | fail (); | |
374 | } | |
375 | ||
376 | /* Make sure we can relocate an ADR instruction with a negative offset. | |
377 | ||
378 | B set_point7 | |
379 | target: | |
380 | MOV %[ok], #1 | |
381 | B end | |
382 | set_point7: | |
383 | ADR x0, target ; tracepoint here. | |
384 | BR x0 ; jump to target | |
385 | MOV %[ok], #0 | |
386 | end | |
387 | ||
388 | */ | |
389 | ||
390 | static void | |
391 | can_relocate_adr_backward (void) | |
392 | { | |
393 | int ok = 0; | |
394 | ||
395 | asm ("b set_point7\n" | |
396 | "0:\n" | |
397 | " mov %[ok], #1\n" | |
398 | " b 1f\n" | |
399 | "set_point7:\n" | |
400 | " adr x0, 0b\n" | |
401 | " br x0\n" | |
402 | " mov %[ok], #0\n" | |
403 | "1:\n" | |
404 | : [ok] "=r" (ok) | |
405 | : | |
406 | : "0"); | |
407 | ||
408 | if (ok == 1) | |
409 | pass (); | |
410 | else | |
411 | fail (); | |
412 | } | |
413 | ||
414 | /* Make sure we can relocate an ADRP instruction. | |
415 | ||
416 | set_point8: | |
417 | ADRP %[addr], set_point8 ; tracepoint here. | |
418 | ADR %[pc], set_point8 | |
419 | ||
420 | ADR computes the address of the given label. While ADRP gives us its | |
421 | page, on a 4K boundary. We can check ADRP executed normally by | |
422 | making sure the result of ADR and ADRP are equivalent, except for the | |
423 | 12 lowest bits which should be cleared. | |
424 | ||
425 | */ | |
426 | ||
427 | static void | |
428 | can_relocate_adrp (void) | |
429 | { | |
430 | uintptr_t page; | |
431 | uintptr_t pc; | |
432 | ||
433 | asm ("set_point8:\n" | |
434 | " adrp %[page], set_point8\n" | |
435 | " adr %[pc], set_point8\n" | |
436 | : [page] "=r" (page), [pc] "=r" (pc)); | |
437 | ||
438 | if (page == (pc & ~0xfff)) | |
439 | pass (); | |
440 | else | |
441 | fail (); | |
442 | } | |
443 | ||
444 | /* Make sure we can relocate an LDR instruction, where the memory to | |
445 | read is an offset from the current PC. | |
446 | ||
447 | B set_point9 | |
448 | data: | |
449 | .word 0x0cabba9e | |
450 | set_point9: | |
451 | LDR %[result], data ; tracepoint here. | |
452 | ||
453 | */ | |
454 | ||
455 | static void | |
456 | can_relocate_ldr (void) | |
457 | { | |
458 | uint32_t result = 0; | |
459 | ||
460 | asm ("b set_point9\n" | |
461 | "0:\n" | |
462 | " .word 0x0cabba9e\n" | |
463 | "set_point9:\n" | |
464 | " ldr %w[result], 0b\n" | |
465 | : [result] "=r" (result)); | |
466 | ||
467 | if (result == 0x0cabba9e) | |
468 | pass (); | |
469 | else | |
470 | fail (); | |
471 | } | |
8240f442 YQ |
472 | |
473 | /* Make sure we can relocate a B.cond instruction and condition is false. */ | |
474 | ||
475 | static void | |
476 | can_relocate_bcond_false (void) | |
477 | { | |
478 | int ok = 0; | |
479 | ||
480 | asm (" mov x0, #8\n" | |
481 | " tst x0, #8\n" /* Clear the Z flag. */ | |
482 | "set_point10:\n" /* Set tracepoint here. */ | |
483 | " b.eq 0b\n" /* Condition is false. */ | |
484 | " mov %[ok], #1\n" | |
485 | " b 1f\n" | |
486 | "0:\n" | |
487 | " mov %[ok], #0\n" | |
488 | "1:\n" | |
489 | : [ok] "=r" (ok) | |
490 | : | |
491 | : "0", "cc"); | |
492 | ||
493 | if (ok == 1) | |
494 | pass (); | |
495 | else | |
496 | fail (); | |
497 | } | |
498 | ||
499 | static void | |
500 | foo (void) | |
501 | { | |
502 | } | |
503 | ||
504 | /* Make sure we can relocate a BL instruction. */ | |
505 | ||
506 | static void | |
507 | can_relocate_bl (void) | |
508 | { | |
509 | asm ("set_point11:\n" | |
510 | " bl foo\n" | |
511 | " bl pass\n"); /* Test that LR is updated correctly. */ | |
512 | } | |
513 | ||
4f51c22a PL |
514 | #endif |
515 | ||
516 | /* Functions testing relocations need to be placed here. GDB will read | |
517 | n_testcases to know how many fast tracepoints to place. It will look | |
518 | for symbols in the form of 'set_point\[0-9\]+' so each functions | |
519 | needs one, starting at 0. */ | |
520 | ||
521 | static testcase_ftype testcases[] = { | |
522 | #if (defined __x86_64__ || defined __i386__) | |
523 | can_relocate_call, | |
524 | can_relocate_jump | |
525 | #elif (defined __aarch64__) | |
526 | can_relocate_b, | |
8240f442 | 527 | can_relocate_bcond_true, |
4f51c22a PL |
528 | can_relocate_cbz, |
529 | can_relocate_cbnz, | |
530 | can_relocate_tbz, | |
531 | can_relocate_tbnz, | |
532 | can_relocate_adr_forward, | |
533 | can_relocate_adr_backward, | |
534 | can_relocate_adrp, | |
8240f442 YQ |
535 | can_relocate_ldr, |
536 | can_relocate_bcond_false, | |
537 | can_relocate_bl, | |
4f51c22a PL |
538 | #endif |
539 | }; | |
540 | ||
541 | static size_t n_testcases = (sizeof (testcases) / sizeof (testcase_ftype)); | |
542 | ||
543 | int | |
544 | main () | |
545 | { | |
546 | int i = 0; | |
547 | ||
548 | for (i = 0; i < n_testcases; i++) | |
549 | testcases[i] (); | |
550 | ||
551 | return 0; | |
552 | } |