readelf: Add DWARF5 .debug_line support.
[platform/upstream/elfutils.git] / libdw / libdwP.h
index 37f2872..da0383f 100644 (file)
@@ -1,16 +1,30 @@
-/* Internal definitions for libdwarf.
-   Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
-   Written by Ulrich Drepper <drepper@redhat.com>, 2002.
+/* Internal definitions for libdw.
+   Copyright (C) 2002-2011, 2013-2018 Red Hat, Inc.
+   This file is part of elfutils.
 
-   This program is Open Source software; you can redistribute it and/or
-   modify it under the terms of the Open Software License version 1.0 as
-   published by the Open Source Initiative.
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
 
-   You should have received a copy of the Open Software License along
-   with this program; if not, you may obtain a copy of the Open Software
-   License version 1.0 from http://www.opensource.org/licenses/osl.php or
-   by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
-   3001 King Ranch Road, Ukiah, CA 95482.   */
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
 
 #ifndef _LIBDWP_H
 #define _LIBDWP_H 1
 #include <stdbool.h>
 
 #include <libdw.h>
+#include <dwarf.h>
 
 
 /* gettext helper macros.  */
 #define _(Str) dgettext ("elfutils", Str)
 
 
-/* Version of the DWARF specification we support.  */
-#define DWARF_VERSION 2
-
-/* Version of the CIE format.  */
-#define CIE_VERSION 1
-
-
-/* Known location lists.  */
+/* Known location expressions already decoded.  */
 struct loc_s
 {
   void *addr;
-  Dwarf_Loc *loc;
+  Dwarf_Op *loc;
   size_t nloc;
 };
 
+/* Known DW_OP_implicit_value blocks already decoded.
+   This overlaps struct loc_s exactly, but only the
+   first member really has to match.  */
+struct loc_block_s
+{
+  void *addr;
+  unsigned char *data;
+  size_t length;
+};
+
+/* Already decoded .debug_line units.  */
+struct files_lines_s
+{
+  Dwarf_Off debug_line_offset;
+  Dwarf_Files *files;
+  Dwarf_Lines *lines;
+};
+
 /* Valid indeces for the section data.  */
 enum
   {
     IDX_debug_info = 0,
+    IDX_debug_types,
     IDX_debug_abbrev,
     IDX_debug_aranges,
+    IDX_debug_addr,
     IDX_debug_line,
+    IDX_debug_line_str,
     IDX_debug_frame,
-    IDX_eh_frame,
     IDX_debug_loc,
     IDX_debug_pubnames,
     IDX_debug_str,
-    IDX_debug_funcnames,
-    IDX_debug_typenames,
-    IDX_debug_varnames,
-    IDX_debug_weaknames,
+    IDX_debug_str_offsets,
     IDX_debug_macinfo,
+    IDX_debug_macro,
     IDX_debug_ranges,
+    IDX_gnu_debugaltlink,
     IDX_last
   };
 
@@ -72,6 +99,7 @@ enum
   DWARF_E_IO_ERROR,
   DWARF_E_INVALID_ELF,
   DWARF_E_NO_DWARF,
+  DWARF_E_COMPRESSED_ERROR,
   DWARF_E_NOELF,
   DWARF_E_GETEHDR_ERROR,
   DWARF_E_NOMEM,
@@ -82,6 +110,9 @@ enum
   DWARF_E_NO_ENTRY,
   DWARF_E_INVALID_DWARF,
   DWARF_E_NO_STRING,
+  DWARF_E_NO_DEBUG_STR,
+  DWARF_E_NO_DEBUG_LINE_STR,
+  DWARF_E_NO_STR_OFFSETS,
   DWARF_E_NO_ADDR,
   DWARF_E_NO_CONSTANT,
   DWARF_E_NO_REFERENCE,
@@ -100,15 +131,26 @@ enum
   DWARF_E_NO_FLAG,
   DWARF_E_INVALID_OFFSET,
   DWARF_E_NO_DEBUG_RANGES,
+  DWARF_E_INVALID_CFI,
+  DWARF_E_NO_ALT_DEBUGLINK,
+  DWARF_E_INVALID_OPCODE,
+  DWARF_E_NOT_CUDIE,
+  DWARF_E_UNKNOWN_LANGUAGE,
+  DWARF_E_NO_DEBUG_ADDR,
 };
 
 
