blob: a387d8ff9ad73a1a355482a82dde74b1f077ebaf [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* Test custom provided Dwfl_Thread_Callbacks vector.
2 Copyright (C) 2013 Red Hat, Inc.
3 This file is part of elfutils.
4
5 This file 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 elfutils is distributed in the hope that it will be useful, but
11 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/* Test custom provided Dwfl_Thread_Callbacks vector. Test mimics what
19 a ptrace based vector would do. */
20
21#include <config.h>
22#include <assert.h>
23#include <inttypes.h>
24#include <stdio.h>
25#include <stdio_ext.h>
26#include <locale.h>
27#include <dirent.h>
28#include <stdlib.h>
29#include <errno.h>
30#include <error.h>
31#include <unistd.h>
32#include <dwarf.h>
33#if defined(__x86_64__) && defined(__linux__)
34#include <sys/resource.h>
35#include <sys/ptrace.h>
36#include <signal.h>
37#include <sys/types.h>
38#include <sys/wait.h>
39#include <sys/user.h>
40#include <fcntl.h>
41#include <string.h>
42#include ELFUTILS_HEADER(dwfl)
43#endif
44
45#if !defined(__x86_64__) || !defined(__linux__)
46
47int
48main (int argc __attribute__ ((unused)), char **argv)
49{
50 fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
51 argv[0]);
52 return 77;
53}
54
55#else /* __x86_64__ && __linux__ */
56
57/* The only arch specific code is set_initial_registers. */
58
59static int
60find_elf (Dwfl_Module *mod __attribute__ ((unused)),
61 void **userdata __attribute__ ((unused)),
62 const char *modname __attribute__ ((unused)),
63 Dwarf_Addr base __attribute__ ((unused)),
64 char **file_name __attribute__ ((unused)),
65 Elf **elfp __attribute__ ((unused)))
66{
67 /* Not used as modules are reported explicitly. */
68 assert (0);
69}
70
71static bool
72memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
73 void *dwfl_arg __attribute__ ((unused)))
74{
75 pid_t child = dwfl_pid (dwfl);
76
77 errno = 0;
78 long l = ptrace (PTRACE_PEEKDATA, child, (void *) (uintptr_t) addr, NULL);
79
80 // The unwinder can ask for an invalid address.
81 // Don't assert on that but just politely refuse.
82 if (errno != 0) {
83 errno = 0;
84 return false;
85 }
86 *result = l;
87
88 return true;
89}
90
91/* Return filename and VMA address *BASEP where its mapping starts which
92 contains ADDR. */
93
94static char *
95maps_lookup (pid_t pid, Dwarf_Addr addr, GElf_Addr *basep)
96{
97 char *fname;
98 int i = asprintf (&fname, "/proc/%ld/maps", (long) pid);
99 assert (errno == 0);
100 assert (i > 0);
101 FILE *f = fopen (fname, "r");
102 assert (errno == 0);
103 assert (f);
104 free (fname);
105 for (;;)
106 {
107 // 37e3c22000-37e3c23000 rw-p 00022000 00:11 49532 /lib64/ld-2.14.90.so */
108 unsigned long start, end, offset;
109 i = fscanf (f, "%lx-%lx %*s %lx %*x:%*x %*x", &start, &end, &offset);
110 assert (errno == 0);
111 if (i != 3)
112 break;
113 char *filename = strdup ("");
114 assert (filename);
115 size_t filename_len = 0;
116 for (;;)
117 {
118 int c = fgetc (f);
119 assert (c != EOF);
120 if (c == '\n')
121 break;
122 if (c == ' ' && *filename == '\0')
123 continue;
124 filename = realloc (filename, filename_len + 2);
125 assert (filename);
126 filename[filename_len++] = c;
127 filename[filename_len] = '\0';
128 }
129 if (start <= addr && addr < end)
130 {
131 i = fclose (f);
132 assert (errno == 0);
133 assert (i == 0);
134
135 *basep = start - offset;
136 return filename;
137 }
138 free (filename);
139 }
140 *basep = 0;
141 return NULL;
142}
143
144/* Add module containing ADDR to the DWFL address space.
145
146 dwfl_report_elf call here violates Dwfl manipulation as one should call
147 dwfl_report only between dwfl_report_begin_add and dwfl_report_end.
148 Current elfutils implementation does not mind as dwfl_report_begin_add is
149 empty. */
150
151static Dwfl_Module *
152report_module (Dwfl *dwfl, pid_t child, Dwarf_Addr addr)
153{
154 GElf_Addr base;
155 char *long_name = maps_lookup (child, addr, &base);
156 if (!long_name)
157 return NULL; // not found
158 Dwfl_Module *mod = dwfl_report_elf (dwfl, long_name, long_name, -1,
159 base, false /* add_p_vaddr */);
160 assert (mod);
161 free (long_name);
162 assert (dwfl_addrmodule (dwfl, addr) == mod);
163 return mod;
164}
165
166static pid_t
167next_thread (Dwfl *dwfl, void *dwfl_arg __attribute__ ((unused)),
168 void **thread_argp)
169{
170 if (*thread_argp != NULL)
171 return 0;
172 /* Put arbitrary non-NULL value into *THREAD_ARGP as a marker so that this
173 function returns non-zero PID only once. */
174 *thread_argp = thread_argp;
175 return dwfl_pid (dwfl);
176}
177
178static bool
179set_initial_registers (Dwfl_Thread *thread,
180 void *thread_arg __attribute__ ((unused)))
181{
182 pid_t child = dwfl_pid (dwfl_thread_dwfl (thread));
183
184 struct user_regs_struct user_regs;
185 long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
186 assert (errno == 0);
187 assert (l == 0);
188
189 Dwarf_Word dwarf_regs[17];
190 dwarf_regs[0] = user_regs.rax;
191 dwarf_regs[1] = user_regs.rdx;
192 dwarf_regs[2] = user_regs.rcx;
193 dwarf_regs[3] = user_regs.rbx;
194 dwarf_regs[4] = user_regs.rsi;
195 dwarf_regs[5] = user_regs.rdi;
196 dwarf_regs[6] = user_regs.rbp;
197 dwarf_regs[7] = user_regs.rsp;
198 dwarf_regs[8] = user_regs.r8;
199 dwarf_regs[9] = user_regs.r9;
200 dwarf_regs[10] = user_regs.r10;
201 dwarf_regs[11] = user_regs.r11;
202 dwarf_regs[12] = user_regs.r12;
203 dwarf_regs[13] = user_regs.r13;
204 dwarf_regs[14] = user_regs.r14;
205 dwarf_regs[15] = user_regs.r15;
206 dwarf_regs[16] = user_regs.rip;
207 bool ok = dwfl_thread_state_registers (thread, 0, 17, dwarf_regs);
208 assert (ok);
209
210 /* x86_64 has PC contained in its CFI subset of DWARF register set so
211 elfutils will figure out the real PC value from REGS.
212 So no need to explicitly call dwfl_thread_state_register_pc. */
213
214 return true;
215}
216
217static const Dwfl_Thread_Callbacks callbacks =
218{
219 next_thread,
220 NULL, /* get_thread */
221 memory_read,
222 set_initial_registers,
223 NULL, /* detach */
224 NULL, /* thread_detach */
225};
226
227static int
228frame_callback (Dwfl_Frame *state, void *arg)
229{
230 unsigned *framenop = arg;
231 Dwarf_Addr pc;
232 bool isactivation;
233 if (! dwfl_frame_pc (state, &pc, &isactivation))
234 {
235 error (1, 0, "%s", dwfl_errmsg (-1));
236 return 1;
237 }
238 Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
239
240 /* Get PC->SYMNAME. */
241 Dwfl *dwfl = dwfl_thread_dwfl (dwfl_frame_thread (state));
242 Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
243 if (mod == NULL)
244 mod = report_module (dwfl, dwfl_pid (dwfl), pc_adjusted);
245 const char *symname = NULL;
246 symname = dwfl_module_addrname (mod, pc_adjusted);
247
248 printf ("#%2u %#" PRIx64 "%4s\t%s\n", (*framenop)++, (uint64_t) pc,
249 ! isactivation ? "- 1" : "", symname);
250 return DWARF_CB_OK;
251}
252
253static int
254thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__ ((unused)))
255{
256 unsigned frameno = 0;
257 switch (dwfl_thread_getframes (thread, frame_callback, &frameno))
258 {
259 case 0:
260 break;
261 case -1:
262 error (1, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
263 break;
264 default:
265 abort ();
266 }
267 return DWARF_CB_OK;
268}
269
270int
271main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
272{
273 /* We use no threads here which can interfere with handling a stream. */
274 __fsetlocking (stdin, FSETLOCKING_BYCALLER);
275 __fsetlocking (stdout, FSETLOCKING_BYCALLER);
276 __fsetlocking (stderr, FSETLOCKING_BYCALLER);
277
278 /* Set locale. */
279 (void) setlocale (LC_ALL, "");
280
281 elf_version (EV_CURRENT);
282
283 pid_t child = fork ();
284 switch (child)
285 {
286 case -1:
287 assert (errno == 0);
288 assert (0);
289 case 0:;
290 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
291 assert (errno == 0);
292 assert (l == 0);
293 raise (SIGUSR1);
294 return 0;
295 default:
296 break;
297 }
298
299 int status;
300 pid_t pid = waitpid (child, &status, 0);
301 assert (errno == 0);
302 assert (pid == child);
303 assert (WIFSTOPPED (status));
304 assert (WSTOPSIG (status) == SIGUSR1);
305
306 static char *debuginfo_path;
307 static const Dwfl_Callbacks offline_callbacks =
308 {
309 .find_debuginfo = dwfl_standard_find_debuginfo,
310 .debuginfo_path = &debuginfo_path,
311 .section_address = dwfl_offline_section_address,
312 .find_elf = find_elf,
313 };
314 Dwfl *dwfl = dwfl_begin (&offline_callbacks);
315 assert (dwfl);
316
317 struct user_regs_struct user_regs;
318 long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
319 assert (errno == 0);
320 assert (l == 0);
321 report_module (dwfl, child, user_regs.rip);
322
323 bool ok = dwfl_attach_state (dwfl, EM_NONE, child, &callbacks, NULL);
324 assert (ok);
325
326 /* Multiple threads are not handled here. */
327 int err = dwfl_getthreads (dwfl, thread_callback, NULL);
328 assert (! err);
329
330 dwfl_end (dwfl);
331 kill (child, SIGKILL);
332 pid = waitpid (child, &status, 0);
333 assert (errno == 0);
334 assert (pid == child);
335 assert (WIFSIGNALED (status));
336 assert (WTERMSIG (status) == SIGKILL);
337
338 return EXIT_SUCCESS;
339}
340
341#endif /* x86_64 */