libdw: Support DW_OP_addrx/constx and split DWARF addrx/constx support.
authorMark Wielaard <mark@klomp.org>
Sun, 20 May 2018 21:30:01 +0000 (23:30 +0200)
committerMark Wielaard <mark@klomp.org>
Fri, 25 May 2018 13:07:58 +0000 (15:07 +0200)
DW_OP_addrx/constx and GNU DebugFission DW_OP_GNU_addr/const_index take
as argument an index into the .debug_addr section for the associated CU.
This index gets resolved through dwarf_getlocation_attr. A new fake addr
CU is created per Dwarf for use with this new attribute. For split DWARF
files, the IDX_debug_addr gets replaced with the skeleton section and the
addr base is resolved immediately when constructing the split DWARF CU.
Move __libdw_cu_addr_base to libdwP.h to share with eu-readelf. Also
make it possible to resolve addrx[1234]/GNU_addr_index also as constant
indexes to (also) show when displaying these attributes in eu-readelf.

A new varlocs tests is added to test the resolving for both the DWARF4
and DWARF5 DW_OP variants. And now that addrx forms are resolved in
split DWARF files add the new DIEs with "single ranges" (those DIEs that
have a lowpc/highpc attribute pair) to run-all-dwarf-ranges.sh.

Signed-off-by: Mark Wielaard <mark@klomp.org>
19 files changed:
libdw/ChangeLog
libdw/dwarf_begin_elf.c
libdw/dwarf_end.c
libdw/dwarf_formaddr.c
libdw/dwarf_formudata.c
libdw/dwarf_getlocation_attr.c
libdw/libdwP.h
libdw/libdw_find_split_unit.c
src/ChangeLog
src/readelf.c
tests/ChangeLog
tests/Makefile.am
tests/addrx_constx-4.dwo.bz2 [new file with mode: 0644]
tests/addrx_constx-5.dwo.bz2 [new file with mode: 0644]
tests/run-all-dwarf-ranges.sh
tests/run-varlocs.sh
tests/testfile-addrx_constx-4.bz2 [new file with mode: 0755]
tests/testfile-addrx_constx-5.bz2 [new file with mode: 0755]
tests/varlocs.c

index 08c8f7b..f568963 100644 (file)
@@ -1,3 +1,22 @@
+2018-05-21  Mark Wielaard  <mark@klomp.org>
+
+       * dwarf_begin_elf.c (valid_p): Add a fake_addr_cu to the result.
+       * dwarf_end.c (cu_free): Disconnect the fake_addr_cu from the split
+       dwarf if shared with skeleton.
+       (dwarf_end): release fake_addr_cu.
+       * dwarf_formaddr.c (__libdw_cu_addr_base): Move to...
+       * libdwP.h (__libdw_cu_addr_base): ... here.
+       (struct Dwarf): Add fake_addr_cu field.
+       * dwarf_formudata.c (dwarf_formudata): Handle
+       DW_FORM_GNU_addr_index and DW_FORM_addrx[1234].
+       * dwarf_getlocation_attr.c (addr_valp): New static function.
+       (dwarf_getlocation_attr): Create attribute for values of
+       DW_OP_GNU_const_index, DW_OP_constx and DW_OP_GNU_addr_index and
+       DW_OP_addrx.
+       * libdw_find_split_unit.c (__libdw_find_split_unit): Connect
+       IDX_debug_addr sectiondata and fake_addr_cu between split and
+       skeleton.
+
 2018-05-20  Mark Wielaard  <mark@klomp.org>
 
        * dwarf_cu_info.c: New file.
index 0e435c5..5d8e79e 100644 (file)
@@ -246,6 +246,33 @@ valid_p (Dwarf *result)
        }
     }
 
