Commits

Anonymous committed 0818c23

add APIs for locating type information for an address or type name.

Some more API tidying readying for APIv3.

Comments (0)

Files changed (12)

 libgimli_ana_la_SOURCES = \
 	trace.c linux.c elf.c hash.c elf-read.c dwarf-read.c dwarf-unwind.c \
 	dwarf-expr.c darwin.c solaris.c demangle.c freebsd.c proc.c \
-	proc_service.c symbols.c types.c maps.c apiv2.c print.c slab.c
+	proc_service.c symbols.c types.c maps.c apiv2.c print.c slab.c \
+	apiv3.c
 
 libgimli_la_SOURCES = \
   heartbeat.c
   int buflen, int *lineno)
 {
   uint64_t l;
-  int ret = dwarf_determine_source_line_number(the_proc, addr, buf, buflen, &l);
+  int ret = gimli_determine_source_line_number(the_proc,
+      (gimli_addr_t)addr, buf, buflen, &l);
   if (ret) {
     *lineno = (int)l;
   }
 
 static char *v2_get_string_symbol(const char *obj, const char *name)
 {
-  struct gimli_symbol *sym;
-
-  sym = gimli_sym_lookup(the_proc, obj, name);
-  if (sym) {
-    void *addr;
-
-    if (gimli_read_mem(the_proc, sym->addr, &addr,
-          sizeof(addr)) == sizeof(addr)) {
-      return gimli_read_string(the_proc, (gimli_addr_t)addr);
-    }
-  }
-  return NULL;
+  return gimli_get_string_symbol(the_proc, obj, name);
 }
 
 static int v2_copy_from_symbol(const char *obj, const char *name,
   int deref, void *buf, uint32_t size)
 {
-  struct gimli_symbol *sym;
-
-  sym = gimli_sym_lookup(the_proc, obj, name);
-  if (sym) {
-    void *addr = (void*)sym->addr;
-
-    while (deref--) {
-      if (gimli_read_mem(the_proc, (gimli_addr_t)addr,
-            &addr, sizeof(addr)) != sizeof(addr)) {
-        return 0;
-      }
-    }
-
-    return gimli_read_mem(the_proc, (gimli_addr_t)addr, buf, size) == size;
-  }
-  return 0;
+  return gimli_copy_from_symbol(obj, name, deref, buf, size);
 }
 
 static struct gimli_symbol *v1_sym_lookup(
+/*
+ * Copyright (c) 2012 Message Systems, Inc. All rights reserved
+ * For licensing information, see:
+ * https://bitbucket.org/wez/gimli/src/tip/LICENSE
+ */
+#include "impl.h"
+
+int gimli_read_pointer(gimli_proc_t proc, gimli_addr_t addr, gimli_addr_t *val)
+{
+  uint64_t p64;
+  uint32_t p32;
+
+  *val = 0;
+  if (sizeof(void*) == 8) { // FIXME: data model aware
+    if (gimli_read_mem(proc, addr, &p64, sizeof(p64)) == sizeof(p64)) {
+      *val = p32;
+      return 1;
+    }
+    return 0;
+  }
+  /* 32-bit target */
+  if (gimli_read_mem(proc, addr, &p32, sizeof(p32)) == sizeof(p32)) {
+    *val = p32;
+    return 1;
+  }
+  return 0;
+}
+
+char *gimli_get_string_symbol(gimli_proc_t proc,
+    const char *obj, const char *name)
+{
+  struct gimli_symbol *sym;
+
+  sym = gimli_sym_lookup(proc, obj, name);
+  if (sym) {
+    gimli_addr_t addr;
+
+    if (gimli_read_pointer(proc, sym->addr, &addr)) {
+      return gimli_read_string(proc, addr);
+    }
+  }
+  return NULL;
+}
+
+int gimli_copy_from_symbol(const char *obj, const char *name,
+  int deref, void *buf, uint32_t size)
+{
+  struct gimli_symbol *sym;
+
+  sym = gimli_sym_lookup(the_proc, obj, name);
+  if (sym) {
+    gimli_addr_t addr = sym->addr;
+
+    while (deref--) {
+      if (!gimli_read_pointer(the_proc, addr, &addr)) {
+        return 0;
+      }
+    }
+
+    return gimli_read_mem(the_proc, addr, buf, size) == size;
+  }
+  return 0;
+}
+
+
+
+/* vim:ts=2:sw=2:et:
+ */
+
 
 CFLAGS=`echo $CFLAGS | sed -e 's/-O2//;'`
 CFLAGS="-D_REENTRANT $CFLAGS"
+if test "$GCC" == "yes" ; then
+  CFLAGS="$CFLAGS -Wmissing-prototypes -Wformat"
+fi
 MODULE_SUFFIX=".so"
 SHLIB_SUFFIX=".so"
 DWARF_DEBUG_FLAGS="-gdwarf-2 -g3"
   return a->addr - b->addr;
 }
 
-static int search_compare_line(const void *pc, const void *L)
+static int search_compare_line(const void *addrp, const void *L)
 {
   struct gimli_line_info *line = (struct gimli_line_info*)L;
+  gimli_addr_t pc = *(gimli_addr_t*)addrp;
 
   if (pc < line->addr) {
     return -1;
 
 /* read dwarf info to determine the source/line information for a given
  * address */
-int dwarf_determine_source_line_number(gimli_proc_t proc,
-  void *pc, char *src, int srclen,
+int gimli_determine_source_line_number(gimli_proc_t proc,
+  gimli_addr_t pc, char *src, int srclen,
   uint64_t *lineno)
 {
   struct gimli_object_mapping *m;
   gimli_mapped_object_t f;
   struct gimli_line_info *linfo;
 
-  m = gimli_mapping_for_addr(proc, (gimli_addr_t)pc);
+  m = gimli_mapping_for_addr(proc, pc);
   if (!m) return 0;
   f = m->objfile;
 
     pc -= m->base;
   }
 
-  linfo = bsearch(pc, f->lines, f->linecount, sizeof(*linfo),
+  linfo = bsearch(&pc, f->lines, f->linecount, sizeof(*linfo),
       search_compare_line);
 
   if (linfo) {
           f->lines = realloc(f->lines, f->linealloc * sizeof(*linfo));
         }
         linfo = &f->lines[f->linecount++];
-        linfo->filename = (char*)filenames[regs.file];
+        linfo->filename = filenames[regs.file];
         linfo->lineno = regs.line;
-        linfo->addr = regs.address;
+        linfo->addr = (gimli_addr_t)regs.address;
       }
     }
   }
   tag = dw_read_uleb128(&abbr, file->abbr.end);
   memcpy(&has_children, abbr, sizeof(has_children));
   abbr += sizeof(has_children);
+  if (has_children != 0 && has_children != 1) {
+    printf("invalid value for has_children! %d\n", has_children);
+    abort();
+  }
 
   die = gimli_slab_alloc(&file->dieslab);
   memset(die, 0, sizeof(*die));
     }
   }
 
