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