+  /* For DW_OP_constx/GNU_const_index and DW_OP_addrx/GNU_addr_index
+     the dwarf_location_attr () will need a "fake" address CU to
+     indicate where the attribute data comes from.  This is a just
+     inside the .debug_addr section, if it exists.  */
+  if (result != NULL && result->sectiondata[IDX_debug_addr] != NULL)
+    {
+      result->fake_addr_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU));
+      if (unlikely (result->fake_addr_cu == NULL))
+       {
+         Dwarf_Sig8_Hash_free (&result->sig8_hash);
+         __libdw_seterrno (DWARF_E_NOMEM);
+         free (result->fake_loc_cu);
+         free (result);
+         result = NULL;
+       }
+      else
+       {
+         result->fake_addr_cu->sec_idx = IDX_debug_addr;
+         result->fake_addr_cu->dbg = result;
+         result->fake_addr_cu->startp
+           = result->sectiondata[IDX_debug_addr]->d_buf;
+         result->fake_addr_cu->endp
+           = (result->sectiondata[IDX_debug_addr]->d_buf
+              + result->sectiondata[IDX_debug_addr]->d_size);
+       }
+    }
+
   if (result != NULL)
     result->debugdir = __libdw_debugdir (result->elf->fildes);
 
index 4702f1b..1954674 100644 (file)
@@ -59,7 +59,12 @@ cu_free (void *arg)
   /* Free split dwarf one way (from skeleton to split).  */
   if (p->unit_type == DW_UT_skeleton
       && p->split != NULL && p->split != (void *)-1)
-    INTUSE(dwarf_end) (p->split->dbg);
+    {
+      /* The fake_addr_cu might be shared, only release one.  */
+      if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu)
+       p->split->dbg->fake_addr_cu = NULL;
+      INTUSE(dwarf_end) (p->split->dbg);
+    }
 }
 
 
@@ -108,6 +113,11 @@ dwarf_end (Dwarf *dwarf)
          cu_free (dwarf->fake_loc_cu);
          free (dwarf->fake_loc_cu);
        }
+      if (dwarf->fake_addr_cu != NULL)
+       {
+         cu_free (dwarf->fake_addr_cu);
+         free (dwarf->fake_addr_cu);
+       }
 
       /* Did we find and allocate the alt Dwarf ourselves?  */
       if (dwarf->alt_fd != -1)
index c917dea..3c89a5d 100644 (file)
@@ -136,21 +136,3 @@ dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr)
   return 0;
 }
 INTDEF(dwarf_formaddr)
-
-Dwarf_Off __libdw_cu_addr_base (Dwarf_CU *cu)
-{
-  if (cu->addr_base == (Dwarf_Off) -1)
-    {
-      Dwarf_Die cu_die = CUDIE(cu);
-      Dwarf_Attribute attr;
-      if (dwarf_attr (&cu_die, DW_AT_GNU_addr_base, &attr) != NULL
-         || dwarf_attr (&cu_die, DW_AT_addr_base, &attr) != NULL)
-       {
-         Dwarf_Word off;
-         if (dwarf_formudata (&attr, &off) == 0)
-           cu->addr_base = off;
-       }
-    }
-
-  return cu->addr_base;
-}
index 316ad86..d56e7dc 100644 (file)
@@ -288,6 +288,39 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
       get_sleb128_unchecked (*return_uval, datap);
       break;
 
+    /* These are indexes into the .debug_addr section, normally resolved
+       with dwarf_formaddr.  Here treat as constants.  */
+    case DW_FORM_GNU_addr_index:
+    case DW_FORM_addrx:
+      if (datap >= endp)
+       goto invalid;
+      get_uleb128 (*return_uval, datap, endp);
+      break;
+
+    case DW_FORM_addrx1:
+      if (datap >= endp - 1)
+       goto invalid;
+      *return_uval = *datap;
+      break;
+
+    case DW_FORM_addrx2:
+      if (datap >= endp - 2)
+       goto invalid;
+      *return_uval = read_2ubyte_unaligned (attr->cu->dbg, datap);
+      break;
+
+    case DW_FORM_addrx3:
+      if (datap >= endp - 3)
+       goto invalid;
+      *return_uval = read_3ubyte_unaligned (attr->cu->dbg, datap);
+      break;
+
+    case DW_FORM_addrx4:
+      if (datap >= endp - 4)
+       goto invalid;
+      *return_uval = read_4ubyte_unaligned (attr->cu->dbg, datap);
+      break;
+
     default:
       __libdw_seterrno (DWARF_E_NO_CONSTANT);
       return -1;