+#if 0
+  printf("process_die stopping at offset %" PRIx64 "\n",
+      data - file->debug_info.start);
+#endif
   *datap = data;
   return die;
 }
   return NULL;
 }
 
+static struct gimli_dwarf_die *find_die_r(struct gimli_dwarf_die *die, uint64_t offset)
+{
+  struct gimli_dwarf_die *kid, *res;
+
+  if (die->offset == offset) {
+    return die;
+  }
+  STAILQ_FOREACH(kid, &die->kids, siblings) {
+    if (kid->offset == offset) {
+      return kid;
+    }
+  }
+  STAILQ_FOREACH(kid, &die->kids, siblings) {
+    res = find_die_r(kid, offset);
+    if (res) {
+      return res;
+    }
+  }
+  return NULL;
+}
+
 struct gimli_dwarf_die *gimli_dwarf_get_die(
   gimli_mapped_object_t f,
   uint64_t offset)
 {
-  struct gimli_dwarf_die *die = NULL, *kid;
+  struct gimli_dwarf_die *die = NULL, *res;
   struct gimli_dwarf_cu *cu;
 
   cu = find_cu(f, offset);
 
   if (cu) {
     STAILQ_FOREACH(die, &cu->dies, siblings) {
-      if (die->offset == offset) {
-        return die;
-      }
-      STAILQ_FOREACH(kid, &die->kids, siblings) {
-        if (kid->offset == offset) {
-          return kid;
-        }
+      res = find_die_r(die, offset);
+      if (res) {
+        return res;
       }
     }
   }
 
-  printf("get_die: %" PRIx64 " MISSING\n", offset);
+  printf("get_die: %" PRIx64 " MISSING cu=%p %" PRIx64 "-%" PRIx64 "\n",
+      offset, cu, cu->offset, cu->end);
   return NULL;
 }
 
   return NULL;
 }
 
