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
47int
48main (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
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  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
89static char *
90maps_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
143static Dwfl_Module *
144report_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
156static pid_t
157next_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
168static bool
169set_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
207static 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
217static int
218frame_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
243static int
244thread_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
259int
260main (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