index 162330f..62ef47a 100644 (file)
@@ -52,6 +52,18 @@ attr_form_cu (Dwarf_Attribute *attr)
     }
 }
 
+static unsigned char *
+addr_valp (Dwarf_CU *cu, Dwarf_Word index)
+{
+  Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr];
+  Dwarf_Word offset = __libdw_cu_addr_base (cu) + (index * cu->address_size);
+  if (debug_addr == NULL)
+    /* This is really an error, will trigger with dwarf_formaddr.  */
+    return (unsigned char *) offset;
+
+  return (unsigned char *) debug_addr->d_buf + offset;
+}
+
 int
 dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribute *result)
 {
@@ -83,6 +95,25 @@ dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribu
        result->cu = attr_form_cu (attr);
        break;
 
+      case DW_OP_GNU_const_index:
+      case DW_OP_constx:
+       result->code = DW_AT_const_value;
+       if (attr->cu->address_size == 4)
+         result->form = DW_FORM_data4;
+       else
+         result->form = DW_FORM_data8;
+       result->valp = addr_valp (attr->cu, op->number);
+       result->cu = attr->cu->dbg->fake_addr_cu;
+       break;
+
+      case DW_OP_GNU_addr_index:
+      case DW_OP_addrx:
+       result->code = DW_AT_low_pc;
+       result->form = DW_FORM_addr;
+       result->valp = addr_valp (attr->cu, op->number);
+       result->cu = attr->cu->dbg->fake_addr_cu;
+       break;
+
       case DW_OP_call2:
       case DW_OP_call4:
       case DW_OP_call_ref:
index 2b5b5ea..82ee5d0 100644 (file)
@@ -204,6 +204,9 @@ struct Dwarf
      came from a location list entry in dwarf_getlocation_attr.  */
   struct Dwarf_CU *fake_loc_cu;
 
+  /* Similar for addrx/constx, which will come from .debug_addr section.  */
+  struct Dwarf_CU *fake_addr_cu;
+
   /* Internal memory handling.  This is basically a simplified
      reimplementation of obstacks.  Unfortunately the standard obstack
      implementation is not usable in libraries.  */
@@ -947,7 +950,26 @@ const char *__libdw_getcompdir (Dwarf_Die *cudie);
 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);
+static inline Dwarf_Off
+__libdw_cu_addr_base (Dwarf_CU *cu)
+{
+  if (cu->addr_base == (Dwarf_Off) -1)
+    {
+      Dwarf_Die cu_die = CUDIE(cu);
+      Dwarf_Attribute attr;
+      Dwarf_Off offset = 0;
+      if (dwarf_attr (&cu_die, DW_AT_GNU_addr_base, &attr) != NULL
+         || dwarf_attr (&cu_die, DW_AT_addr_base, &attr) != NULL)
+       {
+         Dwarf_Word off;
+         if (dwarf_formudata (&attr, &off) == 0)
+           offset = off;
+       }
+      cu->addr_base = offset;
+    }
+
+  return cu->addr_base;
+}
 
 /* Gets the .debug_str_offsets base offset to use.  static inline to
    be shared between libdw and eu-readelf.  */
index bd48b9e..78c9a2a 100644 (file)
@@ -88,6 +88,20 @@ __libdw_find_split_unit (Dwarf_CU *cu)
                              cu->split = split;
                              split->split = cu;
 