-int gimli_dwarf_die_get_uint64_t_attr(
+static int gimli_dwarf_die_get_uint64_t_attr(
   struct gimli_dwarf_die *die, uint64_t attrcode, uint64_t *val)
 {
   struct gimli_dwarf_attr *attr;
     memcpy(&seg_size, data, sizeof(seg_size));
     data += sizeof(seg_size);
 
+    if (seg_size) {
+      printf("DWARF: I don't support segmented debug_aranges data\n");
+      return 0;
+    }
+
+//    printf("arange: ver %d addr_size %d seg %d\n", ver, addr_size, seg_size);
+
     /* align to double-addr-size boundary */
     mask = (2 * addr_size) - 1;
     data += mask;
 
     while (data < next) {
       /* now we have a series of tuples */
-      void *addr;
+      gimli_addr_t addr;
       uint64_t l;
       struct dw_die_arange *arange;
 
-      memcpy(&addr, data, sizeof(addr));
-      data += sizeof(addr);
+      if (addr_size == 8) {
+        memcpy(&l, data, sizeof(l));
+        data += sizeof(l);
+        addr = l;
+      } else {
+        memcpy(&len32, data, sizeof(len32));
+        data += sizeof(len32);
+        addr = len32;
+      }
+
       if (data >= next) break;
 
-      if (sizeof(void*) == 8) {
+      if (addr_size == 8) {
         memcpy(&l, data, sizeof(l));
         data += sizeof(l);
       } else {
         m->objfile->arange = realloc(m->objfile->arange, m->objfile->alloc_arange * sizeof(*arange));
       }
       arange = &m->objfile->arange[m->objfile->num_arange++];
-      arange->addr = (uint64_t)(intptr_t)addr;
+      arange->addr = addr;
       arange->len = l;
       arange->di_offset = di_offset;
 
-//      printf("arange: pc=%p addr=%p %llx\n", cur->st.pc, addr, l);
+//      printf("arange: addr=" PTRFMT " 0x%" PRIx64 "\n", addr, l);
     }
     data = next;
   }
   return 1;
 }
 
+/* Given a CU, locate the DIE corresponding to the provided data address */
+static struct gimli_dwarf_die *find_var_die_for_addr(gimli_proc_t proc,
+    struct gimli_object_mapping *m,
+    struct gimli_dwarf_cu *cu, gimli_addr_t addr)
+{
+  struct gimli_dwarf_die *die, *kid;
+  struct gimli_unwind_cursor cur;
+  uint64_t frame_base = 0;
+  uint64_t comp_unit_base = 0;
+  int is_stack = 0;
+  struct gimli_dwarf_attr *frame_base_attr;
+
+  memset(&cur, 0, sizeof(cur));
+  cur.proc = proc;
+
+  STAILQ_FOREACH(die, &cu->dies, siblings) {
+    uint64_t lopc, hipc;
+
+    if (die->tag != DW_TAG_compile_unit) {
+      printf("DIE is not a compile unit!? tag=0x%" PRIx64 "\n", die->tag);
+      continue;
+    }
+
+    gimli_dwarf_die_get_uint64_t_attr(die,
+        DW_AT_low_pc, &comp_unit_base);
+
+    frame_base_attr = gimli_dwarf_die_get_attr(die, DW_AT_frame_base);
+    if (frame_base_attr) {
+      switch (frame_base_attr->form) {
+        case DW_FORM_block:
+          dw_eval_expr(&cur, (uint8_t*)frame_base_attr->ptr, frame_base_attr->code,
+              0, &frame_base, NULL, &is_stack);
+          break;
+        case DW_FORM_data8:
+          dw_calc_location(&cur, comp_unit_base, m,
+              frame_base_attr->code, &frame_base, NULL, &is_stack);
+          break;
+        default:
+          printf("Unhandled frame base form 0x%" PRIx64 "\n",
+              frame_base_attr->form);
+          return 0;
+      }
+    }
+
+    /* this is the die for the compilation unit; we need to walk
+     * through it and find the data it contains */
+    STAILQ_FOREACH(kid, &die->kids, siblings) {
+      uint64_t res = 0;
+      is_stack = 1;
+      struct gimli_dwarf_attr *location, *type, *name;
+
+      if (kid->tag != DW_TAG_variable && kid->tag != DW_TAG_constant) {
+//        printf("skipping kid with tag 0x%" PRIx64 "\n", kid->tag);
+        continue;
+      }
+
+      location = gimli_dwarf_die_get_attr(kid, DW_AT_location);
+      if (!location) {
+        continue;
+      }
+
+      switch (location->form) {
+        case DW_FORM_block:
+          if (!dw_eval_expr(&cur, (uint8_t*)location->ptr, location->code,
+                frame_base, &res, NULL, &is_stack)) {
+            res = 0;
+          }
+          break;
+        case DW_FORM_data8:
+          if (!dw_calc_location(&cur, comp_unit_base, m,
+                location->code, &res, NULL, &is_stack)) {
+            res = 0;
+          }
+          break;
+        default:
+          printf("Unhandled location form 0x%" PRIx64 "\n", location->form);
+          res = 0;
+      }
+
+      if (res == addr) {
+        return kid;
+      }
+    }
+  }
+
+  /* no joy */
+  return NULL;
+}
+
+/* attempt to locate a DW_TAG_variable or DW_TAG_constant die
+ * corresponding to the provided data address.
+ * We make this attempt using the debug_aranges data, but it
+ * may not be successful, as gcc doesn't appear to generate
+ * it for the data segment, despite the DWARF standard indicating
+ * that it can be used in that fashion.
+ * In that case, we have to fall back to crawling the CU data.
+ * */
+static struct gimli_dwarf_die *gimli_dwarf_get_die_for_data(
+    gimli_proc_t proc, gimli_addr_t pc)
+{
+  struct gimli_object_mapping *m;
+  struct gimli_dwarf_cu *cu;
+  struct dw_die_arange *arange;
+  struct gimli_dwarf_die *die;
+  gimli_mapped_object_t file;
+  const uint8_t *cuptr;
+  uint64_t off;
+
+  m = gimli_mapping_for_addr(proc, pc);
+  if (!m) {
+    return NULL;
+  }
+  file = m->objfile;
+
+  if (!file->elf) {
+    return NULL;
+  }
+
+  if (!file->arange && !load_arange(m)) {
+    return NULL;
+  }
+
+  arange = bsearch(&pc, file->arange, file->num_arange,
+      sizeof(*arange), search_compare_arange);
+  if (arange) {
+    /* arange gives us a pointer to the CU */
+    cu = find_cu(file, arange->di_offset);
+    if (!cu) {
+      cu = load_cu(file, arange->di_offset);
+    }
+    if (cu) {
+      return find_var_die_for_addr(proc, m, cu, pc);
+    }
+  }
+
+  cuptr = file->debug_info.start;
+  while (cuptr < file->debug_info.end) {
+    off = cuptr - file->debug_info.start;
+    cu = find_cu(file, off);
+    if (!cu) {
+      cu = load_cu(file, off);
+    }
+    if (!cu) break;
+    cuptr = file->debug_info.start + cu->end;
+
+    die = find_var_die_for_addr(proc, m, cu, pc);
+    if (die) return die;
+  }
+
+  return NULL;
+}
+
 struct gimli_dwarf_die *gimli_dwarf_get_die_for_pc(gimli_proc_t proc, gimli_addr_t pc)
 {
   struct gimli_object_mapping *m;
       hipc = arange->len;
 
       if (kid->tag != DW_TAG_subprogram) {
-//        printf("skipping kid with tag 0x%" PRIx64 "\n", kid->tag);
         continue;
       }
 
         continue;
       }
 
-#if 0
-      printf("consider pc:" PTRFMT " vs " PTRFMT " - " PTRFMT " (m->base=%" PRIx64 ")\n",
-          pc, lopc, hipc, m->base);
-#endif
-
       if (pc >= lopc && pc <= hipc) {
         return kid;
       }
     }
