From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 2533E3858CDB for ; Mon, 26 Feb 2024 15:40:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2533E3858CDB Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2533E3858CDB Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1708962030; cv=none; b=qIzcznHmmvSjRnilNHoCaemtpIvdHVjaoRrelSdVnp/wUs3zCgAj8ENjask+UiT71iqBUQefh9SuFzxMvsa4lkBMdwKNe1diESMnNhgNGSUUmbIFtleMh1oAymSv8Q3ev+eO+wB/P6vAKZ3QfGJ6geuiDuOfrQ1oKId+f87kPN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1708962030; c=relaxed/simple; bh=GJSkl9kV34Fb1DA6XlO5jYLYeGzDYkD6j6OPD7ym5bM=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=dYdiIRqcSiQflJC2Q5tCP8nI3tgrUUdM9Kg6is+KrcRdmzuuOaKiSU1j3p35wOAx1dxFDuIyXUfDgSWtyjy2EDtTYM3UHuW/aTV9Eks4lKbKXGzInNPesS5tqK2wLh6xCO4pnWsINEAXQXDIX1MREVDMqdbaeq0ZR8g2fz7x+58= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1708962027; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Dbgt/AHZRc/DOHtvKqQN99tog8aBn1zLXKs9Rr8A9Do=; b=YRodUBAUxxuGVNC/s1u6N3YYknF7WKrB36hRH8Gjovs+NFHg89CXyww8XVM2KDXJ+yniFy tPtBYREQMriSlWnw9uriaVwI68BhWEsqgDefWSyc6TIktB3FsOsTIx3bg3xbM51NjYicmL UhH1GrSyeYxyMB/pLYGwAoTDcCkNupg= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-479-mIwrapGpOOuqQjIm3y3YAQ-1; Mon, 26 Feb 2024 10:40:26 -0500 X-MC-Unique: mIwrapGpOOuqQjIm3y3YAQ-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 30D21185A785 for ; Mon, 26 Feb 2024 15:40:26 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.18.96]) by smtp.corp.redhat.com (Postfix) with ESMTP id B0670400D784; Mon, 26 Feb 2024 15:40:25 +0000 (UTC) From: Aaron Merey To: elfutils-devel@sourceware.org Cc: Aaron Merey Subject: [PATCH] Add __libdw_getdieranges Date: Mon, 26 Feb 2024 10:40:23 -0500 Message-ID: <20240226154023.72098-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.2 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: __libdw_getdieranges builds an aranges list by iterating over CUs and recording each address range. __libdw_getdieranges provides an alternative to relying on .debug_aranges for address ranges, since this section might be absent or incomplete. https://sourceware.org/bugzilla/show_bug.cgi?id=22288 https://sourceware.org/bugzilla/show_bug.cgi?id=30948 Signed-off-by: Aaron Merey --- Once this function's interface is refined it can be added to the public libdw API. We might add some form of lazy loading, for example. I mentioned [1] that there are some clang-compiled shared libraries where some subprograms starting at address 0 don't have a corresponding entry in .debug_ranges (for a CU that contains DW_AT_ranges). These address ranges with no corresponding entry in .debug_ranges won't be included in the aranges generated by this patch's CU iteration approach. [1] https://sourceware.org/pipermail/elfutils-devel/2024q1/006832.html libdw/dwarf_addrdie.c | 2 +- libdw/dwarf_getaranges.c | 190 ++++++++++++++++++++++++++++++--------- libdw/libdwP.h | 13 ++- libdwfl/cu.c | 8 +- tests/run-getsrc-die.sh | 5 ++ 5 files changed, 169 insertions(+), 49 deletions(-) diff --git a/libdw/dwarf_addrdie.c b/libdw/dwarf_addrdie.c index 3a08ab75..48a1aaea 100644 --- a/libdw/dwarf_addrdie.c +++ b/libdw/dwarf_addrdie.c @@ -41,7 +41,7 @@ dwarf_addrdie (Dwarf *dbg, Dwarf_Addr addr, Dwarf_Die *result) size_t naranges; Dwarf_Off off; - if (INTUSE(dwarf_getaranges) (dbg, &aranges, &naranges) != 0 + if (__libdw_getdieranges (dbg, &aranges, &naranges) != 0 || INTUSE(dwarf_getarangeinfo) (INTUSE(dwarf_getarange_addr) (aranges, addr), NULL, NULL, &off) != 0) diff --git a/libdw/dwarf_getaranges.c b/libdw/dwarf_getaranges.c index 27439d37..41fe96d0 100644 --- a/libdw/dwarf_getaranges.c +++ b/libdw/dwarf_getaranges.c @@ -33,7 +33,6 @@ #endif #include -#include #include "libdwP.h" #include @@ -54,6 +53,149 @@ compare_aranges (const void *a, const void *b) return 0; } +/* Convert ARANGELIST into Dwarf_Aranges and store at ARANGES. */ +static bool +finalize_aranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges, + struct arangelist *arangelist, unsigned int narangelist) +{ + /* Allocate the array for the result. */ + void *buf = libdw_alloc (dbg, Dwarf_Aranges, + sizeof (Dwarf_Aranges) + + narangelist * sizeof (Dwarf_Arange), 1); + + /* First use the buffer for the pointers, and sort the entries. + We'll write the pointers in the end of the buffer, and then + copy into the buffer from the beginning so the overlap works. */ + eu_static_assert (sizeof (Dwarf_Arange) >= sizeof (Dwarf_Arange *)); + struct arangelist **sortaranges + = (buf + sizeof (Dwarf_Aranges) + + ((sizeof (Dwarf_Arange) - sizeof sortaranges[0]) * narangelist)); + + /* The list is in LIFO order and usually they come in clumps with + ascending addresses. So fill from the back to probably start with + runs already in order before we sort. */ + unsigned int i = narangelist; + while (i-- > 0) + { + sortaranges[i] = arangelist; + arangelist = arangelist->next; + } + + /* Something went wrong if narangelist is less then the actual length + of arangelist. */ + if (arangelist != NULL) + { + __libdw_seterrno (DWARF_E_UNKNOWN_ERROR); + return false; + } + + /* Sort by ascending address. */ + qsort (sortaranges, narangelist, sizeof sortaranges[0], &compare_aranges); + + /* Now that they are sorted, put them in the final array. + The buffers overlap, so we've clobbered the early elements + of SORTARANGES by the time we're reading the later ones. */ + *aranges = buf; + (*aranges)->dbg = dbg; + (*aranges)->naranges = narangelist; + if (naranges != NULL) + *naranges = narangelist; + for (i = 0; i < narangelist; ++i) + { + struct arangelist *elt = sortaranges[i]; + (*aranges)->info[i] = elt->arange; + free (elt); + } + + return true; +} + +int +__libdw_getdieranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges) +{ + if (dbg == NULL) + return -1; + + if (dbg->dieranges != NULL) + { + *aranges = dbg->dieranges; + if (naranges != NULL) + *naranges = dbg->dieranges->naranges; + return 0; + } + + struct arangelist *arangelist = NULL; + unsigned int narangelist = 0; + + Dwarf_CU *cu = NULL; + while (INTUSE(dwarf_get_units) (dbg, cu, &cu, NULL, NULL, NULL, NULL) == 0) + { + Dwarf_Addr base; + Dwarf_Addr low; + Dwarf_Addr high; + + Dwarf_Die cudie = CUDIE (cu); + + /* Skip CUs that only contain type information. */ + if (!INTUSE(dwarf_hasattr) (&cudie, DW_AT_low_pc) + && !INTUSE(dwarf_hasattr) (&cudie, DW_AT_ranges)) + continue; + + ptrdiff_t offset = 0; + + /* Add arange for each range list entry or high_pc and low_pc. */ + while ((offset = INTUSE(dwarf_ranges) (&cudie, offset, + &base, &low, &high)) > 0) + { + if (offset == -1) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + goto fail; + } + + struct arangelist *new_arange = malloc (sizeof *new_arange); + if (unlikely (new_arange == NULL)) + { + __libdw_seterrno (DWARF_E_NOMEM); + goto fail; + } + + new_arange->arange.addr = low; + new_arange->arange.length = (Dwarf_Word) (high - low); + new_arange->arange.offset = __libdw_first_die_off_from_cu (cu); + + new_arange->next = arangelist; + arangelist = new_arange; + ++narangelist; + } + } + + if (narangelist == 0) + { + if (arangelist != NULL) + goto fail; + if (naranges != NULL) + *naranges = 0; + *aranges = NULL; + return 0; + } + + if (!finalize_aranges (dbg, aranges, naranges, arangelist, narangelist)) + goto fail; + + dbg->dieranges = *aranges; + return 0; + +fail: + while (arangelist != NULL) + { + struct arangelist *next = arangelist->next; + free (arangelist); + arangelist = next; + } + return -1; +} + int dwarf_getaranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges) { @@ -235,56 +377,18 @@ dwarf_getaranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges) if (narangelist == 0) { - assert (arangelist == NULL); + if (arangelist != NULL) + goto fail; if (naranges != NULL) *naranges = 0; *aranges = NULL; return 0; } - /* Allocate the array for the result. */ - void *buf = libdw_alloc (dbg, Dwarf_Aranges, - sizeof (Dwarf_Aranges) - + narangelist * sizeof (Dwarf_Arange), 1); - - /* First use the buffer for the pointers, and sort the entries. - We'll write the pointers in the end of the buffer, and then - copy into the buffer from the beginning so the overlap works. */ - assert (sizeof (Dwarf_Arange) >= sizeof (Dwarf_Arange *)); - struct arangelist **sortaranges - = (buf + sizeof (Dwarf_Aranges) - + ((sizeof (Dwarf_Arange) - sizeof sortaranges[0]) * narangelist)); - - /* The list is in LIFO order and usually they come in clumps with - ascending addresses. So fill from the back to probably start with - runs already in order before we sort. */ - unsigned int i = narangelist; - while (i-- > 0) - { - sortaranges[i] = arangelist; - arangelist = arangelist->next; - } - assert (arangelist == NULL); - - /* Sort by ascending address. */ - qsort (sortaranges, narangelist, sizeof sortaranges[0], &compare_aranges); + if (!finalize_aranges (dbg, aranges, naranges, arangelist, narangelist)) + goto fail; - /* Now that they are sorted, put them in the final array. - The buffers overlap, so we've clobbered the early elements - of SORTARANGES by the time we're reading the later ones. */ - *aranges = buf; - (*aranges)->dbg = dbg; - (*aranges)->naranges = narangelist; dbg->aranges = *aranges; - if (naranges != NULL) - *naranges = narangelist; - for (i = 0; i < narangelist; ++i) - { - struct arangelist *elt = sortaranges[i]; - (*aranges)->info[i] = elt->arange; - free (elt); - } - return 0; } INTDEF(dwarf_getaranges) diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 75e0283b..714e95e3 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -232,9 +232,12 @@ struct Dwarf /* Search tree for decoded .debug_line units. */ void *files_lines; - /* Address ranges. */ + /* Address ranges read from .debug_aranges. */ Dwarf_Aranges *aranges; + /* Address ranges inferred from CUs. */ + Dwarf_Aranges *dieranges; + /* Cached info from the CFI section. */ struct Dwarf_CFI_s *cfi; @@ -1472,4 +1475,12 @@ void __libdw_set_debugdir (Dwarf *dbg); char * __libdw_filepath (const char *debugdir, const char *dir, const char *file) internal_function; + +/* Like dwarf_getaranges but aranges are generated from CU address + ranges instead of being read from .debug_aranges. + + Returns 0 if successful and updates ARANGES and NARANGES. Otherwise + returns -1 and sets libdw_errno. +*/ +int __libdw_getdieranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges); #endif /* libdwP.h */ diff --git a/libdwfl/cu.c b/libdwfl/cu.c index b1afb19a..06684357 100644 --- a/libdwfl/cu.c +++ b/libdwfl/cu.c @@ -39,7 +39,7 @@ static inline Dwarf_Arange * dwar (Dwfl_Module *mod, unsigned int idx) { - return &mod->dw->aranges->info[mod->aranges[idx].arange]; + return &mod->dw->dieranges->info[mod->aranges[idx].arange]; } @@ -51,7 +51,7 @@ addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange) struct dwfl_arange *aranges = NULL; Dwarf_Aranges *dwaranges = NULL; size_t naranges; - if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0) + if (__libdw_getdieranges (mod->dw, &dwaranges, &naranges) != 0) return DWFL_E_LIBDW; /* If the module has no aranges (when no code is included) we @@ -119,7 +119,7 @@ addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange) { /* It might be in the last range. */ const Dwarf_Arange *last - = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1]; + = &mod->dw->dieranges->info[mod->dw->dieranges->naranges - 1]; if (addr > last->addr + last->length) break; } @@ -296,7 +296,7 @@ arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu) { if (arange->cu == NULL) { - const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange]; + const Dwarf_Arange *dwarange = &mod->dw->dieranges->info[arange->arange]; Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu); if (result != DWFL_E_NOERROR) return result; diff --git a/tests/run-getsrc-die.sh b/tests/run-getsrc-die.sh index 4da16e7a..3418b33a 100755 --- a/tests/run-getsrc-die.sh +++ b/tests/run-getsrc-die.sh @@ -23,6 +23,11 @@ # dwarf_getsrc_die testfiles testfile testfile-inlines testfile-lex-inlines +# The following tests should pass without .debug_aranges present. +objcopy --remove-section .debug_aranges testfile +objcopy --remove-section .debug_aranges testfile-inlines +objcopy --remove-section .debug_aranges testfile-lex-inlines + testrun_compare ${abs_top_builddir}/tests/getsrc_die testfile 0x08048468 0x0804845c <<\EOF /home/drepper/gnu/new-bu/build/ttt/f.c:3 /home/drepper/gnu/new-bu/build/ttt/b.c:4 -- 2.43.0