+#include "dwarf_sig8_hash.h"
+
 /* This is the structure representing the debugging state.  */
 struct Dwarf
 {
   /* The underlying ELF file.  */
   Elf *elf;
 
+  /* dwz alternate DWARF file.  */
+  Dwarf *alt_dwarf;
+
   /* The section data.  */
   Elf_Data *sectiondata[IDX_last];
 
@@ -118,6 +160,10 @@ struct Dwarf
   /* If true, we allocated the ELF descriptor ourselves.  */
   bool free_elf;
 
+  /* If >= 0, we allocated the alt_dwarf ourselves and must end it and
+     close this file descriptor.  */
+  int alt_fd;
+
   /* Information for traversing the .debug_pubnames section.  This is
      an array and separately allocated with malloc.  */
   struct pubnames_s
@@ -133,9 +179,27 @@ struct Dwarf
   void *cu_tree;
   Dwarf_Off next_cu_offset;
 
+  /* Search tree and sig8 hash table for .debug_types type units.  */
+  void *tu_tree;
+  Dwarf_Off next_tu_offset;
+  Dwarf_Sig8_Hash sig8_hash;
+
+  /* Search tree for .debug_macro operator tables.  */
+  void *macro_ops;
+
+  /* Search tree for decoded .debug_line units.  */
+  void *files_lines;
+
   /* Address ranges.  */
   Dwarf_Aranges *aranges;
 
+  /* Cached info from the CFI section.  */
+  struct Dwarf_CFI_s *cfi;
+
+  /* Fake loc CU.  Used when synthesizing attributes for Dwarf_Ops that
+     came from a location list entry in dwarf_getlocation_attr.  */
+  struct Dwarf_CU *fake_loc_cu;
+
   /* Internal memory handling.  This is basically a simplified
      reimplementation of obstacks.  Unfortunately the standard obstack
      implementation is not usable in libraries.  */
@@ -158,13 +222,12 @@ struct Dwarf
 /* Abbreviation representation.  */
 struct Dwarf_Abbrev
 {
-  unsigned int code;
-  unsigned int tag;
-  int has_children;
-  unsigned int attrcnt;
-  unsigned char *attrp;
-  Dwarf_Off offset;
-};
+  Dwarf_Off offset;      /* Offset to start of abbrev into .debug_abbrev.  */
+  unsigned char *attrp;   /* Pointer to start of attribute name/form pairs. */
+  bool has_children : 1;  /* Whether or not the DIE has children. */
+  unsigned int code : 31; /* The (unique) abbrev code.  */
+  unsigned int tag;      /* The tag of the DIE. */
+} attribute_packed;
 
 #include "dwarf_abbrev_hash.h"
 
@@ -172,7 +235,7 @@ struct Dwarf_Abbrev
 /* Files in line information records.  */
 struct Dwarf_Files_s
   {
-    Dwarf *dbg;
+    unsigned int ndirs;
     unsigned int nfiles;
     struct Dwarf_Fileinfo_s
     {
@@ -180,31 +243,39 @@ struct Dwarf_Files_s
       Dwarf_Word mtime;
       Dwarf_Word length;
     } info[0];
+    /* nfiles of those, followed by char *[ndirs].  */
   };
 typedef struct Dwarf_Fileinfo_s Dwarf_Fileinfo;
 
 
 /* Representation of a row in the line table.  */
-struct Dwarf_Lines_s
-  {
-    size_t nlines;
 
-    struct Dwarf_Line_s
-    {
-      Dwarf_Addr addr;
-      unsigned int file;
-      int line;
-      unsigned short int column;
-      unsigned int is_stmt:1;
-      unsigned int basic_block:1;
-      unsigned int end_sequence:1;
-      unsigned int prologue_end:1;
-      unsigned int epilogue_begin:1;
-
-      Dwarf_Files *files;
-    } info[0];
-  };
+struct Dwarf_Line_s
+{
+  Dwarf_Files *files;
+
+  Dwarf_Addr addr;
+  unsigned int file;
+  int line;
+  unsigned short int column;
+  unsigned int is_stmt:1;
+  unsigned int basic_block:1;
+  unsigned int end_sequence:1;
+  unsigned int prologue_end:1;
+  unsigned int epilogue_begin:1;
+  /* The remaining bit fields are not flags, but hold values presumed to be
+     small.  All the flags and other bit fields should add up to 48 bits
+     to give the whole struct a nice round size.  */
+  unsigned int op_index:8;
+  unsigned int isa:8;
+  unsigned int discriminator:24;
+};
 
+struct Dwarf_Lines_s
+{
+  size_t nlines;
+  struct Dwarf_Line_s info[0];
+};
 
 /* Representation of address ranges.  */
 struct Dwarf_Aranges_s
@@ -229,6 +300,19 @@ struct Dwarf_CU
   Dwarf_Off end;
   uint8_t address_size;
   uint8_t offset_size;
+  uint16_t version;
+
+  size_t sec_idx; /* Normally .debug_info, could be .debug_type or "fake". */
+
+  /* The unit type if version >= 5.  Otherwise 0 for normal CUs (from
+     .debug_info) or 1 for v4 type units (from .debug_types).  */
+  uint8_t unit_type;
+
+  /* Zero if the unit type doesn't support a die/type offset and/or id/sig.
+     Nonzero if it is a v4 type unit or for DWARFv5 units depending on
+     unit_type.  */
+  size_t subdie_offset;
+  uint64_t unit_id8;
 
   /* Hash table for the abbreviations.  */
   Dwarf_Abbrev_Hash abbrev_hash;
@@ -245,30 +329,207 @@ struct Dwarf_CU
 
   /* Known location lists.  */
   void *locs;