-
-#if 0
-    lopc = 0;
-    hipc = 0;
-      gimli_dwarf_die_get_uint64_t_attr(die, DW_AT_low_pc, &lopc);
-      gimli_dwarf_die_get_uint64_t_attr(die, DW_AT_high_pc, &hipc);
-
-      printf("bottom pc:" PTRFMT " vs " PTRFMT " - " PTRFMT " (m->base=%" PRIx64 ")\n",
-          pc, lopc, hipc, m->base);
-#endif
   }
 
   /* no joy */
-//  printf("fell out bottom; no subprogram in range for pc " PTRFMT "\n", pc);
   return NULL;
 }
 
   return 0;
 }
 
-static int do_before(
-    struct gimli_unwind_cursor *cur,
-//    int tid, int frameno, void *pcaddr, void *context,
-    const char *datatype, const char *varname,
-    void *varaddr, uint64_t varsize)
-{
-  gimli_mapped_object_t file;
-
-#if 0
-  for (file = cur->proc->files; file; file = file->next) {
-    if (file->tracer_module &&
-        file->tracer_module->api_version >= 2 &&
-        file->tracer_module->before_print_frame_var) {
-      if (file->tracer_module->before_print_frame_var(
-            &ana_api, file->objname, cur->tid, cur->frameno,
-            cur->st.pc, (void*)cur, datatype, varname, varaddr, varsize)
-          == GIMLI_ANA_SUPPRESS) {
-        return 1;
-      }
-    }
-  }
-#endif
-
-  /* ideally would like to factor this out somewhere nicer...
-   * When we see a siginfo_t, replace our usual render with a more terse
-   * and readable version; siginfo_t is quite a big structure with a lot
-   * of union fields that are quite noisy to read */
-  if (!strcmp(datatype, "siginfo_t *")) {
-    char namebuf[1024];
-    siginfo_t si;
-
-    if (gimli_read_mem(cur->proc, (gimli_addr_t)varaddr,
-          &si, sizeof(si)) == sizeof(si)) {
-      gimli_render_siginfo(cur->proc, &si, namebuf, sizeof(namebuf));
-      printf("  siginfo_t *%s = %p\n    %s\n",
-          varname, varaddr, namebuf);
-      return 1;
-    }
-  }
-  return 0;
-}
-
-static int do_after(
-    struct gimli_unwind_cursor *cur, // FIXME: gimli_stack_frame_t
-//    int tid, int frameno, void *pcaddr, void *context,
-    const char *datatype, const char *varname,
-    void *varaddr, uint64_t varsize)
-{
-  gimli_mapped_object_t file;
-
-#if 0
-  for (file = cur->proc->files; file; file = file->next) {
-    if (file->tracer_module &&
-        file->tracer_module->api_version >= 2 &&
-        file->tracer_module->after_print_frame_var) {
-      file->tracer_module->after_print_frame_var(
-          &ana_api, file->objname, cur->tid, cur->frameno,
-          cur->st.pc, (void*)cur, datatype, varname, varaddr, varsize);
-    }
-  }
-#endif
-  return 0;
-}
 
 static gimli_type_t load_type(
     gimli_mapped_object_t file,
 
     memt = load_type(file, type);
     if (!memt) {
-      printf("failed to load type info for member %s\n", mname->ptr);
+#if 0
+      printf("failed to load type info for member %s %" PRIx64 "\n", mname->ptr,
+          type->code);
+#endif
       continue;
     }
     offset = 0;
     return NULL;
   }
 
+  /* find target type */
   type = gimli_dwarf_die_get_attr(die, DW_AT_type);
-  t = array_dim(file, STAILQ_FIRST(&die->kids), load_type(file, type));
+  t = load_type(file, type);
+  if (!t) {
+    return NULL;
+  }
+
+  t = array_dim(file, STAILQ_FIRST(&die->kids), t);
   return t;
 }
 
   return 1;
 }
 
