Author: emaste
Date: Wed Feb 12 21:52:10 2020
New Revision: 357844
URL: https://svnweb.freebsd.org/changeset/base/357844

Log:
  addr2line: Handle DW_AT_ranges in compile units
  
  Based on original submission by Marat Radchenko in ELF Tool Chain
  ticket #545, rebased and updated by Tiger Gao.
  
  PR:           217736
  Submitted by: Marat Radchenko <ma...@slonopotamus.org>
  Submitted by: Tiger Gao <t...@freebsdfoundation.org>
  Reviewed by:  markj
  MFC after:    2 weeks
  Sponsored by: The FreeBSD Foundation
  Differential Revision:        https://reviews.freebsd.org/D23501

Modified:
  head/contrib/elftoolchain/addr2line/addr2line.c

Modified: head/contrib/elftoolchain/addr2line/addr2line.c
==============================================================================
--- head/contrib/elftoolchain/addr2line/addr2line.c     Wed Feb 12 21:16:30 
2020        (r357843)
+++ head/contrib/elftoolchain/addr2line/addr2line.c     Wed Feb 12 21:52:10 
2020        (r357844)
@@ -65,6 +65,7 @@ struct CU {
        Dwarf_Signed nsrcfiles;
        STAILQ_HEAD(, Func) funclist;
        Dwarf_Die die;
+       Dwarf_Debug dbg;
 };
 
 static struct option longopts[] = {
@@ -345,7 +346,8 @@ cont_search:
                collect_func(dbg, ret_die, parent, cu);
 
        /* Cleanup */
-       dwarf_dealloc(dbg, die, DW_DLA_DIE);
+       if (die != cu->die)
+               dwarf_dealloc(dbg, die, DW_DLA_DIE);
 
        if (abst_die != NULL)
                dwarf_dealloc(dbg, abst_die, DW_DLA_DIE);
@@ -411,6 +413,102 @@ culookup(Dwarf_Unsigned addr)
        return (NULL);
 }
 
