• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
47 int
main(int argc,char ** argv)48 main (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 
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   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 
89 static char *
maps_lookup(pid_t pid,Dwarf_Addr addr,GElf_Addr * basep)90 maps_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 
143 static Dwfl_Module *
report_module(Dwfl * dwfl,pid_t child,Dwarf_Addr addr)144 report_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 
156 static pid_t
next_thread(Dwfl * dwfl,void * dwfl_arg,void ** thread_argp)157 next_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 
168 static bool
set_initial_registers(Dwfl_Thread * thread,void * thread_arg)169 set_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 
207 static 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 
217 static int
frame_callback(Dwfl_Frame * state,void * arg)218 frame_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 
243 static int
thread_callback(Dwfl_Thread * thread,void * thread_arg)244 thread_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 
259 int
main(int argc,char ** argv)260 main (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