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 <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
     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
     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 
     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 *
     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 *
    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
    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
    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
    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
    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
    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     case 0:;
    284       long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
    285       assert (l == 0);
    286       raise (SIGUSR1);
    287       return 0;
    288     default:
    289       break;
    290   }
    291 
    292   int status;
    293   pid_t pid = waitpid (child, &status, 0);
    294   assert (pid == child);
    295   assert (WIFSTOPPED (status));
    296   assert (WSTOPSIG (status) == SIGUSR1);
    297 
    298   static char *debuginfo_path;
    299   static const Dwfl_Callbacks offline_callbacks =
    300     {
    301       .find_debuginfo = dwfl_standard_find_debuginfo,
    302       .debuginfo_path = &debuginfo_path,
    303       .section_address = dwfl_offline_section_address,
    304       .find_elf = find_elf,
    305     };
    306   Dwfl *dwfl = dwfl_begin (&offline_callbacks);
    307   assert (dwfl);
    308 
    309   struct user_regs_struct user_regs;
    310   long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
    311   assert (l == 0);
    312   report_module (dwfl, child, user_regs.rip);
    313 
    314   bool ok = dwfl_attach_state (dwfl, EM_NONE, child, &callbacks, NULL);
    315   assert (ok);
    316 
    317   /* Multiple threads are not handled here.  */
    318   int err = dwfl_getthreads (dwfl, thread_callback, NULL);
    319   assert (! err);
    320 
    321   dwfl_end (dwfl);
    322   kill (child, SIGKILL);
    323   pid = waitpid (child, &status, 0);
    324   assert (pid == child);
    325   assert (WIFSIGNALED (status));
    326   assert (WTERMSIG (status) == SIGKILL);
    327 
    328   return EXIT_SUCCESS;
    329 }
    330 
    331 #endif /* x86_64 */
    332