1 | //===-- ptrace_example.c --------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include <asm/ptrace.h> |
10 | #include <linux/elf.h> |
11 | #include <stdint.h> |
12 | #include <stdio.h> |
13 | #include <sys/prctl.h> |
14 | #include <sys/ptrace.h> |
15 | #include <sys/uio.h> |
16 | #include <sys/wait.h> |
17 | #include <unistd.h> |
18 | |
19 | // The demo program shows how to do basic ptrace operations without lldb |
20 | // or lldb-server. For the purposes of experimentation or reporting bugs |
21 | // in kernels. |
22 | // |
23 | // It is AArch64 Linux specific, adapt as needed. |
24 | // |
25 | // Expected output: |
26 | // Before breakpoint |
27 | // After breakpoint |
28 | |
29 | void inferior() { |
30 | if (ptrace(request: PTRACE_TRACEME, 0, 0, 0) < 0) { |
31 | perror(s: "ptrace" ); |
32 | return; |
33 | } |
34 | |
35 | printf(format: "Before breakpoint\n" ); |
36 | |
37 | // Go into debugger. Instruction replaced with nop later. |
38 | // We write 2 instuctions because POKETEXT works with |
39 | // 64 bit values and we don't want to overwrite the |
40 | // call to printf accidentally. |
41 | asm volatile("BRK #0 \n nop" ); |
42 | |
43 | printf(format: "After breakpoint\n" ); |
44 | } |
45 | |
46 | void debugger(pid_t child) { |
47 | int wait_status; |
48 | // Wait until it hits the breakpoint. |
49 | wait(stat_loc: &wait_status); |
50 | |
51 | while (WIFSTOPPED(wait_status)) { |
52 | if (WIFEXITED(wait_status)) { |
53 | printf(format: "inferior exited normally\n" ); |
54 | return; |
55 | } |
56 | |
57 | // Read general purpose registers to find the PC value. |
58 | struct user_pt_regs regs; |
59 | struct iovec io; |
60 | io.iov_base = ®s; |
61 | io.iov_len = sizeof(regs); |
62 | if (ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &io) < 0) { |
63 | printf(format: "getregset failed\n" ); |
64 | return; |
65 | } |
66 | |
67 | // Replace brk #0 / nop with nop / nop by writing to memory |
68 | // at the current PC. |
69 | uint64_t replace = 0xd503201fd503201f; |
70 | if (ptrace(request: PTRACE_POKETEXT, child, regs.pc, replace) < 0) { |
71 | printf(format: "replacing bkpt failed\n" ); |
72 | return; |
73 | } |
74 | |
75 | // Single step over where the brk was. |
76 | if (ptrace(request: PTRACE_SINGLESTEP, child, 0, 0) < 0) { |
77 | perror(s: "ptrace" ); |
78 | return; |
79 | } |
80 | |
81 | // Wait for single step to be done. |
82 | wait(stat_loc: &wait_status); |
83 | |
84 | // Run to completion. |
85 | if (ptrace(request: PTRACE_CONT, child, 0, 0) < 0) { |
86 | perror(s: "ptrace" ); |
87 | return; |
88 | } |
89 | |
90 | // Wait to see that the inferior exited. |
91 | wait(stat_loc: &wait_status); |
92 | } |
93 | } |
94 | |
95 | int main() { |
96 | pid_t child = fork(); |
97 | |
98 | if (child == 0) |
99 | inferior(); |
100 | else if (child > 0) |
101 | debugger(child); |
102 | else |
103 | return -1; |
104 | |
105 | return 0; |
106 | } |
107 | |