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 #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 
     43 #if !defined(__x86_64__) || !defined(__linux__)
     44 
     45 int
     46 main (int argc __attribute__ ((unused)), char **argv)
     47 {
     48   fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
     49           argv[0]);
     50   return 77;
     51 }
     52 
     53 #else /* __x86_64__ && __linux__ */
     54 
     55 /* The only arch specific code is set_initial_registers.  */
     56 
     57 static int
     58 find_elf (Dwfl_Module *mod __attribute__ ((unused)),
     59 	  void **userdata __attribute__ ((unused)),
     60 	  const char *modname __attribute__ ((unused)),
     61 	  Dwarf_Addr base __attribute__ ((unused)),
     62 	  char **file_name __attribute__ ((unused)),
     63 	  Elf **elfp __attribute__ ((unused)))
     64 {
     65   /* Not used as modules are reported explicitly.  */
     66   assert (0);
     67 }
     68 
     69 static bool
     70 memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
     71 	     void *dwfl_arg __attribute__ ((unused)))
     72 {
     73   pid_t child = dwfl_pid (dwfl);
     74 
     75   errno = 0;
     76   long l = ptrace (PTRACE_PEEKDATA, child, (void *) (uintptr_t) addr, NULL);
     77   assert_perror (errno);
     78   *result = l;
     79 
     80   /* We could also return false for failed ptrace.  */
     81   return true;
     82 }
     83 
     84 /* Return filename and VMA address *BASEP where its mapping starts which
     85    contains ADDR.  */
     86 
     87 static char *
     88 maps_lookup (pid_t pid, Dwarf_Addr addr, GElf_Addr *basep)
     89 {
     90   char *fname;
     91   int i = asprintf (&fname, "/proc/%ld/maps", (long) pid);
     92   assert_perror (errno);
     93   assert (i > 0);
     94   FILE *f = fopen (fname, "r");
     95   assert_perror (errno);
     96   assert (f);
     97   free (fname);
     98   for (;;)
     99     {
    100       // 37e3c22000-37e3c23000 rw-p 00022000 00:11 49532 /lib64/ld-2.14.90.so */
    101       unsigned long start, end, offset;
    102       i = fscanf (f, "%lx-%lx %*s %lx %*x:%*x %*x", &start, &end, &offset);
    103       assert_perror (errno);
    104       assert (i == 3);
    105       char *filename = strdup ("");
    106       assert (filename);
    107       size_t filename_len = 0;
    108       for (;;)
    109 	{
    110 	  int c = fgetc (f);
    111 	  assert (c != EOF);
    112 	  if (c == '\n')
    113 	    break;
    114 	  if (c == ' ' && *filename == '\0')
    115 	    continue;
    116 	  filename = realloc (filename, filename_len + 2);
    117 	  assert (filename);
    118 	  filename[filename_len++] = c;
    119 	  filename[filename_len] = '\0';
    120 	}
    121       if (start <= addr && addr < end)
    122 	{
    123 	  i = fclose (f);
    124 	  assert_perror (errno);
    125 	  assert (i == 0);
    126 
    127 	  *basep = start - offset;
    128 	  return filename;
    129 	}
    130       free (filename);
    131     }
    132 }
    133 
    134 /* Add module containing ADDR to the DWFL address space.
    135 
    136    dwfl_report_elf call here violates Dwfl manipulation as one should call
    137    dwfl_report only between dwfl_report_begin_add and dwfl_report_end.
    138    Current elfutils implementation does not mind as dwfl_report_begin_add is
    139    empty.  */
    140 
    141 static Dwfl_Module *
    142 report_module (Dwfl *dwfl, pid_t child, Dwarf_Addr addr)
    143 {
    144   GElf_Addr base;
    145   char *long_name = maps_lookup (child, addr, &base);
    146   Dwfl_Module *mod = dwfl_report_elf (dwfl, long_name, long_name, -1,
    147 				      base, false /* add_p_vaddr */);
    148   assert (mod);
    149   free (long_name);
    150   assert (dwfl_addrmodule (dwfl, addr) == mod);
    151   return mod;
    152 }
    153 
    154 static pid_t
    155 next_thread (Dwfl *dwfl, void *dwfl_arg __attribute__ ((unused)),
    156 	     void **thread_argp)
    157 {
    158   if (*thread_argp != NULL)
    159     return 0;
    160   /* Put arbitrary non-NULL value into *THREAD_ARGP as a marker so that this
    161      function returns non-zero PID only once.  */
    162   *thread_argp = thread_argp;
    163   return dwfl_pid (dwfl);
    164 }
    165 
    166 static bool
    167 set_initial_registers (Dwfl_Thread *thread,
    168 		       void *thread_arg __attribute__ ((unused)))
    169 {
    170   pid_t child = dwfl_pid (dwfl_thread_dwfl (thread));
    171 
    172   struct user_regs_struct user_regs;
    173   long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
    174   assert_perror (errno);
    175   assert (l == 0);
    176 
    177   Dwarf_Word dwarf_regs[17];
    178   dwarf_regs[0] = user_regs.rax;
    179   dwarf_regs[1] = user_regs.rdx;
    180   dwarf_regs[2] = user_regs.rcx;
    181   dwarf_regs[3] = user_regs.rbx;
    182   dwarf_regs[4] = user_regs.rsi;
    183   dwarf_regs[5] = user_regs.rdi;
    184   dwarf_regs[6] = user_regs.rbp;
    185   dwarf_regs[7] = user_regs.rsp;
    186   dwarf_regs[8] = user_regs.r8;
    187   dwarf_regs[9] = user_regs.r9;
    188   dwarf_regs[10] = user_regs.r10;
    189   dwarf_regs[11] = user_regs.r11;
    190   dwarf_regs[12] = user_regs.r12;
    191   dwarf_regs[13] = user_regs.r13;
    192   dwarf_regs[14] = user_regs.r14;
    193   dwarf_regs[15] = user_regs.r15;
    194   dwarf_regs[16] = user_regs.rip;
    195   bool ok = dwfl_thread_state_registers (thread, 0, 17, dwarf_regs);
    196   assert (ok);
    197 
    198   /* x86_64 has PC contained in its CFI subset of DWARF register set so
    199      elfutils will figure out the real PC value from REGS.
    200      So no need to explicitly call dwfl_thread_state_register_pc.  */
    201 
    202   return true;
    203 }
    204 
    205 static const Dwfl_Thread_Callbacks callbacks =
    206 {
    207   next_thread,
    208   NULL, /* get_thread */
    209   memory_read,
    210   set_initial_registers,
    211   NULL, /* detach */
    212   NULL, /* thread_detach */
    213 };
    214 
    215 static int
    216 frame_callback (Dwfl_Frame *state, void *arg)
    217 {
    218   unsigned *framenop = arg;
    219   Dwarf_Addr pc;
    220   bool isactivation;
    221   if (! dwfl_frame_pc (state, &pc, &isactivation))
    222     {
    223       error (1, 0, "%s", dwfl_errmsg (-1));
    224       return 1;
    225     }
    226   Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
    227 
    228   /* Get PC->SYMNAME.  */
    229   Dwfl *dwfl = dwfl_thread_dwfl (dwfl_frame_thread (state));
    230   Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
    231   if (mod == NULL)
    232     mod = report_module (dwfl, dwfl_pid (dwfl), pc_adjusted);
    233   const char *symname = NULL;
    234   symname = dwfl_module_addrname (mod, pc_adjusted);
    235 
    236   printf ("#%2u %#" PRIx64 "%4s\t%s\n", (*framenop)++, (uint64_t) pc,
    237 	  ! isactivation ? "- 1" : "", symname);
    238   return DWARF_CB_OK;
    239 }
    240 
    241 static int
    242 thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__ ((unused)))
    243 {
    244   unsigned frameno = 0;
    245   switch (dwfl_thread_getframes (thread, frame_callback, &frameno))
    246     {
    247     case 0:
    248       break;
    249     case -1:
    250       error (1, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
    251     default:
    252       abort ();
    253     }
    254   return DWARF_CB_OK;
    255 }
    256 
    257 int
    258 main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
    259 {
    260   /* We use no threads here which can interfere with handling a stream.  */
    261   __fsetlocking (stdin, FSETLOCKING_BYCALLER);
    262   __fsetlocking (stdout, FSETLOCKING_BYCALLER);
    263   __fsetlocking (stderr, FSETLOCKING_BYCALLER);
    264 
    265   /* Set locale.  */
    266   (void) setlocale (LC_ALL, "");
    267 
    268   elf_version (EV_CURRENT);
    269 
    270   pid_t child = fork ();
    271   switch (child)
    272   {
    273     case -1:
    274       assert_perror (errno);
    275       assert (0);
    276     case 0:;
    277       long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
    278       assert_perror (errno);
    279       assert (l == 0);
    280       raise (SIGUSR1);
    281       return 0;
    282     default:
    283       break;
    284   }
    285 
    286   int status;
    287   pid_t pid = waitpid (child, &status, 0);
    288   assert_perror (errno);
    289   assert (pid == child);
    290   assert (WIFSTOPPED (status));
    291   assert (WSTOPSIG (status) == SIGUSR1);
    292 
    293   static char *debuginfo_path;
    294   static const Dwfl_Callbacks offline_callbacks =
    295     {
    296       .find_debuginfo = dwfl_standard_find_debuginfo,
    297       .debuginfo_path = &debuginfo_path,
    298       .section_address = dwfl_offline_section_address,
    299       .find_elf = find_elf,
    300     };
    301   Dwfl *dwfl = dwfl_begin (&offline_callbacks);
    302   assert (dwfl);
    303 
    304   struct user_regs_struct user_regs;
    305   long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
    306   assert_perror (errno);
    307   assert (l == 0);
    308   report_module (dwfl, child, user_regs.rip);
    309 
    310   bool ok = dwfl_attach_state (dwfl, EM_NONE, child, &callbacks, NULL);
    311   assert (ok);
    312 
    313   /* Multiple threads are not handled here.  */
    314   int err = dwfl_getthreads (dwfl, thread_callback, NULL);
    315   assert (! err);
    316 
    317   dwfl_end (dwfl);
    318   kill (child, SIGKILL);
    319   pid = waitpid (child, &status, 0);
    320   assert_perror (errno);
    321   assert (pid == child);
    322   assert (WIFSIGNALED (status));
    323   assert (WTERMSIG (status) == SIGKILL);
    324 
    325   return EXIT_SUCCESS;
    326 }
    327 
    328 #endif /* x86_64 */
    329