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 5CCF63858C52 for ; Tue, 31 Oct 2023 20:33:19 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5CCF63858C52 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 5CCF63858C52 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=1698784402; cv=none; b=kfJlyzLbls6laawM+owv8+wQZVkpJy97mh7JST06AFgXRtVVAWiaIokcyX9v9+T9p4v/FOW2RiSjyepG+rkj14Neg1OVnJ8Mp4r3zeWCqaWQ9mkg/MH/kG+u7Ta6UTJ+iP1O/937bmAFQGOnyVjfp6MRZrbIrYYxpwcV4D8lLfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698784402; c=relaxed/simple; bh=l+JfMZ3o0PTRJXboIJwN9rrSGgaEXyxpuCIPPhxCK3Y=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=cYu79dBzg5wyLkcvKQjrxEligCjiKvIvqmG4d5HwpqiO1nECiTfCnD7w/T0jD3bker3ZpQX6PfWhhr9mNe46kD04kKFBf/+cmCLaLsCLT5GonfKRCxZa4d15h/mHMF4ue0zFPdG1PsU55WreqD1akTkkmq9nDxZCR29v/lJwhBo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1698784399; 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=LyB456kILqEjksEyfTaYRht08Y68zxBc1aBpoqI3U/c=; b=hmCK9XyJj4/VvW0c6lyi0GkqE2SWKOZ5Kk2xFmiDWVDbJRdWR8RMUIHuGLPjzkYiKcMl6s /KBdszBtdNGphun17gcnqiVqsvV1024jV2ddwUv/CLYmiQkFs4HjYAzbgGFxHAnKxnetqo k0kRJ1WiUJHXvo0xCi1ENXiaMyV8du4= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-463-LLsOt974MSaVGevPjQfBlw-1; Tue, 31 Oct 2023 16:33:17 -0400 X-MC-Unique: LLsOt974MSaVGevPjQfBlw-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 0454129ABA2A for ; Tue, 31 Oct 2023 20:33:17 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.17.88]) by smtp.corp.redhat.com (Postfix) with ESMTP id 761D040C6EBC; Tue, 31 Oct 2023 20:33:16 +0000 (UTC) From: Aaron Merey To: elfutils-devel@sourceware.org Cc: Aaron Merey Subject: [PATCH] readelf: Support .gdb_index version 9 Date: Tue, 31 Oct 2023 16:33:12 -0400 Message-ID: <20231031203312.536219-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.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_NUMSUBJECT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,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: Hi, I'd like to merge this patch before the next release. Unless anyone objects I'll merge it by Friday Nov 3. Commit message: Version 9 adds a "shortcut table" to the index. The shortcut table contains the name and language of the main function, if it exists. A testcase added in this patch uses an executable written with Fortran. This is because gdb does not currently populate the shortcut table of C/C++ programs (see sourceware PR30996). Signed-off-by: Aaron Merey --- src/readelf.c | 66 +++++++++++++++- tests/Makefile.am | 3 +- tests/run-readelf-gdb_index.sh | 95 +++++++++++++++++++++++- tests/testfilegdbindex9-no-maininfo.bz2 | Bin 0 -> 3502 bytes tests/testfilegdbindex9.bz2 | Bin 0 -> 4266 bytes 5 files changed, 159 insertions(+), 5 deletions(-) create mode 100755 tests/testfilegdbindex9-no-maininfo.bz2 create mode 100755 tests/testfilegdbindex9.bz2 diff --git a/src/readelf.c b/src/readelf.c index db31ad09..a28f6236 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -11539,8 +11539,9 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, // hash used for generating the table. Version 6 contains symbols // for inlined functions, older versions didn't. Version 7 adds // symbol kinds. Version 8 just indicates that it correctly includes - // TUs for symbols. - if (vers < 4 || vers > 8) + // TUs for symbols. Version 9 adds shortcut table for information + // regarding the main function. + if (vers < 4 || vers > 9) { printf (_(" unknown version, cannot parse section\n")); return; @@ -11578,6 +11579,17 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, if (unlikely (readp + 4 > dataend)) goto invalid_data; + uint32_t shortcut_off = 0; + if (vers >= 9) + { + shortcut_off = read_4ubyte_unaligned (dbg, readp); + printf (_(" shortcut offset: %#" PRIx32 "\n"), shortcut_off); + + readp += 4; + if (unlikely (readp + 4 > dataend)) + goto invalid_data; + } + uint32_t const_off = read_4ubyte_unaligned (dbg, readp); printf (_(" constant offset: %#" PRIx32 "\n"), const_off); @@ -11675,8 +11687,19 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, if (const_off >= data->d_size) goto invalid_data; + const unsigned char *shortcut_start = NULL; + if (vers >= 9) + { + if (shortcut_off >= data->d_size) + goto invalid_data; + + shortcut_start = data->d_buf + shortcut_off; + nextp = shortcut_start; + } + else + nextp = const_start; + readp = data->d_buf + sym_off; - nextp = const_start; size_t sym_nr = (nextp - readp) / 8; printf (_("\n Symbol table at offset %#" PRIx32 @@ -11750,6 +11773,43 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, } n++; } + + if (vers < 9) + return; + + if (unlikely (shortcut_start == NULL)) + goto invalid_data; + + readp = shortcut_start; + nextp = const_start; + size_t shortcut_nr = (nextp - readp) / 4; + + if (unlikely (shortcut_nr != 2)) + goto invalid_data; + + printf (_("\nShortcut table at offset %#" PRIx32 " contains %zu slots:\n"), + shortcut_off, shortcut_nr); + + uint32_t lang = read_4ubyte_unaligned (dbg, readp); + readp += 4; + + printf (_("Language of main: %s\n"), dwarf_lang_name (lang)); + printf (_("Name of main: ")); + + if (lang != 0) + { + uint32_t name = read_4ubyte_unaligned (dbg, readp); + readp += 4; + const unsigned char *sym = const_start + name; + + if (unlikely ((size_t) (dataend - const_start) < name + || memchr (sym, '\0', dataend - sym) == NULL)) + goto invalid_data; + + printf ("%s\n", sym); + } + else + printf ("\n"); } /* Returns true and sets split DWARF CU id if there is a split compile diff --git a/tests/Makefile.am b/tests/Makefile.am index ef5b6bb5..e8bc9058 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -421,7 +421,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ run-readelf-Dd.sh \ testfile-s390x-hash-both.bz2 \ run-readelf-gdb_index.sh testfilegdbindex5.bz2 \ - testfilegdbindex7.bz2 \ + testfilegdbindex7.bz2 testfilegdbindex9.bz2 \ + testfilegdbindex9-no-maininfo.bz2 \ run-readelf-s.sh testfilebazdbg.bz2 testfilebazdyn.bz2 \ testfilebazmin.bz2 testfilebazdbg.debug.bz2 testfilebazmdb.bz2 \ testfilebaztab.bz2 testfilebasmin.bz2 testfilebaxmin.bz2 \ diff --git a/tests/run-readelf-gdb_index.sh b/tests/run-readelf-gdb_index.sh index fcbc3c57..95367ef8 100755 --- a/tests/run-readelf-gdb_index.sh +++ b/tests/run-readelf-gdb_index.sh @@ -63,7 +63,7 @@ # (gdb) save gdb-index . # objcopy --add-section .gdb_index=testfilegdbindex7.gdb-index --set-section-flags .gdb_index=readonly testfilegdbindex7 testfilegdbindex7 -testfiles testfilegdbindex5 testfilegdbindex7 +testfiles testfilegdbindex5 testfilegdbindex7 testfilegdbindex9 testfilegdbindex9-no-maininfo testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex5 <<\EOF @@ -127,4 +127,97 @@ GDB section [33] '.gdb_index' at offset 0xe76 contains 8399 bytes : [ 754] symbol: int, CUs: 0 (type:S) EOF +# testfilegdbindex9-no-maininfo is built the same way as testfilegdbindex7. +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex9-no-maininfo <<\EOF + +GDB section [33] '.gdb_index' at offset 0x38e1 contains 8415 bytes : + Version: 9 + CU offset: 0x1c + TU offset: 0x3c + address offset: 0x54 + symbol offset: 0x7c + shortcut offset: 0x207c + constant offset: 0x2084 + + CU list at offset 0x1c contains 2 entries: + [ 0] start: 0x00004c, length: 220 + [ 1] start: 0x000128, length: 214 + + TU list at offset 0x3c contains 1 entries: + [ 0] CU offset: 0, type offset: 30, signature: 0x87e03f92cc37cdf0 + + Address list at offset 0x54 contains 2 entries: + [ 0] 0x0000000000401106
..0x000000000040113b , CU index: 1 + [ 1] 0x000000000040113c ..0x0000000000401173 , CU index: 2 + + Symbol table at offset 0x54 contains 1024 slots: + [ 123] symbol: global, CUs: 1 (var:G), 0T (var:G) + [ 489] symbol: main, CUs: 1 (func:G) + [ 518] symbol: char, CUs: 0 (type:S) + [ 661] symbol: foo, CUs: 0 (type:S) + [ 741] symbol: hello, CUs: 1 (var:S), 0T (func:S) + [ 746] symbol: say, CUs: 0T (func:G) + [ 754] symbol: int, CUs: 1 (type:S) + +Shortcut table at offset 0x207c contains 2 slots: +Language of main: ??? +Name of main: +EOF + +# testfilegdbindex9.f90 +# +# program repro +# type small_stride +# character*40 long_string +# integer small_pad +# end type small_stride +# type(small_stride), dimension (20), target :: unpleasant +# character*40, pointer, dimension(:):: c40pt +# integer i +# do i = 0,19 +# unpleasant(i+1)%small_pad = i+1 +# unpleasant(i+1)%long_string = char (ichar('0') + i) +# end do +# c40pt => unpleasant%long_string +# print *, c40pt +#end program repro + +# gfortran -g -o testfilegdbindex9 testfilegdbindex9.f90 +# gdb-add-index testfilegdbindex9 + +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex9 <<\EOF + +GDB section [35] '.gdb_index' at offset 0x37d9 contains 8395 bytes : + Version: 9 + CU offset: 0x1c + TU offset: 0x2c + address offset: 0x2c + symbol offset: 0x40 + shortcut offset: 0x2040 + constant offset: 0x2048 + + CU list at offset 0x1c contains 1 entries: + [ 0] start: 00000000, length: 307 + + TU list at offset 0x2c contains 0 entries: + + Address list at offset 0x2c contains 1 entries: + [ 0] 0x0000000000401166 ..0x00000000004013f0 , CU index: 0 + + Symbol table at offset 0x2c contains 1024 slots: + [ 61] symbol: small_stride, CUs: 0 (type:S) + [ 71] symbol: integer(kind=8), CUs: 0 (type:S) + [ 161] symbol: character(kind=1), CUs: 0 (type:S) + [ 397] symbol: unpleasant, CUs: 0 (var:S) + [ 489] symbol: main, CUs: 0 (func:G) + [ 827] symbol: integer(kind=4), CUs: 0 (type:S) + [ 858] symbol: c40pt, CUs: 0 (var:S) + [ 965] symbol: repro, CUs: 0 (func:S) + [1016] symbol: i, CUs: 0 (var:S) + +Shortcut table at offset 0x2040 contains 2 slots: +Language of main: Fortran08 +Name of main: repro +EOF + exit 0 diff --git a/tests/testfilegdbindex9-no-maininfo.bz2 b/tests/testfilegdbindex9-no-maininfo.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..043b8d38df6c19365c39f2c6d23d1c49ff507d14 GIT binary patch literal 3502 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