+
+  /* Base address for use with ranges and locs.
+     Don't access directly, call __libdw_cu_base_address.  */
+  Dwarf_Addr base_address;
+
+  /* The offset into the .debug_addr section where index zero begins.
+     Don't access directly, call __libdw_cu_addr_base.  */
+  Dwarf_Off addr_base;
+
+  /* The offset into the .debug_str_offsets section where index zero begins.
+     Don't access directly, call __libdw_cu_str_off_base.  */
+  Dwarf_Off str_off_base;
+
+  /* Memory boundaries of this CU.  */
+  void *startp;
+  void *endp;
 };
 
+#define ISV4TU(cu) ((cu)->version == 4 && (cu)->sec_idx == IDX_debug_types)
 
-/* Function information.  */
-struct Dwarf_Func_s
+/* Compute the offset of a CU's first DIE from the CU offset.
+   CU must be a valid/known version/unit_type.  */
+static inline Dwarf_Off
+__libdw_first_die_from_cu_start (Dwarf_Off cu_start,
+                                uint8_t offset_size,
+                                uint16_t version,
+                                uint8_t unit_type)
 {
-  // XXX If we want to cache functions, we need to change this struct.
-  Dwarf_Die *die;
-  Dwarf_Die *cudie;
-};
+/*
+  assert (offset_size == 4 || offset_size == 8);
+  assert (version >= 2 && version <= 5);
+  assert (version >= 5 || (unit_type == DW_UT_compile
+                          || unit_type == DW_UT_partial
+                          || unit_type == DW_UT_type));
+  assert (version != 5 || (unit_type == DW_UT_compile
+                          || unit_type == DW_UT_partial
+                          || unit_type == DW_UT_skeleton
+                          || unit_type == DW_UT_split_compile
+                          || unit_type == DW_UT_type
+                          || unit_type == DW_UT_split_type));
+*/
+
+  Dwarf_Off off = cu_start;
+  if (version < 5)
+    {
+   /*
+        LEN       VER     OFFSET    ADDR
+      4-bytes + 2-bytes + 4-bytes + 1-byte  for 32-bit dwarf
+     12-bytes + 2-bytes + 8-bytes + 1-byte  for 64-bit dwarf
+   or in .debug_types,                              SIGNATURE TYPE-OFFSET
+      4-bytes + 2-bytes + 4-bytes + 1-byte + 8-bytes + 4-bytes  for 32-bit
+     12-bytes + 2-bytes + 8-bytes + 1-byte + 8-bytes + 8-bytes  for 64-bit
+
+   Note the trick in the computation.  If the offset_size is 4
+   the '- 4' term changes the '3 *' (or '4 *') into a '2 *' (or '3 *).
+   If the offset_size is 8 it accounts for the 4-byte escape value
+   used at the start of the length.  */
+      if (unit_type != DW_UT_type)
+       off += 3 * offset_size - 4 + 3;
+      else
+       off += 4 * offset_size - 4 + 3 + 8;
+    }
+  else
+    {
+     /*
+        LEN       VER      TYPE     ADDR     OFFSET   SIGNATURE  TYPE-OFFSET
+      4-bytes + 2-bytes + 1-byte + 1-byte + 4-bytes + 8-bytes + 4-bytes 32-bit
+     12-bytes + 2-bytes + 1-byte + 1-byte + 8-bytes + 8-bytes + 8-bytes 64-bit
+        Both signature and type offset are optional.
+
+        Note same 4/8 offset size trick as above.
+        We explicitly ignore unknow unit types (see asserts above).  */
+      off += 3 * offset_size - 4 + 4;
+      if (unit_type == DW_UT_skeleton || unit_type == DW_UT_split_compile
+         || unit_type == DW_UT_type || unit_type == DW_UT_split_type)
+       {
+         off += 8;
+         if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
+           off += offset_size;
+       }
+    }
+
+  return off;
+}
+
+static inline Dwarf_Off
+__libdw_first_die_off_from_cu (struct Dwarf_CU *cu)
+{
+  return __libdw_first_die_from_cu_start (cu->start,
+                                         cu->offset_size,
+                                         cu->version,
+                                         cu->unit_type);
+}
+
+#define CUDIE(fromcu)                                                        \
+  ((Dwarf_Die)                                                               \
+   {                                                                         \
+     .cu = (fromcu),                                                         \
+     .addr = ((char *) (fromcu)->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \
+             + __libdw_first_die_off_from_cu (fromcu))                       \
+   })
+
+#define SUBDIE(fromcu)                                                       \
+  ((Dwarf_Die)                                                               \
+   {                                                                         \
+     .cu = (fromcu),                                                         \
+     .addr = ((char *) (fromcu)->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \
+             + (fromcu)->start + (fromcu)->subdie_offset)                    \
+   })
+
+
+/* Prototype of a single .debug_macro operator.  */
+typedef struct
+{
+  Dwarf_Word nforms;
+  unsigned char const *forms;
+} Dwarf_Macro_Op_Proto;
+
+/* Prototype table.  */
+typedef struct
+{
+  /* Offset of .debug_macro section.  */
+  Dwarf_Off offset;
+
+  /* Offset of associated .debug_line section.  */
+  Dwarf_Off line_offset;
+
+  /* The source file information.  */
+  Dwarf_Files *files;
+
+  /* If this macro unit was opened through dwarf_getmacros or
+     dwarf_getmacros_die, this caches value of DW_AT_comp_dir, if
+     present.  */
+  const char *comp_dir;
+
+  /* Header length.  */
+  Dwarf_Half header_len;
+
+  uint16_t version;
+  bool is_64bit;
+  uint8_t sec_index;   /* IDX_debug_macro or IDX_debug_macinfo.  */
+
+  /* Shows where in TABLE each opcode is defined.  Since opcode 0 is
+     never used, it stores index of opcode X in X-1'th element.  The
+     value of 0xff means not stored at all.  */
+  unsigned char opcodes[255];
 
+  /* Individual opcode prototypes.  */
+  Dwarf_Macro_Op_Proto table[];
+} Dwarf_Macro_Op_Table;
 
