xref: /llvm-project/lldb/examples/ptrace_example.c (revision 3398744a6106c83993611bd3c5e79ec6b94417dc)
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 = &regs;
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