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