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
47int
48main (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
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 (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
147static Dwfl_Module *
148report_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
162static pid_t
163next_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
174static bool
175set_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
212static 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
222static int
223frame_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
248static int
249thread_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
265int
266main (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