+                             /* Get .debug_addr and addr_base greedy.
+                                We also need it for the fake addr cu.
+                                There is only one per split debug.  */
+                             Dwarf *dbg = cu->dbg;
+                             Dwarf *sdbg = split->dbg;
+                             if (sdbg->sectiondata[IDX_debug_addr] == NULL
+                                 && dbg->sectiondata[IDX_debug_addr] != NULL)
+                               {
+                                 sdbg->sectiondata[IDX_debug_addr]
+                                   = dbg->sectiondata[IDX_debug_addr];
+                                 split->addr_base = __libdw_cu_addr_base (cu);
+                                 sdbg->fake_addr_cu = dbg->fake_addr_cu;
+                               }
+
                              /* We have everything we need from this
                                 ELF file.  And we are going to close
                                 the fd to not run out of file
index e187277..1a9f4a3 100644 (file)
@@ -1,3 +1,12 @@
+2018-01-21  Mark Wielaard  <mark@klomp.org>
+
+       * readelf.c (get_indexed_addr): New function.
+       (print_ops): Handle DW_OP_addrx, DW_OP_GNU_addr_index,
+       DW_OP_constx, DW_OP_GNU_const_index separately and resolve
+       address.
+       (attr_callback): Print index and address for
+       DW_FORM_GNU_addr_index and DW_FORM_addrx[1234].
+
 2018-01-19  Mark Wielaard  <mark@klomp.org>
 
        * readelf.c (options): Add info+.
index 466d941..1858802 100644 (file)
@@ -4180,6 +4180,29 @@ print_bytes (size_t n, const unsigned char *bytes)
     }
 }
 
+static int
+get_indexed_addr (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr)
+{
+  Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr];
+  if (debug_addr == NULL)
+    return -1;
+
+  Dwarf_Off base = __libdw_cu_addr_base (cu);
+  Dwarf_Word off = idx * cu->address_size;
+  if (base > debug_addr->d_size
+      || off > debug_addr->d_size - base
+      || cu->address_size > debug_addr->d_size - base - off)
+    return -1;
+
+  const unsigned char *addrp = debug_addr->d_buf + base + off;
+  if (cu->address_size == 4)
+    *addr = read_4ubyte_unaligned (cu->dbg, addrp);
+  else
+    *addr = read_8ubyte_unaligned (cu->dbg, addrp);
+
+  return 0;
+}
+
 static void
 print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
           unsigned int vers, unsigned int addrsize, unsigned int offset_size,
@@ -4348,19 +4371,36 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
        case DW_OP_piece:
        case DW_OP_regx:
        case DW_OP_plus_uconst:
-       case DW_OP_constu:
+       case DW_OP_constu:;
+         const unsigned char *start = data;
+         uint64_t uleb;
+         NEED (1);
+         get_uleb128 (uleb, data, data + len);
+         printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 "\n",
+                 indent, "", (uintmax_t) offset, op_name, uleb);
+         CONSUME (data - start);
+         offset += 1 + (data - start);
+         break;
+
        case DW_OP_addrx:
        case DW_OP_GNU_addr_index:
        case DW_OP_constx:
        case DW_OP_GNU_const_index:;
-         const unsigned char *start = data;
-         uint64_t uleb;
+         start = data;
          NEED (1);
          get_uleb128 (uleb, data, data + len);
-         printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 "\n",
+         printf ("%*s[%2" PRIuMAX "] %s [%" PRIu64 "] ",
                  indent, "", (uintmax_t) offset, op_name, uleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
+         if (get_indexed_addr (cu, uleb, &addr) != 0)
+           printf ("???\n");
+         else
+           {
+             a = format_dwarf_addr (dwflmod, 0, addr, addr);
+             printf ("%s\n", a);
+             free (a);
+           }
          break;
 
        case DW_OP_bit_piece:
@@ -6148,9 +6188,19 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
            }
          char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize,
                                       addr, addr);
-         printf ("           %*s%-20s (%s) %s\n",
-                 (int) (level * 2), "", dwarf_attr_name (attr),
-                 dwarf_form_name (form), a);
+         if (form != DW_FORM_addr )
+           {
+             Dwarf_Word index;
+             if (dwarf_formudata (attrp, &index) != 0)
+               goto attrval_out;
+             printf ("           %*s%-20s (%s) [%" PRIx64 "] %s\n",
+                     (int) (level * 2), "", dwarf_attr_name (attr),
+                     dwarf_form_name (form), index, a);
+           }
+         else
+           printf ("           %*s%-20s (%s) %s\n",
+                   (int) (level * 2), "", dwarf_attr_name (attr),
+                   dwarf_form_name (form), a);
          free (a);
        }
       break;
index a93b0e9..2f92cc2 100644 (file)
@@ -1,3 +1,17 @@
+2018-05-21  Mark Wielaard  <mark@klomp.org>
+
+       * addrx_constx-4.dwo.bz2: New testfile.
+       * addrx_constx-5.dwo.bz2: Likewise.
+       * testfile-addrx_constx-4.bz2: Likewise.
+       * testfile-addrx_constx-5.bz2: Likewise
+       * Makefile.am (EXTRA_DIST): Add addrx_constx-5.dwo.bz2
+       testfile-addrx_constx-4\ .bz2 testfile-addrx_constx-5.bz2.
+       * run-varlocs.sh: Add addrx_constx tests for DWARF4 and DWARF5.
+       * varlocx.c (print_expr): Handle DW_OP_GNU_addr_index,
+       DW_OP_addrx, DW_OP_GNU_const_index and DW_OP_constx.
+       (main): Handle split DWARF.
+       * run-all-dwarf-ranges.sh: Add new ranges for addrx low/highpc.
+
 2018-05-20  Mark Wielaard  <mark@klomp.org>
 
        * unit-info.c: New test.
index 9beae14..4b13be2 100644 (file)
@@ -305,6 +305,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             testfile_entry_value.c testfile_entry_value.bz2 \
             testfile_implicit_value.c testfile_implicit_value.bz2 \
             testfile_aarch64_core.bz2 testfile_i686_core.bz2 \
+            addrx_constx-4.dwo.bz2 addrx_constx-5.dwo.bz2 \
+            testfile-addrx_constx-4.bz2 testfile-addrx_constx-5.bz2 \
             run-funcretval.sh funcretval_test.c funcretval_test_aarch64.bz2 \
             run-backtrace-data.sh run-backtrace-dwarf.sh cleanup-13.c \
             run-backtrace-native.sh run-backtrace-native-biarch.sh \
diff --git a/tests/addrx_constx-4.dwo.bz2 b/tests/addrx_constx-4.dwo.bz2
new file mode 100644 (file)
index 0000000..f0bae1c
Binary files /dev/null and b/tests/addrx_constx-4.dwo.bz2 differ
diff --git a/tests/addrx_constx-5.dwo.bz2 b/tests/addrx_constx-5.dwo.bz2
new file mode 100644 (file)
index 0000000..a5f4b1a
Binary files /dev/null and b/tests/addrx_constx-5.dwo.bz2 differ
index ba5528d..ad5e634 100755 (executable)
@@ -33,9 +33,30 @@ die: hello.c (11)
  4004e0..4004ff
  4003e0..4003f7
 
+die: no_say (2e)
+ 4004f0..4004ff
+
+die: main (2e)
+ 4003e0..4003f7
+
+die: subject (1d)
+ 4003e3..4003ef
+
+die: subject (2e)
+ 4004e0..4004f0
+
 die: world.c (11)
  400500..400567
 
+die: no_main (2e)
+ 400550..400567
+
+die: no_subject (1d)
+ 400553..40055f
+
+die: say (2e)
+ 400500..400540
+
 die: happy (1d)
  40051c..400526
  400530..400534
@@ -45,6 +66,9 @@ die: sad (1d)
  40051c..400526
  400535..40053f
 
+die: no_subject (2e)
+ 400540..400550
+
 EOF
 
 exit 0
index 9c4b313..2781fef 100755 (executable)
@@ -125,4 +125,212 @@ module 'testfile_implicit_pointer'
 EOF
 
 
+# DW_OP_addrx and DW_OP_constx testcases.
+#
+# int i, j, k;
+# __thread int l, m, n;
+#
+# int main ()
+# {
+#   int r1 = i + j + k;
+#   int r2 = l + m + n;
+#   int res = r1 + r2;
+#
+#   return res;
+# }
+#
+# gcc -O2 -gdwarf-5 -gsplit-dwarf -o addrx_constx-5.o -c addrx_constx.c
+# gcc -O2 -gdwarf-5 -gsplit-dwarf -o testfile-addrx_constx-5 addrx_constx-5.o
+# gcc -O2 -gdwarf-4 -gsplit-dwarf -o addrx_constx-4.o -c addrx_constx.c
+# gcc -O2 -gdwarf-4 -gsplit-dwarf -o testfile-addrx_constx-4 addrx_constx-4.o
+
+testfiles testfile-addrx_constx-5 addrx_constx-5.dwo
+testrun_compare ${abs_top_builddir}/tests/varlocs --exprlocs -e testfile-addrx_constx-5 <<\EOF
+module 'testfile-addrx_constx-5'
+[14] CU 'addrx_constx.c'
+  producer (strx)
+  language (data1)
+  name (strx)
+  comp_dir (strx)
+  [19] variable "i"
+    name (string)
+    decl_file (implicit_const)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {addr: 0x404038}
+  [25] base_type "int"
+    byte_size (data1)
+    encoding (data1)
+    name (string)
+  [2c] variable "j"
+    name (string)
+    decl_file (implicit_const)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {addr: 0x404034}
+  [38] variable "k"
+    name (string)
+    decl_file (implicit_const)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {addr: 0x40403c}
+  [44] variable "l"
+    name (string)
+    decl_file (implicit_const)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {const: 0x403e10, form_tls_address}
+  [51] variable "m"
+    name (string)
+    decl_file (implicit_const)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {const: 0x403e0c, form_tls_address}
+  [5e] variable "n"
+    name (string)
+    decl_file (implicit_const)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {const: 0x403e08, form_tls_address}
+  [6b] subprogram "main"
+    external (flag_present)
+    name (strx)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    low_pc (addrx)
+    high_pc (data8)
+    frame_base (exprloc) {call_frame_cfa {bregx(7,8)}}
+    call_all_calls (flag_present)
+    [7f] variable "r1"
+      name (string)
+      decl_file (implicit_const)
+      decl_line (data1)
+      decl_column (implicit_const)
+      type (ref4)
+      location (exprloc) {addr: 0x404038, deref_size(4), addr: 0x404034, deref_size(4), plus, addr: 0x40403c, deref_size(4), plus, stack_value}
+    [98] variable "r2"
+      name (string)
+      decl_file (implicit_const)
+      decl_line (data1)
+      decl_column (implicit_const)
+      type (ref4)
+      location (exprloc) {form_tls_address, const: 0x403e10, deref_size(4), form_tls_address, const: 0x403e0c, deref_size(4), plus, form_tls_address, const: 0x403e08, deref_size(4), plus, stack_value}
+    [b4] variable "res"
+      name (string)
+      decl_file (implicit_const)
+      decl_line (data1)
+      decl_column (implicit_const)
+      type (ref4)
+      location (exprloc) {addr: 0x404038, deref_size(4), form_tls_address, const: 0x403e08, deref_size(4), plus, form_tls_address, const: 0x403e0c, deref_size(4), plus, form_tls_address, const: 0x403e10, deref_size(4), plus, addr: 0x404034, deref_size(4), plus, addr: 0x40403c, deref_size(4), plus, stack_value}
+EOF
+
+testfiles testfile-addrx_constx-4 addrx_constx-4.dwo
+testrun_compare ${abs_top_builddir}/tests/varlocs --exprlocs -e testfile-addrx_constx-4 <<\EOF
+module 'testfile-addrx_constx-4'
+[b] CU 'addrx_constx.c'
+  producer (GNU_str_index)
+  language (data1)
+  name (GNU_str_index)
+  comp_dir (GNU_str_index)
+  GNU_dwo_id (data8)
+  [18] variable "i"
+    name (string)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {addr: 0x404038}
+  [25] base_type "int"
+    byte_size (data1)
+    encoding (data1)
+    name (string)
+  [2c] variable "j"
+    name (string)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {addr: 0x404034}
+  [39] variable "k"
+    name (string)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {addr: 0x40403c}
+  [46] variable "l"
+    name (string)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {const: 0x403e10, GNU_push_tls_address}
+  [54] variable "m"
+    name (string)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {const: 0x403e0c, GNU_push_tls_address}
+  [62] variable "n"
+    name (string)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    external (flag_present)
+    location (exprloc) {const: 0x403e08, GNU_push_tls_address}
+  [70] subprogram "main"
+    external (flag_present)
+    name (GNU_str_index)
+    decl_file (data1)
+    decl_line (data1)
+    decl_column (data1)
+    type (ref4)
+    low_pc (GNU_addr_index)
+    high_pc (data8)
+    frame_base (exprloc) {call_frame_cfa {bregx(7,8)}}
+    GNU_all_call_sites (flag_present)
+    [84] variable "r1"
+      name (string)
+      decl_file (data1)
+      decl_line (data1)
+      decl_column (data1)
+      type (ref4)
+      location (exprloc) {addr: 0x404038, deref_size(4), addr: 0x404034, deref_size(4), plus, addr: 0x40403c, deref_size(4), plus, stack_value}
+    [9f] variable "r2"
+      name (string)
+      decl_file (data1)
+      decl_line (data1)
+      decl_column (data1)
+      type (ref4)
+      location (exprloc) {GNU_push_tls_address, const: 0x403e10, deref_size(4), GNU_push_tls_address, const: 0x403e0c, deref_size(4), plus, GNU_push_tls_address, const: 0x403e08, deref_size(4), plus, stack_value}
+    [bd] variable "res"
+      name (string)
+      decl_file (data1)
+      decl_line (data1)
+      decl_column (data1)
+      type (ref4)
+      location (exprloc) {addr: 0x404038, deref_size(4), GNU_push_tls_address, const: 0x403e08, deref_size(4), plus, GNU_push_tls_address, const: 0x403e0c, deref_size(4), plus, GNU_push_tls_address, const: 0x403e10, deref_size(4), plus, addr: 0x404034, deref_size(4), plus, addr: 0x40403c, deref_size(4), plus, stack_value}
+EOF
+
 exit 0
diff --git a/tests/testfile-addrx_constx-4.bz2 b/tests/testfile-addrx_constx-4.bz2
new file mode 100755 (executable)
index 0000000..cf10fbb
Binary files /dev/null and b/tests/testfile-addrx_constx-4.bz2 differ
diff --git a/tests/testfile-addrx_constx-5.bz2 b/tests/testfile-addrx_constx-5.bz2
new file mode 100755 (executable)
index 0000000..eb2a1f1
Binary files /dev/null and b/tests/testfile-addrx_constx-5.bz2 differ
index b2ceda2..859068d 100644 (file)
@@ -652,6 +652,42 @@ print_expr (Dwarf_Attribute *attr, Dwarf_Op *expr, Dwarf_Addr addr)
       }
       break;
 
+    case DW_OP_GNU_addr_index:
+    case DW_OP_addrx:
+      /* Address from the .debug_addr section (indexed based on CU).  */
+      {
+       Dwarf_Attribute addr_attr;
+       if (dwarf_getlocation_attr (attr, expr, &addr_attr) != 0)
+         error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for addr: %s",
+                dwarf_errmsg (-1));
+
+       Dwarf_Addr address;
+       if (dwarf_formaddr (&addr_attr, &address) != 0)
+         error (EXIT_FAILURE, 0, "dwarf_formaddr address failed: %s",
+                dwarf_errmsg (-1));
+
+       printf ("addr: 0x%" PRIx64, address);
+      }
+      break;
+
+    case DW_OP_GNU_const_index:
+    case DW_OP_constx:
+      /* Constant from the .debug_addr section (indexed based on CU).  */
+      {
+       Dwarf_Attribute addr_attr;
+       if (dwarf_getlocation_attr (attr, expr, &addr_attr) != 0)
+         error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for addr: %s",
+                dwarf_errmsg (-1));
+
+       Dwarf_Word constant;
+       if (dwarf_formudata (&addr_attr, &constant) != 0)
+         error (EXIT_FAILURE, 0, "dwarf_formudata constant failed: %s",
+                dwarf_errmsg (-1));
+
+       printf ("const: 0x%" PRIx64, constant);
+      }
+      break;
+
     default:
       error (EXIT_FAILURE, 0, "unhandled opcode: DW_OP_%s (0x%x)",
             opname, atom);
