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