+/*
+ * Check whether addr falls into range(s) of current CU, and save current CU
+ * to lookup tree if so.
+ */
+static int
+check_range(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Unsigned addr,
+    struct CU **cu)
+{
+       Dwarf_Error de;
+       Dwarf_Unsigned addr_base, lopc, hipc;
+       Dwarf_Off ranges_off;
+       Dwarf_Signed ranges_cnt;
+       Dwarf_Ranges *ranges;
+       int i, ret;
+       bool in_range;
+
+       addr_base = 0;
+       ranges = NULL;
+       ranges_cnt = 0;
+       in_range = false;
+
+       ret = dwarf_attrval_unsigned(die, DW_AT_ranges, &ranges_off, &de);
+       if (ret == DW_DLV_NO_ENTRY) {
+               if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) ==
+                   DW_DLV_OK) {
+                       if (lopc == curlopc)
+                               return (DW_DLV_ERROR);
+                       if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc,
+                               &de) == DW_DLV_OK) {
+                               /*
+                                * Check if the address falls into the PC
+                                * range of this CU.
+                                */
+                               if (handle_high_pc(die, lopc, &hipc) !=
+                                       DW_DLV_OK)
+                                       return (DW_DLV_ERROR);
+                       } else {
+                               /* Assume ~0ULL if DW_AT_high_pc not present */
+                               hipc = ~0ULL;
+                       }
+
+                       if (addr >= lopc && addr < hipc) {
+                               in_range = true;
+                       }
+               }
+       } else if (ret == DW_DLV_OK) {
+               ret = dwarf_get_ranges(dbg, ranges_off, &ranges,
+                       &ranges_cnt, NULL, &de);
+               if (ret != DW_DLV_OK)
+                       return (ret);
+
+               if (!ranges || ranges_cnt <= 0)
+                       return (DW_DLV_ERROR);
+
+               for (i = 0; i < ranges_cnt; i++) {
+                       if (ranges[i].dwr_type == DW_RANGES_END)
+                               return (DW_DLV_NO_ENTRY);
+
+                       if (ranges[i].dwr_type ==
+                               DW_RANGES_ADDRESS_SELECTION) {
+                               addr_base = ranges[i].dwr_addr2;
+                               continue;
+                       }
+
+                       /* DW_RANGES_ENTRY */
+                       lopc = ranges[i].dwr_addr1 + addr_base;
+                       hipc = ranges[i].dwr_addr2 + addr_base;
+
+                       if (lopc == curlopc)
+                               return (DW_DLV_ERROR);
+
+                       if (addr >= lopc && addr < hipc){
+                               in_range = true;
+                               break;
+                       }
+               }
+       } else {
+               return (DW_DLV_ERROR);
+       }
+       
+       if (in_range) {
+               if ((*cu = calloc(1, sizeof(struct CU))) == NULL)
+                       err(EXIT_FAILURE, "calloc");
+               (*cu)->lopc = lopc;
+               (*cu)->hipc = hipc;
+               (*cu)->die = die;
+               (*cu)->dbg = dbg;
+               STAILQ_INIT(&(*cu)->funclist);
+               RB_INSERT(cutree, &cuhead, *cu);
+               curlopc = lopc;
+               return (DW_DLV_OK);
+       } else {
+               return (DW_DLV_NO_ENTRY);
+       }
+}
+
 static void
 translate(Dwarf_Debug dbg, Elf *e, const char* addrstr)
 {
@@ -418,10 +516,9 @@ translate(Dwarf_Debug dbg, Elf *e, const char* addrstr
        Dwarf_Line *lbuf;
        Dwarf_Error de;
        Dwarf_Half tag;
-       Dwarf_Unsigned lopc, hipc, addr, lineno, plineno;
+       Dwarf_Unsigned addr, lineno, plineno;
        Dwarf_Signed lcount;
        Dwarf_Addr lineaddr, plineaddr;
-       Dwarf_Off off;
        struct CU *cu;
        struct Func *f;
        const char *funcname;
@@ -439,6 +536,7 @@ translate(Dwarf_Debug dbg, Elf *e, const char* addrstr
        cu = culookup(addr);
        if (cu != NULL) {
                die = cu->die;
+               dbg = cu->dbg;
                goto status_ok;
        }
 
@@ -477,44 +575,11 @@ translate(Dwarf_Debug dbg, Elf *e, const char* addrstr
                        warnx("could not find DW_TAG_compile_unit die");
                        goto next_cu;
                }
-               if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) ==
-                   DW_DLV_OK) {
-                       if (lopc == curlopc)
-                               goto out;
-                       if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc,
-                          &de) == DW_DLV_OK) {
-                               /*
-                                * Check if the address falls into the PC
-                                * range of this CU.
-                                */
-                               if (handle_high_pc(die, lopc, &hipc) !=
-                                   DW_DLV_OK)
-                                       goto out;
-                       } else {
-                               /* Assume ~0ULL if DW_AT_high_pc not present */
-                               hipc = ~0ULL;
-                       }
-
-                       if (dwarf_dieoffset(die, &off, &de) != DW_DLV_OK) {
-                               warnx("dwarf_dieoffset failed: %s",
-                                   dwarf_errmsg(de));
-                               goto out;
-                       }
-
-                       if (addr >= lopc && addr < hipc) {
-                               if ((cu = calloc(1, sizeof(*cu))) == NULL)
-                                       err(EXIT_FAILURE, "calloc");
-                               cu->off = off;
-                               cu->lopc = lopc;
-                               cu->hipc = hipc;
-                               cu->die = die;
-                               STAILQ_INIT(&cu->funclist);
-                               RB_INSERT(cutree, &cuhead, cu);
-
-                               curlopc = lopc;
-                               break;
-                       }
-               }
+               ret = check_range(dbg, die, addr, &cu);
+               if (ret == DW_DLV_OK)
+                       break;
+               if (ret == DW_DLV_ERROR)
+                       goto out;
 next_cu:
                if (die != NULL) {
                        dwarf_dealloc(dbg, die, DW_DLA_DIE);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to