-/* Macro information.  */
 struct Dwarf_Macro_s
 {
-  unsigned int opcode;
-  Dwarf_Word param1;
-  union
-  {
-    Dwarf_Word u;
-    const char *s;
-  } param2;
+  Dwarf_Macro_Op_Table *table;
+  Dwarf_Attribute *attributes;
+  uint8_t opcode;
 };
 
+static inline Dwarf_Word
+libdw_macro_nforms (Dwarf_Macro *macro)
+{
+  return macro->table->table[macro->table->opcodes[macro->opcode - 1]].nforms;
+}
+
+/* Returns true for any allowed FORM in the opcode_operands_table as
+   mentioned in the DWARF5 spec (6.3.1 Macro Information Header).
+   Or those mentioned in DWARF5 spec (6.2.4.2 Vendor-defined Content
+   Descriptions) for the directory/file table (plus DW_FORM_strp_sup).  */
+static inline bool
+libdw_valid_user_form (int form)
+{
+  switch (form)
+    {
+      case DW_FORM_block:
+      case DW_FORM_block1:
+      case DW_FORM_block2:
+      case DW_FORM_block4:
+      case DW_FORM_data1:
+      case DW_FORM_data2:
+      case DW_FORM_data4:
+      case DW_FORM_data8:
+      case DW_FORM_data16:
+      case DW_FORM_flag:
+      case DW_FORM_line_strp:
+      case DW_FORM_sdata:
+      case DW_FORM_sec_offset:
+      case DW_FORM_string:
+      case DW_FORM_strp:
+      case DW_FORM_strp_sup:
+      case DW_FORM_strx:
+      case DW_FORM_strx1:
+      case DW_FORM_strx2:
+      case DW_FORM_strx3:
+      case DW_FORM_strx4:
+      case DW_FORM_udata:
+       return true;
+      default:
+       return false;
+    }
+}
+
 
 /* We have to include the file at this point because the inline
    functions access internals of the Dwarf structure.  */
@@ -288,33 +549,50 @@ extern void __libdw_seterrno (int value) internal_function;
                         - ((uintptr_t) _result & (__alignof (type) - 1)))    \
                        & (__alignof (type) - 1));                            \
      if (unlikely (_tail->remaining < _required + _padding))                 \
-       {                                                                     \
-        _result = (type *) __libdw_allocate (dbg, _required);                \
-        _tail = (dbg)->mem_tail;                                             \
-       }                                                                     \
+       _result = (type *) __libdw_allocate (dbg, _required, __alignof (type));\
      else                                                                    \
        {                                                                     \
         _required += _padding;                                               \
         _result = (type *) ((char *) _result + _padding);                    \
+        _tail->remaining -= _required;                                       \
        }                                                                     \
-     _tail->remaining -= _required;                                          \
      _result; })
 
 #define libdw_typed_alloc(dbg, type) \
   libdw_alloc (dbg, type, sizeof (type), 1)
 
 /* Callback to allocate more.  */
-extern void *__libdw_allocate (Dwarf *dbg, size_t minsize)
+extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
      __attribute__ ((__malloc__)) __nonnull_attribute__ (1);
 
 /* Default OOM handler.  */
-extern void __libdw_oom (void) __attribute ((noreturn, visibility ("hidden")));
+extern void __libdw_oom (void) __attribute ((noreturn)) attribute_hidden;
+
+/* Read next unit (or v4 debug type) and return next offset.  Doesn't
+   create an actual Dwarf_CU just provides necessary header fields.  */
+extern int
+internal_function
+__libdw_next_unit (Dwarf *dbg, bool v4_debug_types, Dwarf_Off off,
+                  Dwarf_Off *next_off, size_t *header_sizep,
+                  Dwarf_Half *versionp, uint8_t *unit_typep,
+                  Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep,
+                  uint8_t *offset_sizep, uint64_t *unit_id8p,
+                  Dwarf_Off *subdie_offsetp)
+     __nonnull_attribute__ (4) internal_function;
+
+/* Allocate the internal data for a unit not seen before.  */
+extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
+     __nonnull_attribute__ (1) internal_function;
 
 /* Find CU for given offset.  */
-extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset)
+extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset, bool tu)
      __nonnull_attribute__ (1) internal_function;
 
-/* Return tag of given DIE.  */
+/* Find CU for given DIE address.  */
+extern struct Dwarf_CU *__libdw_findcu_addr (Dwarf *dbg, void *addr)
+     __nonnull_attribute__ (1) internal_function;
+
+/* Get abbreviation with given code.  */
 extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
                                         unsigned int code)
      __nonnull_attribute__ (1) internal_function;
@@ -325,11 +603,93 @@ extern Dwarf_Abbrev *__libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu,
                                        Dwarf_Abbrev *result)
      __nonnull_attribute__ (1) internal_function;
 
+/* Get abbreviation of given DIE, and optionally set *READP to the DIE memory
+   just past the abbreviation code.  */
+static inline Dwarf_Abbrev *
+__nonnull_attribute__ (1)
+__libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp)
+{
+  /* Do we need to get the abbreviation, or need to read after the code?  */
+  if (die->abbrev == NULL || readp != NULL)
+    {
+      /* Get the abbreviation code.  */
+      unsigned int code;
+      const unsigned char *addr = die->addr;
+      if (die->cu == NULL)
+       return DWARF_END_ABBREV;
+      get_uleb128 (code, addr, die->cu->endp);
+      if (readp != NULL)
+       *readp = addr;
+
+      /* Find the abbreviation.  */
+      if (die->abbrev == NULL)
+       die->abbrev = __libdw_findabbrev (die->cu, code);
+    }
+  return die->abbrev;
+}
+
 /* Helper functions for form handling.  */