-static gimli_type_t load_type(
+static gimli_type_t load_type_die(
     gimli_mapped_object_t file,
-    struct gimli_dwarf_attr *type)
+    struct gimli_dwarf_die *die)
 {
   gimli_type_t t = NULL, target = NULL;
-  struct gimli_dwarf_die *die;
   uint64_t ate, size = 0;
   struct gimli_type_encoding enc;
   const char *type_name = NULL;
   struct gimli_dwarf_attr *name = NULL;
-
-  die = gimli_dwarf_get_die(file, type->code);
-  if (!die) {
-    return NULL;
-  }
+  struct gimli_dwarf_attr *type = NULL;
 
   if (file->die_to_type) {
     if (gimli_hash_find_ptr(file->die_to_type, die, (void**)&t)) {
   return t;
 }
 
+static gimli_type_t load_type(
+    gimli_mapped_object_t file,
+    struct gimli_dwarf_attr *type)
+{
+  struct gimli_dwarf_die *die;
+
+  die = gimli_dwarf_get_die(file, type->code);
+  if (!die) {
+    return NULL;
+  }
+
+  return load_type_die(file, die);
+}
+
+static void load_types_in_die(gimli_mapped_object_t file,
+    struct gimli_dwarf_die *die)
+{
+  struct gimli_dwarf_die *kid;
+
+  switch (die->tag) {
+    case DW_TAG_base_type:
+    case DW_TAG_pointer_type:
+    case DW_TAG_const_type:
+    case DW_TAG_volatile_type:
+    case DW_TAG_restrict_type:
+    case DW_TAG_typedef:
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+    case DW_TAG_array_type:
+    case DW_TAG_enumeration_type:
+    case DW_TAG_subroutine_type:
+      load_type_die(file, die);
+      break;
+  }
+
+  STAILQ_FOREACH(kid, &die->kids, siblings) {
+    load_types_in_die(file, kid);
+  }
+}
+
+/* pull in all the CU's and register all the types we find;
+ * this is relatively expensive but we only do it based
+ * on user request; a module has to ask for a type by name
+ * that we haven't already loaded */
+void gimli_dwarf_load_all_types(gimli_mapped_object_t file)
+{
+  struct gimli_dwarf_cu *cu;
+  struct gimli_dwarf_die *die;
+  const uint8_t *cuptr;
+  uint64_t off;
+
+  if (!init_debug_info(file)) {
+    return;
+  }
+
+  cuptr = file->debug_info.start;
+  while (cuptr < file->debug_info.end) {
+    off = cuptr - file->debug_info.start;
+    cu = find_cu(file, off);
+    if (!cu) {
+      cu = load_cu(file, off);
+    }
+    if (!cu) break;
+    cuptr = file->debug_info.start + cu->end;
+
+    /* now walk the DIEs and map the types */
+    STAILQ_FOREACH(die, &cu->dies, siblings) {
+      load_types_in_die(file, die);
+    }
+  }
+}
+
+/* Locate the DIE for a data address and load its type
+ * information */
+gimli_type_t gimli_dwarf_load_type_for_data(gimli_proc_t proc,
+    gimli_addr_t addr)
+{
+  struct gimli_dwarf_die *die;
+  struct gimli_object_mapping *m;
+  gimli_mapped_object_t file;
+  struct gimli_dwarf_attr *type;
+
+  die = gimli_dwarf_get_die_for_data(proc, addr);
+  if (!die) {
+    return NULL;
+  }
+
+  m = gimli_mapping_for_addr(proc, addr);
+  file = m->objfile;
+
+  type = gimli_dwarf_die_get_attr(die, DW_AT_type);
+  if (!type) {
+    return NULL;
+  }
+
+  return load_type(file, type);
+}
+
 static void load_var(
     gimli_stack_frame_t frame,
     struct gimli_dwarf_die *die,
 #endif
   ;
 
-struct gimli_elf_shdr *gimli_get_section_by_index(
+static struct gimli_elf_shdr *gimli_get_section_by_index(
   struct gimli_elf_ehdr *elf, int section)
 {
   struct gimli_elf_shdr *s;
   return NULL;
 }
 
-struct gimli_elf_shdr *gimli_get_elf_section_by_name(struct gimli_elf_ehdr *elf,
+static struct gimli_elf_shdr *gimli_get_elf_section_by_name(struct gimli_elf_ehdr *elf,
   const char *name)
 {
   struct gimli_elf_shdr *s = NULL;
   return NULL;
 }
 
-const char *gimli_get_section_data(struct gimli_elf_ehdr *elf, int section)
+static const char *gimli_get_section_data(struct gimli_elf_ehdr *elf, int section)
 {
   struct gimli_elf_shdr *s;
   int i;
 }
 
 
-const char *gimli_elf_get_string(struct gimli_elf_ehdr *elf,
+static const char *gimli_elf_get_string(struct gimli_elf_ehdr *elf,
   int section, uint64_t off)
 {
   const char *d = gimli_get_section_data(elf, section);
   return GIMLI_ITER_CONT;
 }
 
-void trace_process(int pid)
+static void trace_process(int pid)
 {
   int i;
   struct glider_args args;
 
 
 struct gimli_line_info {
-  char *filename;
+  const char *filename;
   uint64_t lineno;
-  void *addr;
-  void *end;
+  gimli_addr_t addr, end;
 };
 
 #ifdef __MACH__
 int gimli_dwarf_regs_to_thread(struct gimli_unwind_cursor *cur);
 int gimli_thread_regs_to_dwarf(struct gimli_unwind_cursor *cur);
 void *gimli_reg_addr(struct gimli_unwind_cursor *cur, int col);
-int dwarf_determine_source_line_number(gimli_proc_t proc,
-  void *pc, char *src, int srclen,
-  uint64_t *lineno);
 
 char **gimli_init_proctitle(int argc, char **argv);
 void gimli_set_proctitle(const char *fmt, ...);
 gimli_err_t gimli_attach(gimli_proc_t proc);
 gimli_err_t gimli_detach(gimli_proc_t proc);
 
-const char *gimli_data_sym_name(gimli_proc_t proc, gimli_addr_t addr, char *buf, int buflen);
-const char *gimli_pc_sym_name(gimli_proc_t proc, gimli_addr_t addr, char *buf, int buflen);
-int gimli_read_mem(gimli_proc_t proc, gimli_addr_t src, void *dest, int len);
-int gimli_write_mem(gimli_proc_t proc, gimli_addr_t src, const void *dest, int len);
 struct gimli_symbol *gimli_sym_lookup(gimli_proc_t proc, const char *obj, const char *name);
-char *gimli_read_string(gimli_proc_t proc, gimli_addr_t addr);
 int gimli_get_parameter(void *context, const char *varname,
   const char **datatype, void **addr, uint64_t *size);
 extern struct gimli_symbol *find_symbol_for_addr(gimli_mapped_object_t f,
   struct gimli_dwarf_attr *type);
 int gimli_dwarf_read_value(gimli_proc_t proc, gimli_addr_t addr, int is_stack, void *out, uint64_t size);
 
+gimli_type_t gimli_dwarf_load_type_for_data(gimli_proc_t proc,
+    gimli_addr_t addr);
 
+int tracer_attach(int pid);
+void gimli_proc_service_destroy(gimli_proc_t proc);
+#ifdef __linux__
+long gimli_ptrace(int cmd, pid_t pid, void *addr, void *data);
+#endif
+int gimli_init_unwind(struct gimli_unwind_cursor *cur,
+  struct gimli_thread_state *st);
+int gimli_is_signal_frame(struct gimli_unwind_cursor *cur);
+void gimli_render_frame(int tid, int nframe, gimli_stack_frame_t frame);
+
+int dw_calc_location(struct gimli_unwind_cursor *cur,
+  uint64_t compilation_unit_base_addr,
+  struct gimli_object_mapping *m, uint64_t offset, uint64_t *res,
+  gimli_object_file_t elf, int *is_stack);
+void gimli_dwarf_load_all_types(gimli_mapped_object_t file);
+
+void gimli_object_file_destroy(struct gimli_elf_ehdr *elf);
+void gimli_hash_diagnose(gimli_hash_t h);
+void gimli_dw_fde_destroy(gimli_mapped_object_t file);
 
 /* ensure that the table size is a power of 2 */
 static inline uint32_t power_2(uint32_t x)
 
 struct gimli_symbol {
   /** de-mangled symbol name */
-  char *name;
+  const char *name;
   /** raw, un-mangled symbol name */
-  char *rawname;
+  const char *rawname;
   /** resolved address in the target process */
   gimli_addr_t addr;
   /** size of the symbol. Not all systems provide this information
 
 #define GIMLI_ANA_API_VERSION 3
 
+/* {{{ Deprecated V1 and V2 Gimli APIs enclosed in this block */
+
 struct gimli_proc_stat {
   pid_t pid;
   size_t pr_size;
 
 struct gimli_ana_api {
   int api_version;
-  /** lookup a symbol based on its raw, un-mangled, name */
+  /** lookup a symbol based on its raw, un-mangled, name.
+   * @deprecated use gimli_sym_lookup() instead.
+   * */
   struct gimli_symbol *(*sym_lookup)(const char *obj, const char *name);
 
-  /** compute a readable label for an address */
+  /** compute a readable label for an address.
+   * @deprecated use gimli_data_sym_name() or gimli_pc_sym_name() instead.
+   * */
   const char *(*sym_name)(void *addr, char *buf, int buflen);
 
   /** read memory from the target process, returns the length that
-   * was successfully read */
+   * was successfully read.
+   * @deprecated use gimli_read_mem() or gimli_proc_mem_ref()
+   * */
   int (*read_mem)(void *src, void *dest, int len);
 
   /** read a NUL terminated string from target process.
-   * The caller must free() the memory when it is no longer required */
+   * The caller must free() the memory when it is no longer required.
+   * @deprecated use gimli_read_string()
+   * */
   char *(*read_string)(void *src);
 
 /* API Version 2 begins here */
   /** determine the source filename and line number information
    * for a given code address.
    * Returns 1 if the source information is found, 0 otherwise.
-   * Populates filebuf and lineno if the source is found. */
+   * Populates filebuf and lineno if the source is found.
+   * @deprecated use gimli_determine_source_line_number()
+   * */
   int (*get_source_info)(void *addr, char *buf, int buflen, int *lineno);
 
   /** Given a context, locate a named parameter and return its
    * C-style datatype name, address and size.  Returns 1 if located.
    * 0 otherwise.
-   * context is a gimli_stack_frame_t */
+   * context is a gimli_stack_frame_t.
+   * @deprecated use gimli_stack_frame_resolve_var()
+   * */
   int (*get_parameter)(void *context, const char *varname,
     const char **datatype, void **addr, uint64_t *size);
 
    * This is logically equivalent to sym_lookup, deref'ing the
    * result, and then read_string'ing the result of that.
    * The caller must free() the return memory when it is no longer
-   * required. */
+   * required.
+   * @deprecated use gimli_get_string_symbol()
+   * */
   char *(*get_string_symbol)(const char *obj, const char *name);
 
   /** Lookup a symbol and copy its target into a caller provided
    * de-reference each of those levels to arrive at a final address.
    * SIZE bytes of data will then be read from the final address
    * and copied in the the caller provided buffer.
-   * Returns 0 if SIZE could not be read completely, 1 on success */
+   * Returns 0 if SIZE could not be read completely, 1 on success.
+   * @deprecated use gimli_copy_from_symbol()
+   * */
   int (*copy_from_symbol)(const char *obj, const char *name,
     int deref, void *addr, uint32_t size);
 
   const struct gimli_ana_api *api);
 extern struct gimli_ana_module *gimli_ana_init(const struct gimli_ana_api *api);
 
+/* }}} end of deprecated V1 and V2 APIs */
+
 /* Version 3 APIs start here */
 
 /** hash table utility API */
     gimli_stack_frame_visit_f func,
     void *arg);
 
+/** Given ADDR and a type definition, print out the contents of
+ * ADDR interpreted as that type, using VARNAME as the hypothetical
+ * name of the variable */
+int gimli_print_addr_as_type(gimli_proc_t proc, const char *varname,
+    gimli_type_t t, gimli_addr_t addr);
+
+const char *gimli_data_sym_name(gimli_proc_t proc,
+    gimli_addr_t addr, char *buf, int buflen);
+const char *gimli_pc_sym_name(gimli_proc_t proc,
+    gimli_addr_t addr, char *buf, int buflen);
+struct gimli_symbol *gimli_sym_lookup(gimli_proc_t proc,
+    const char *obj, const char *name);
+
+int gimli_determine_source_line_number(gimli_proc_t proc,
+  gimli_addr_t pc, char *src, int srclen,
+  uint64_t *lineno);
+
+/* {{{ Reading and writing memory */
+
+/** Read memory from SRC address in the target and copy it into the
+ * buffer DEST whose length is LEN.
+ * Returns the number of bytes that were successfully read from the
+ * target */
+int gimli_read_mem(gimli_proc_t proc, gimli_addr_t src, void *dest, int len);
+
+/** Write memory to DEST address in the target by copying it from the
+ * buffer SRC whose length is LEN.
+ * Returns the number of bytes that were successfully written to the
+ * target */
+int gimli_write_mem(gimli_proc_t proc, gimli_addr_t dest, const void *src, int len);
+
+/** Given the address of a pointer in the target, de-reference it
+ * and return the result.
+ * This automatically handles differences in pointer size between
+ * gimli and the target process */
+int gimli_read_pointer(gimli_proc_t proc, gimli_addr_t addr, gimli_addr_t *val);
+
+/** read a NUL terminated string from target process.
+ * The caller must free() the memory when it is no longer required.  */
+char *gimli_read_string(gimli_proc_t proc, gimli_addr_t addr);
+
+/** Lookup a symbol, treat it as a char* in the target and return a copy of the
+ * NUL-terminated string to which it points.
+ * This is logically equivalent to
+ * sym_lookup, deref'ing the result, and then read_string'ing the result of
+ * that.  The caller must free() the returned memory when it is no longer
+ * required. */
+char *gimli_get_string_symbol(gimli_proc_t proc,
+    const char *obj, const char *name);
+
+/** Lookup a symbol and copy its target into a caller provided buffer.
+ * If deref is non-zero, the symbol value is treated as pointer with that
+ * many levels of indirection; this function will de-reference each of those
+ * levels to arrive at a final address.  SIZE bytes of data will then be read
+ * from the final address and copied in the the caller provided buffer.
+ * Returns 0 if SIZE could not be read completely, 1 on success */
+int gimli_copy_from_symbol(const char *obj, const char *name,
+  int deref, void *buf, uint32_t size);
 
 /** Returns mapping to the target address space.
- * Depending on the system and the target, this may be a live mapping
- * wherein writes to the local area are immediately reflected in the
- * target, or it may be a buffered copy of the data that will not
- * update in the target until gimli_proc_mem_commit() is called.
- * For portability, if you want to ensure that writes take effect,
- * you must always call gimli_proc_mem_commit at the appropriate time.
+ * Writes will be buffered and will not reflect in the target
+ * until gimli_proc_mem_commit() is called.
  *
  * NOTE! always verify the length of the mapping, as it may be
  * shorter than you requested, especially if a portion of the
  * range is invalid.
- *
- * NOTE! on systems that can mmap the ref, some memory accesses
- * may lead to a fault in the local process!
  * */
 gimli_err_t gimli_proc_mem_ref(gimli_proc_t p,
     gimli_addr_t addr, size_t size, gimli_mem_ref_t *ref);
 
-/** For targets that don't support direct mmap of the address space,
- * this function will apply changes in the mapping to the target.
+/** apply changes in the mapping to the target.
  * Changes are not guaranteed to be applied unless you call this
  * function at the appropriate time */
 gimli_err_t gimli_proc_mem_commit(gimli_mem_ref_t ref);
 gimli_addr_t gimli_mem_ref_target(gimli_mem_ref_t mem);
 
 /** Returns the base address of a mapping in my address space.
- * This is the start of the readable/writable mapped view of
+ * This is the start of the readable/writable view of
  * the target process */
 void *gimli_mem_ref_local(gimli_mem_ref_t mem);
 
 /** adds a reference to a mapping */
 void gimli_mem_ref_addref(gimli_mem_ref_t mem);
 
+/* }}} */
 
-/* --- types */
+/* {{{ --- types */
 
+gimli_type_t gimli_find_type_by_name(gimli_proc_t proc,
+    const char *objname,
+    const char *tname);
+
+gimli_type_t gimli_find_type_by_addr(gimli_proc_t proc,
+    gimli_addr_t addr);
 
 /** create a new empty type collection object */
 gimli_type_collection_t gimli_type_collection_new(void);
 struct gimli_type_arinfo {
   /** type of array elements */
   gimli_type_t contents;
-#if 0
-  /** type of array index */
-  gimli_type_t idx;
-#endif
   /** size of the array */
   uint32_t nelems;
 };
     gimli_type_visit_f func,
     void *arg);
 
+/* }}} */
+
 #ifdef __cplusplus
 }
 #endif
   char addrkey[64];
   int dummy;
 
-  data->suppress = 0;
+  if (data->frame) {
+    data->suppress = 0;
 
-  gimli_hash_iter(data->proc->files, should_suppress_var, data);
-  if (data->suppress) {
-    return GIMLI_ITER_CONT;
+    gimli_hash_iter(data->proc->files, should_suppress_var, data);
+    if (data->suppress) {
+      return GIMLI_ITER_CONT;
+    }
   }
 
   if (!t) {
   }
 
 after:
-  gimli_hash_iter(data->proc->files, after_print_var, data);
+  if (data->frame) {
+    gimli_hash_iter(data->proc->files, after_print_var, data);
+  }
 
   return GIMLI_ITER_CONT;
 }
   }
 }
 
+int gimli_print_addr_as_type(gimli_proc_t proc, const char *varname,
+    gimli_type_t t, gimli_addr_t addr)
+{
+  struct print_data data;
+
+  memset(&data, 0, sizeof(data));
+  data.proc = proc;
+  data.show_decl = 1;
+  data.prefix = " = ";
+  data.suffix = "\n";
+  data.addr = addr;
+  data.size = gimli_type_size(t);
+
+  print_var(&data, t, varname);
+
+  return 1;
+}
+
 void gimli_render_frame(int tid, int nframe, gimli_stack_frame_t frame)
 {
   const char *name;
     name = gimli_pc_sym_name(cur.proc, (gimli_addr_t)cur.st.pc,
         namebuf, sizeof(namebuf));
     printf("#%-2d " PTRFMT " %s", nframe, (PTRFMT_T)cur.st.pc, name);
-    if (dwarf_determine_source_line_number(cur.proc, cur.st.pc,
+    if (gimli_determine_source_line_number(cur.proc, (gimli_addr_t)cur.st.pc,
           filebuf, sizeof(filebuf), &lineno)) {
       printf(" (%s:%" PRId64 ")", filebuf, lineno);
     }
     return 0;
   }
 
-  if (info.ti_lid != proc->pid && gimli_ptrace(PTRACE_DETACH, info.ti_lid, NULL, SIGCONT)) {
+  if (info.ti_lid != proc->pid && 
+      gimli_ptrace(PTRACE_DETACH, info.ti_lid, NULL, (void*)SIGCONT)) {
     fprintf(stderr, "resume_threads: failed to detach from thread %d %s\n",
         info.ti_lid, strerror(errno));
   }
   STAILQ_ENTRY(gimli_type) typelist;
 
   int kind;
-  char *name;
+  const char *name;
   char *declname;
   char declbuf[24];
   struct gimli_type_encoding enc;
   struct gimli_type_arinfo arinfo;
 
   struct {
-    char *name;
+    const char *name;
     union {
       struct gimli_type_membinfo info;
       int value;
   return NULL;
 }
 
+struct type_lookup_data {
+  const char *typename;
+  gimli_type_t result;
+};
+
+static gimli_iter_status_t find_type_in_object1(
+    const char *k, int klen, void *item, void *arg)
+{
+  struct type_lookup_data *data = arg;
+  gimli_mapped_object_t file = item;
+
+  if (file->types) {
+    data->result = gimli_type_collection_find_type(file->types, data->typename);
+    if (data->result) {
+      return GIMLI_ITER_STOP;
+    }
+  }
+
+  return GIMLI_ITER_CONT;
+}
+
+static gimli_iter_status_t find_type_in_object2(
+    const char *k, int klen, void *item, void *arg)
+{
+  struct type_lookup_data *data = arg;
+  gimli_mapped_object_t file = item;
+
+  gimli_dwarf_load_all_types(file);
+
+  if (file->types) {
+    data->result = gimli_type_collection_find_type(file->types, data->typename);
+    if (data->result) {
+      return GIMLI_ITER_STOP;
+    }
+  }
+
+  return GIMLI_ITER_CONT;
+}
+
+gimli_type_t gimli_find_type_by_name(gimli_proc_t proc,
+    const char *objname,
+    const char *typename)
+{
+  gimli_mapped_object_t f;
+  struct type_lookup_data data;
+
+  memset(&data, 0, sizeof(data));
+  data.typename = typename;
+
+  /* stage 1: lookup in already processed type data */
+  gimli_hash_iter(proc->files, find_type_in_object1, &data);
+  if (data.result) {
+    return data.result;
+  }
+
+  /* stage 2: more expensive crawl of type data */
+  gimli_hash_iter(proc->files, find_type_in_object2, &data);
+
+  return data.result;
+}
+
+gimli_type_t gimli_find_type_by_addr(gimli_proc_t proc,
+    gimli_addr_t addr)
+{
+  return gimli_dwarf_load_type_for_data(proc, addr);
+}
+
 gimli_type_t gimli_type_collection_find_function(
     gimli_type_collection_t col,
     const char *name)
 
   s = t->enc.bits;
 
+#if 0
   if (s == 0) {
     printf("gimli_type_size: kind=%d name=%s has 0 size!\n",
         t->kind, t->name);
   }
+#endif
   return s;
 }
 
   }
 
   if (name) {
-    t->name = name;//strdup(name);
+    t->name = name;
     if (!t->name) {
       return NULL;
     }