@@ -1019,9 +1055,18 @@ main (int argc, char *argv[])
       /* Only walk actual compile units (not partial units) that
         contain code if we are only interested in the function variable
         locations.  */
+      Dwarf_Die cudie;
+      Dwarf_Die subdie;
+      uint8_t unit_type;
+      if (dwarf_cu_info (cu->cu, NULL, &unit_type, &cudie, &subdie,
+                        NULL, NULL, NULL) != 0)
+       error (EXIT_FAILURE, 0, "dwarf_cu_info: %s", dwarf_errmsg (-1));
+      if (unit_type == DW_UT_skeleton)
+       cudie = subdie;
+
       Dwarf_Addr cubase;
-      if (dwarf_tag (cu) == DW_TAG_compile_unit
-         && (exprlocs || dwarf_lowpc (cu, &cubase) == 0))
+      if (dwarf_tag (&cudie) == DW_TAG_compile_unit
+         && (exprlocs || dwarf_lowpc (&cudie, &cubase) == 0))
        {
          found_cu = true;
 
@@ -1043,7 +1088,7 @@ main (int argc, char *argv[])
                              ? modname
                              :  basename (mainfile));
          printf ("module '%s'\n", name);
-         print_die (cu, "CU", 0);
+         print_die (&cudie, "CU", 0);
 
          Dwarf_Addr elfbias;
          Elf *elf = dwfl_module_getelf (mod, &elfbias);