-extern size_t __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
-                                   unsigned int form,
-                                   const unsigned char *valp)
-     __nonnull_attribute__ (1, 2, 4) internal_function;
+extern size_t __libdw_form_val_compute_len (struct Dwarf_CU *cu,
+                                           unsigned int form,
+                                           const unsigned char *valp)
+     __nonnull_attribute__ (1, 3) internal_function;
+
+/* Find the length of a form attribute in DIE/info data.  */
+static inline size_t
+__nonnull_attribute__ (1, 3)
+__libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form,
+                     const unsigned char *valp)
+{
+  /* Small lookup table of forms with fixed lengths.  Absent indexes are
+     initialized 0, so any truly desired 0 is set to 0x80 and masked.  */
+  static const uint8_t form_lengths[] =
+    {
+      [DW_FORM_flag_present] = 0x80,
+      [DW_FORM_implicit_const] = 0x80, /* Value is in abbrev, not in info.  */
+
+      [DW_FORM_flag] = 1,
+      [DW_FORM_data1] = 1, [DW_FORM_ref1] = 1,
+      [DW_FORM_addrx1] = 1, [DW_FORM_strx1] = 1,
+
+      [DW_FORM_data2] = 2, [DW_FORM_ref2] = 2,
+      [DW_FORM_addrx2] = 2, [DW_FORM_strx2] = 2,
+
+      [DW_FORM_addrx3] = 3, [DW_FORM_strx3] = 3,
+
+      [DW_FORM_data4] = 4, [DW_FORM_ref4] = 4, [DW_FORM_ref_sup4] = 4,
+      [DW_FORM_addrx4] = 4, [DW_FORM_strx4] = 4,
+
+      [DW_FORM_ref_sig8] = 8,
+      [DW_FORM_data8] = 8, [DW_FORM_ref8] = 8, [DW_FORM_ref_sup8] = 8,
+
+      [DW_FORM_data16] = 16,
+    };
+
+  /* Return immediately for forms with fixed lengths.  */
+  if (form < sizeof form_lengths / sizeof form_lengths[0])
+    {
+      uint8_t len = form_lengths[form];
+      if (len != 0)
+       {
+         const unsigned char *endp = cu->endp;
+         len &= 0x7f; /* Mask to allow 0x80 -> 0.  */
+         if (unlikely (len > (size_t) (endp - valp)))
+           {
+             __libdw_seterrno (DWARF_E_INVALID_DWARF);
+             return -1;
+           }
+         return len;
+       }
+    }
+
+  /* Other forms require some computation.  */
+  return __libdw_form_val_compute_len (cu, form, valp);
+}
+
+/* Helper function for DW_FORM_ref* handling.  */
+extern int __libdw_formref (Dwarf_Attribute *attr, Dwarf_Off *return_offset)
+     __nonnull_attribute__ (1, 2) internal_function;
+
 
 /* Helper function to locate attribute.  */
 extern unsigned char *__libdw_find_attr (Dwarf_Die *die,
@@ -339,7 +699,7 @@ extern unsigned char *__libdw_find_attr (Dwarf_Die *die,
      __nonnull_attribute__ (1) internal_function;
 
 /* Helper function to access integer attribute.  */
-extern int __libdw_func_intval (Dwarf_Func *func, int *linep, int attval)
+extern int __libdw_attr_intval (Dwarf_Die *die, int *valp, int attval)
      __nonnull_attribute__ (1, 2) internal_function;
 
 /* Helper function to walk scopes.  */
@@ -351,6 +711,7 @@ struct Dwarf_Die_Chain
 };
 extern int __libdw_visit_scopes (unsigned int depth,
                                 struct Dwarf_Die_Chain *root,
+                                struct Dwarf_Die_Chain *imports,
                                 int (*previsit) (unsigned int depth,
                                                  struct Dwarf_Die_Chain *,
                                                  void *arg),
@@ -358,32 +719,370 @@ extern int __libdw_visit_scopes (unsigned int depth,
                                                   struct Dwarf_Die_Chain *,
                                                   void *arg),
                                 void *arg)
-  __nonnull_attribute__ (2, 3) internal_function;
+  __nonnull_attribute__ (2, 4) internal_function;
+
+/* Parse a DWARF Dwarf_Block into an array of Dwarf_Op's,
+   and cache the result (via tsearch).  */
+extern int __libdw_intern_expression (Dwarf *dbg,
+                                     bool other_byte_order,
+                                     unsigned int address_size,
+                                     unsigned int ref_size,
+                                     void **cache, const Dwarf_Block *block,
+                                     bool cfap, bool valuep,
+                                     Dwarf_Op **llbuf, size_t *listlen,
+                                     int sec_index)
+  __nonnull_attribute__ (5, 6, 9, 10) internal_function;
+
+extern Dwarf_Die *__libdw_offdie (Dwarf *dbg, Dwarf_Off offset,
+                                 Dwarf_Die *result, bool debug_types)
+  internal_function;
+
 
 /* Return error code of last failing function call.  This value is kept
    separately for each thread.  */
 extern int __dwarf_errno_internal (void);
 
 
+/* Reader hooks.  */
+
+/* Relocation hooks return -1 on error (in that case the error code
+   must already have been set), 0 if there is no relocation and 1 if a
+   relocation was present.*/
+
+static inline int
+__libdw_relocate_address (Dwarf *dbg __attribute__ ((unused)),
+                         int sec_index __attribute__ ((unused)),
+                         const void *addr __attribute__ ((unused)),
+                         int width __attribute__ ((unused)),
+                         Dwarf_Addr *val __attribute__ ((unused)))
+{
+  return 0;
+}
+
+static inline int
+__libdw_relocate_offset (Dwarf *dbg __attribute__ ((unused)),
+                        int sec_index __attribute__ ((unused)),
+                        const void *addr __attribute__ ((unused)),
+                        int width __attribute__ ((unused)),
+                        Dwarf_Off *val __attribute__ ((unused)))
+{
+  return 0;
+}
+
+static inline Elf_Data *
+__libdw_checked_get_data (Dwarf *dbg, int sec_index)
+{
+  Elf_Data *data = dbg->sectiondata[sec_index];
+  if (unlikely (data == NULL)
+      || unlikely (data->d_buf == NULL))
+    {
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return NULL;
+    }
+  return data;
+}
+
+static inline int
+__libdw_offset_in_section (Dwarf *dbg, int sec_index,
+                          Dwarf_Off offset, size_t size)
+{
+  Elf_Data *data = __libdw_checked_get_data (dbg, sec_index);
+  if (data == NULL)
+    return -1;
+  if (unlikely (offset > data->d_size)
+      || unlikely (data->d_size < size)
+      || unlikely (offset > data->d_size - size))
+    {
+      __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+      return -1;
+    }
+
+  return 0;
+}
+
+static inline bool
+__libdw_in_section (Dwarf *dbg, int sec_index,
+                   const void *addr, size_t size)
+{
+  Elf_Data *data = __libdw_checked_get_data (dbg, sec_index);
+  if (data == NULL)
+    return false;
+  if (unlikely (addr < data->d_buf)
+      || unlikely (data->d_size < size)
+      || unlikely ((size_t)(addr - data->d_buf) > data->d_size - size))
+    {
+      __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+      return false;
+    }
+
+  return true;
+}
+
+#define READ_AND_RELOCATE(RELOC_HOOK, VAL)                             \
+  ({                                                                   \
+    if (!__libdw_in_section (dbg, sec_index, addr, width))             \
+      return -1;                                                       \
+                                                                       \
+    const unsigned char *orig_addr = addr;                             \
+    if (width == 4)                                                    \
+      VAL = read_4ubyte_unaligned_inc (dbg, addr);                     \
+    else                                                               \
+      VAL = read_8ubyte_unaligned_inc (dbg, addr);                     \
+                                                                       \
+    int status = RELOC_HOOK (dbg, sec_index, orig_addr, width, &VAL);  \
+    if (status < 0)                                                    \
+      return status;                                                   \
+    status > 0;                                                                \
+   })
+
+static inline int
+__libdw_read_address_inc (Dwarf *dbg,
+                         int sec_index, const unsigned char **addrp,
+                         int width, Dwarf_Addr *ret)
+{
+  const unsigned char *addr = *addrp;
+  READ_AND_RELOCATE (__libdw_relocate_address, (*ret));
+  *addrp = addr;
+  return 0;
+}
+
+static inline int
+__libdw_read_address (Dwarf *dbg,
+                     int sec_index, const unsigned char *addr,
+                     int width, Dwarf_Addr *ret)
+{
+  READ_AND_RELOCATE (__libdw_relocate_address, (*ret));
+  return 0;
+}
+
+static inline int
+__libdw_read_offset_inc (Dwarf *dbg,
+                        int sec_index, const unsigned char **addrp,
+                        int width, Dwarf_Off *ret, int sec_ret,
+                        size_t size)
+{
+  const unsigned char *addr = *addrp;
+  READ_AND_RELOCATE (__libdw_relocate_offset, (*ret));
+  *addrp = addr;
+  return __libdw_offset_in_section (dbg, sec_ret, *ret, size);
+}
+
+static inline int
+__libdw_read_offset (Dwarf *dbg, Dwarf *dbg_ret,
+                    int sec_index, const unsigned char *addr,
+                    int width, Dwarf_Off *ret, int sec_ret,
+                    size_t size)
+{
+  READ_AND_RELOCATE (__libdw_relocate_offset, (*ret));
+  return __libdw_offset_in_section (dbg_ret, sec_ret, *ret, size);
+}
+
+static inline size_t
+cu_sec_idx (struct Dwarf_CU *cu)
+{
+  return cu->sec_idx;
+}
+
+static inline bool
+is_cudie (Dwarf_Die *cudie)
+{
+  return CUDIE (cudie->cu).addr == cudie->addr;
+}
+
+/* Read up begin/end pair and increment read pointer.
+    - If it's normal range record, set up *BEGINP and *ENDP and return 0.
+    - If it's base address selection record, set up *BASEP and return 1.
+    - If it's end of rangelist, don't set anything and return 2
+    - If an error occurs, don't set anything and return <0.  */
+int __libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index,
+                                    const unsigned char **readp,
+                                    const unsigned char *readend,
+                                    int width,
+                                    Dwarf_Addr *beginp, Dwarf_Addr *endp,
+                                    Dwarf_Addr *basep)
+  internal_function;
+
+const unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
+                                      int err_nodata,
+                                      const unsigned char **endpp,
+                                      Dwarf_Off *offsetp)
+  internal_function;
+
+/* Fills in the given attribute to point at an empty location expression.  */
+void __libdw_empty_loc_attr (Dwarf_Attribute *attr)
+  internal_function;
+
+/* Load .debug_line unit at DEBUG_LINE_OFFSET.  COMP_DIR is a value of
+   DW_AT_comp_dir or NULL if that attribute is not available.  Caches
+   the loaded unit and optionally set *LINESP and/or *FILESP (if not
+   NULL) with loaded information.  Returns 0 for success or a negative
+   value for failure.  */
+int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
+                        const char *comp_dir, unsigned address_size,
+                        Dwarf_Lines **linesp, Dwarf_Files **filesp)
+  internal_function
+  __nonnull_attribute__ (1);
+
+/* Load and return value of DW_AT_comp_dir from CUDIE.  */
+const char *__libdw_getcompdir (Dwarf_Die *cudie);
+
+/* Get the base address for the CU, fetches it when not yet set.
+   This is used as initial base address for ranges and loclists.  */
+Dwarf_Addr __libdw_cu_base_address (Dwarf_CU *cu);
+
+/* Get the address base for the CU, fetches it when not yet set.  */
+Dwarf_Off __libdw_cu_addr_base (Dwarf_CU *cu);
+
+/* Gets the .debug_str_offsets base offset to use.  static inline to
+   be shared between libdw and eu-readelf.  */
+static inline Dwarf_Off
+str_offsets_base_off (Dwarf *dbg, Dwarf_CU *cu)
+{
+  /* If we don't have a CU, then find and use the first one in the
+     debug file (when we support .dwp files, we must actually find the
+     one matching our "caller" - aka macro or line).  If we (now) have
+     a cu and str_offsets_base attribute, just use that.  Otherwise
+     use the first offset.  But we might have to parse the header
+     first, but only if this is version 5.  Assume if all else fails,
+     this is version 4, without header.  */
+
+  if (cu == NULL && dbg != NULL)
+    {
+      Dwarf_CU *first_cu;
+      if (dwarf_get_units (dbg, NULL, &first_cu,
+                          NULL, NULL, NULL, NULL) == 0)
+       cu = first_cu;
+    }
+
+  if (cu != NULL)
+    {
+      if (cu->str_off_base == (Dwarf_Off) -1)
+       {
+         Dwarf_Die cu_die = CUDIE(cu);
+         Dwarf_Attribute attr;
+         if (dwarf_attr (&cu_die, DW_AT_str_offsets_base, &attr) != NULL)
+           {
+             Dwarf_Word off;
+             if (dwarf_formudata (&attr, &off) == 0)
+               {
+                 cu->str_off_base = off;
+                 return cu->str_off_base;
+               }
+           }
+         /* For older DWARF simply assume zero (no header).  */
+         if (cu->version < 5)
+           {
+             cu->str_off_base = 0;
+             return cu->str_off_base;
+           }
+       }
+      else
+       return cu->str_off_base;
+    }
+
+  /* No str_offsets_base attribute, we have to assume "zero".
+     But there could be a header first.  */
+  Dwarf_Off off = 0;
+  if (dbg == NULL)
+    goto no_header;
+
+  Elf_Data *data =  dbg->sectiondata[IDX_debug_str_offsets];
+  if (data == NULL)
+    goto no_header;
+
+  const unsigned char *start;
+  const unsigned char *readp;
+  const unsigned char *readendp;
+  start = readp = (const unsigned char *) data->d_buf;
+  readendp = (const unsigned char *) data->d_buf + data->d_size;
+
+  uint64_t unit_length;
+  uint16_t version;
+
+  unit_length = read_4ubyte_unaligned_inc (dbg, readp);
+  if (unlikely (unit_length == 0xffffffff))
+    {
+      if (unlikely (readendp - readp < 8))
+       goto no_header;
+      unit_length = read_8ubyte_unaligned_inc (dbg, readp);
+      /* In theory the offset size could be different
+        between CU and str_offsets unit.  But we just
+        ignore that here. */
+    }
+
+  /* We need at least 2-bytes (version) + 2-bytes (padding) =
+     4 bytes to complete the header.  And this unit cannot go
+     beyond the section data.  */
+  if (readendp - readp < 4
+      || unit_length < 4
+      || (uint64_t) (readendp - readp) < unit_length)
+    goto no_header;
+
+  version = read_2ubyte_unaligned_inc (dbg, readp);
+  if (version != 5)
+    goto no_header;
+  /* padding */
+  read_2ubyte_unaligned_inc (dbg, readp);
+
+  off = (Dwarf_Off) (readp - start);
+
+ no_header:
+  if (cu != NULL)
+    cu->str_off_base = off;
+
+  return off;
+}
+
+
+/* Get the string offsets base for the CU, fetches it when not yet set.  */
+static inline Dwarf_Off __libdw_cu_str_off_base (Dwarf_CU *cu)
+{
+  return str_offsets_base_off (NULL, cu);
+}
+
+
+/* Given a file descriptor, dir and file returns a full path.  If the
+   file is absolute (starts with a /) a copy of file is returned.  If
+   the file isn't absolute, but dir is absolute, then a path that is
+   the concatenation of dir and file is returned.  If neither file,
+   nor dir is absolute, the path will be constructed using dir (if not
+   NULL) and file relative to the path of the given file descriptor
+   (if valid).
+
+   The file descriptor may be -1 and the dir may be NULL (in which
+   case they aren't used). If file is NULL, or no full path can be
+   constructed NULL is returned.
+
+   The caller is responsible for freeing the result if not NULL.  */
+char * filepath (int fd, const char *dir, const char *file)
+  internal_function;
+
+
 /* Aliases to avoid PLTs.  */
