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 
47 int
main(int argc,char ** argv)48 main (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 
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 
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 
94 static char *
maps_lookup(pid_t pid,Dwarf_Addr addr,GElf_Addr * basep)95 maps_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 
147 static Dwfl_Module *
report_module(Dwfl * dwfl,pid_t child,Dwarf_Addr addr)148 report_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 
162 static pid_t
next_thread(Dwfl * dwfl,void * dwfl_arg,void ** thread_argp)163 next_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 
174 static bool
set_initial_registers(Dwfl_Thread * thread,void * thread_arg)175 set_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 
212 static 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 
222 static int
frame_callback(Dwfl_Frame * state,void * arg)223 frame_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 
248 static int
thread_callback(Dwfl_Thread * thread,void * thread_arg)249 thread_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 
265 int
main(int argc,char ** argv)266 main (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