@@ -1060,18 +1105,10 @@ main (int argc, char *argv[])
          GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
          is_ET_REL = ehdr->e_type == ET_REL;
 
-         // Get the actual CU DIE and walk all all DIEs (or just the
-         // functions) inside it.
-         Dwarf_Die cudie;
-         uint8_t offsize;
-         uint8_t addrsize;
-         if (dwarf_diecu (cu, &cudie, &addrsize, &offsize) == NULL)
-           error (EXIT_FAILURE, 0, "dwarf_diecu %s", dwarf_errmsg (-1));
-
          if (exprlocs)
            {
              Dwarf_Addr entrypc;
-             if (dwarf_entrypc (cu, &entrypc) != 0)
+             if (dwarf_entrypc (&cudie, &entrypc) != 0)
                entrypc = 0;
 
              /* XXX - Passing true for has_frame_base is not really true.
@@ -1079,9 +1116,9 @@ main (int argc, char *argv[])
                 attributes. Technically we should check that the DIE
                 (types) are referenced from variables that are defined in
                 a context (function) that has a frame base.  */
-             handle_die (cu, 0, true /* Should be false */, entrypc);
+             handle_die (&cudie, 0, true /* Should be false */, entrypc);
            }
-         else if (dwarf_getfuncs (cu, handle_function, NULL, 0) != 0)
+         else if (dwarf_getfuncs (&cudie, handle_function, NULL, 0) != 0)
            error (EXIT_FAILURE, 0, "dwarf_getfuncs %s",
                   dwarf_errmsg (-1));
        }