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