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 <marat@slonopotamus.org>
Submitted by:	Tiger Gao <tig@freebsdfoundation.org>
Reviewed by:	markj
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D23501
This commit is contained in:
Ed Maste 2020-02-12 21:52:10 +00:00
parent 99a7fa9268
commit 6c4a4f1bc2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=357844

View File

@ -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 @@ collect_func(Dwarf_Debug dbg, Dwarf_Die die, struct Func *parent, struct CU *cu)
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);