blob: a387d8ff9ad73a1a355482a82dde74b1f077ebaf [file] [log] [blame]
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +01001/* 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>
Pino Toscano65251492015-06-26 20:36:01 +020033#if defined(__x86_64__) && defined(__linux__)
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010034#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)
Pino Toscano65251492015-06-26 20:36:01 +020043#endif
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010044
Kurt Roeckx02cefda2014-04-22 21:46:22 +020045#if !defined(__x86_64__) || !defined(__linux__)
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010046
47int
Mark Wielaard813aae02013-12-05 15:26:51 +010048main (int argc __attribute__ ((unused)), char **argv)
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010049{
Mark Wielaard813aae02013-12-05 15:26:51 +010050 fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
51 argv[0]);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010052 return 77;
53}
54
Kurt Roeckx02cefda2014-04-22 21:46:22 +020055#else /* __x86_64__ && __linux__ */
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010056
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);
Ulf Hermanna9d74692017-02-10 15:19:01 +010079
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 }
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010086 *result = l;
87
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010088 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);
Max Filippovc801acf2015-05-04 20:17:52 +030099 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100100 assert (i > 0);
101 FILE *f = fopen (fname, "r");
Max Filippovc801acf2015-05-04 20:17:52 +0300102 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100103 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);
Max Filippovc801acf2015-05-04 20:17:52 +0300110 assert (errno == 0);
Ulf Hermanna9d74692017-02-10 15:19:01 +0100111 if (i != 3)
112 break;
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100113 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);
Max Filippovc801acf2015-05-04 20:17:52 +0300132 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100133 assert (i == 0);
134
135 *basep = start - offset;
136 return filename;
137 }
138 free (filename);
139 }
Ulf Hermanna9d74692017-02-10 15:19:01 +0100140 *basep = 0;
141 return NULL;
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100142}
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);
Ulf Hermanna9d74692017-02-10 15:19:01 +0100156 if (!long_name)
157 return NULL; // not found
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100158 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);
Max Filippovc801acf2015-05-04 20:17:52 +0300186 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100187 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,
Mark Wielaarde962ec32013-12-20 10:09:12 +0100220 NULL, /* get_thread */
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100221 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));
Mark Wielaarda3cc8182016-11-02 13:29:26 +0100263 break;
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100264 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:
Max Filippovc801acf2015-05-04 20:17:52 +0300287 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100288 assert (0);
289 case 0:;
290 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
Max Filippovc801acf2015-05-04 20:17:52 +0300291 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100292 assert (l == 0);
293 raise (SIGUSR1);
Mark Wielaard70c3a532014-01-04 23:28:33 +0100294 return 0;
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100295 default:
296 break;
297 }
298
299 int status;
300 pid_t pid = waitpid (child, &status, 0);
Max Filippovc801acf2015-05-04 20:17:52 +0300301 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100302 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);
Max Filippovc801acf2015-05-04 20:17:52 +0300319 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100320 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);
Max Filippovc801acf2015-05-04 20:17:52 +0300333 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100334 assert (pid == child);
335 assert (WIFSIGNALED (status));
336 assert (WTERMSIG (status) == SIGKILL);
337
338 return EXIT_SUCCESS;
339}
340
341#endif /* x86_64 */