+INTDECL (dwarf_aggregate_size)
 INTDECL (dwarf_attr)
 INTDECL (dwarf_attr_integrate)
+INTDECL (dwarf_begin)
 INTDECL (dwarf_begin_elf)
 INTDECL (dwarf_child)
+INTDECL (dwarf_default_lower_bound)
 INTDECL (dwarf_dieoffset)
 INTDECL (dwarf_diename)
 INTDECL (dwarf_end)
+INTDECL (dwarf_entrypc)
 INTDECL (dwarf_errmsg)
 INTDECL (dwarf_formaddr)
 INTDECL (dwarf_formblock)
-INTDECL (dwarf_formref)
 INTDECL (dwarf_formref_die)
 INTDECL (dwarf_formsdata)
 INTDECL (dwarf_formstring)
 INTDECL (dwarf_formudata)
+INTDECL (dwarf_getabbrevattr_data)
+INTDECL (dwarf_getalt)
 INTDECL (dwarf_getarange_addr)
 INTDECL (dwarf_getarangeinfo)
 INTDECL (dwarf_getaranges)
+INTDECL (dwarf_getlocation_die)
 INTDECL (dwarf_getsrcfiles)
 INTDECL (dwarf_getsrclines)
 INTDECL (dwarf_hasattr)
@@ -392,8 +1091,13 @@ INTDECL (dwarf_haspc)
 INTDECL (dwarf_highpc)
 INTDECL (dwarf_lowpc)
 INTDECL (dwarf_nextcu)
+INTDECL (dwarf_next_unit)
 INTDECL (dwarf_offdie)
+INTDECL (dwarf_peel_type)
+INTDECL (dwarf_ranges)
+INTDECL (dwarf_setalt)
 INTDECL (dwarf_siblingof)
+INTDECL (dwarf_srclang)
 INTDECL (dwarf_tag)
 
 #endif /* libdwP.h */