Home | History | Annotate | Download | only in tests
      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
     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
     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
     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 *
     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 *
    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
    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
    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
    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
    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
    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