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.133.124]) by sourceware.org (Postfix) with ESMTPS id 842CC3858413 for ; Fri, 19 Jan 2024 14:38:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 842CC3858413 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 842CC3858413 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1705675135; cv=none; b=P1ZTk4ZIz4piEG5ntyny53loq9XKzHd5rNghDkozmqm7RvoOn93KkMUFVb4QFEy3RfyaOmmlQsFNMVX/tLIxhWrdVNSJAyPjsmqOyprpbjKbplw3W9ACrIaxfu+8SVgTwnyiN60z8djjosi3bxzwI2zBKXS2RCA9apaubuVaLVw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1705675135; c=relaxed/simple; bh=ELO3Jv0fANbRYPoJ8QrDqCIzvsY+DjvyOIevXm9j5uM=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=XrshscqE7wSyvEVmUlWJOs++TtPzlrZGnMLBggCT0woABxJN3Vi5sRtzRxy4Lxvg9+Ig4xo9r44CYzBsEAlGwsq1hZPcD3o+29jVW1cfjRq5ny0r5dPVngjpEMlvBRDIggjcNkEEzyNmnWJiXSD8JdBu/nYMrI02oCg7SiANRkI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1705675133; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=miY7vReKjfiXr91d4fq/z+Y6W1+gt2LCrVbKAYp3xjo=; b=AT5cRIIzZlD2LyLRApluhj7cllGeQ7gp8wGSTD/BQTbTGi4kj46u+eOjM+SAR4Ds+Eu8fX eMaqwXLXd8442Awz2OJJE5LAcMuXH7TLeJxleh5Mz7vzxdAZKmocQxsmpHMg01VxlYmA3P cDY1nySAerH9JAlzto7VHIe/FImr6ok= 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-570-7KiSerSwNa2iI6XYQOnx-Q-1; Fri, 19 Jan 2024 09:38:51 -0500 X-MC-Unique: 7KiSerSwNa2iI6XYQOnx-Q-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (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 4D7CD1C41B2B for ; Fri, 19 Jan 2024 14:38:51 +0000 (UTC) Received: from prancer.redhat.com (unknown [10.42.28.71]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D0BBD492BE4 for ; Fri, 19 Jan 2024 14:38:50 +0000 (UTC) From: Nick Clifton To: binutils@sourceware.org Subject: Commit: Display the contents of .eh_frame_hdr alongside .eh_frame Date: Fri, 19 Jan 2024 14:38:49 +0000 Message-ID: <8734utl75y.fsf@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Status: No, score=-11.1 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_H3,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: --=-=-= Content-Type: text/plain Hi Guys, I recently discovered that when readelf or objdump are used to display the contents of a .eh_frame section, they ignore the .eh_frame_hdr section (if present). This is especially embarrassing as both eu-readelf and llvm-readelf display the contents of this section. So I am applying the attached patch to rectify this situation. I chose to (mostly) mimic the format used by eu-readelf as this seems to be fairly compact. Cheers Nick --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=eh-frame-hdr.patch diff --git a/binutils/NEWS b/binutils/NEWS index a4c101c7787..7b7ac1cbba1 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,9 @@ -*- text -*- +* When objdump or readelf are used to display the contents of a .eh_frame + section they will now also display the contents of the .eh_frame_hdr section, + if present. + Changes in 2.42: * The objdump program has a new command line option -Z/--decompress which diff --git a/binutils/dwarf.c b/binutils/dwarf.c index 615e051b2bf..a30bc1cb9ab 100644 --- a/binutils/dwarf.c +++ b/binutils/dwarf.c @@ -9160,6 +9160,265 @@ display_augmentation_data (const unsigned char * data, uint64_t len) display_data (i, data, len); } +static const char * +decode_eh_encoding (unsigned int value) +{ + if (value == DW_EH_PE_omit) + return "omit"; + + char * format; + switch (value & 0x0f) + { + case DW_EH_PE_uleb128: format = "uleb128"; break; + case DW_EH_PE_udata2: format = "udata2"; break; + case DW_EH_PE_udata4: format = "udata4"; break; + case DW_EH_PE_udata8: format = "udata8"; break; + case DW_EH_PE_sleb128: format = "sleb128"; break; + case DW_EH_PE_sdata2: format = "sdata2"; break; + case DW_EH_PE_sdata4: format = "sdata4"; break; + case DW_EH_PE_sdata8: format = "sdata8"; break; + + default: format = ""; break; /* FIXME: Generate a warning ? */ + } + + char * application; + switch (value & 0xf0) + { + case DW_EH_PE_absptr: application = "absolute"; break; + case DW_EH_PE_pcrel: application = "pcrel"; break; + case DW_EH_PE_textrel: application = "textrel"; break; /* FIXME: Is this allowed ? */ + case DW_EH_PE_datarel: application = "datarel"; break; + case DW_EH_PE_funcrel: application = "funcrel"; break; /* FIXME: Is this allowed ? */ + case DW_EH_PE_aligned: application = "aligned"; break; /* FIXME: Is this allowed ? */ + case DW_EH_PE_indirect: application = "indirect"; break; /* FIXME: Is this allowed ? */ + + default: application = ""; break; /* FIXME: Generate a warning ? */ + } + + static char buffer[128]; + sprintf (buffer, "%s, %s", format, application); + return buffer; +} + +/* Reads a value stored at START encoded according to ENCODING. + Does not read from, or past, END. + Upon success, returns the read value and sets * RETURN_LEN to + the number of bytes read. + Upon failure returns zero and sets * RETURN_LEN to 0. + + Note: does not perform any application transformations to the value. */ + +static uint64_t +get_encoded_eh_value (unsigned int encoding, + unsigned char * start, + unsigned char * end, + unsigned int * return_len) +{ + uint64_t val; + unsigned int len; + int status; + unsigned char * old_start; + + switch (encoding & 0x0f) + { + case DW_EH_PE_uleb128: + val = read_leb128 (start, end, false, & len, & status); + if (status != 0) + len = 0; + break; + + case DW_EH_PE_sleb128: + val = read_leb128 (start, end, true, & len, & status); + if (status != 0) + len = 0; + break; + + case DW_EH_PE_udata2: + old_start = start; + SAFE_BYTE_GET_AND_INC (val, start, 2, end); + len = (start == old_start) ? 0 : 2; + break; + + case DW_EH_PE_udata4: + old_start = start; + SAFE_BYTE_GET_AND_INC (val, start, 4, end); + len = (start == old_start) ? 0 : 4; + break; + + case DW_EH_PE_udata8: + old_start = start; + SAFE_BYTE_GET_AND_INC (val, start, 8, end); + len = (start == old_start) ? 0 : 8; + break; + + case DW_EH_PE_sdata2: + old_start = start; + SAFE_SIGNED_BYTE_GET_AND_INC (val, start, 2, end); + len = (start == old_start) ? 0 : 2; + break; + + case DW_EH_PE_sdata4: + old_start = start; + SAFE_SIGNED_BYTE_GET_AND_INC (val, start, 4, end); + len = (start == old_start) ? 0 : 4; + break; + + case DW_EH_PE_sdata8: + old_start = start; + SAFE_SIGNED_BYTE_GET_AND_INC (val, start, 8, end); + len = (start == old_start) ? 0 : 8; + break; + + default: + goto fail; + } + + * return_len = len; + return val; + + fail: + * return_len = 0; + return 0; + +} + +static uint64_t +encoded_eh_offset (unsigned int encoding, + struct dwarf_section * section, + uint64_t section_offset, + uint64_t value) +{ + switch (encoding & 0xf0) + { + default: + /* This should not happen. FIXME: warn ? */ + case DW_EH_PE_absptr: + return value; + + case DW_EH_PE_pcrel: + return value + (uint64_t)(section->address + section_offset); + + case DW_EH_PE_datarel: + return value + (uint64_t)section->address; + } +} + +static int +display_eh_frame_hdr (struct dwarf_section *section, + void *file ATTRIBUTE_UNUSED) +{ + unsigned char *start = section->start; + unsigned char *end = start + section->size; + + introduce (section, false); + + if (section->size < 6) + { + warn (_(".eh_frame_hdr section is too small\n")); + return 0; + } + + unsigned int version = start[0]; + if (version != 1) + { + warn (_("Unsupported .eh_frame_hdr version %u\n"), version); + return 0; + } + + printf (_(" Version: %u\n"), version); + + unsigned int ptr_enc = start[1]; + /* Strictly speaking this is the encoding format of the eh_frame_ptr field below. */ + printf (_(" Pointer Encoding Format: %#x (%s)\n"), ptr_enc, decode_eh_encoding (ptr_enc)); + + unsigned int count_enc = start[2]; + printf (_(" Count Encoding Format: %#x (%s)\n"), count_enc, decode_eh_encoding (count_enc)); + + unsigned int table_enc = start[3]; + printf (_(" Table Encoding Format: %#x (%s)\n"), table_enc, decode_eh_encoding (table_enc)); + + start += 4; + + unsigned int len; + + uint64_t eh_frame_ptr = get_encoded_eh_value (ptr_enc, start, end, & len); + if (len == 0) + { + warn (_("unable to read eh_frame_ptr field in .eh_frame_hdr section\n")); + return 0; + } + printf (_(" Start of frame section: %#" PRIx64), eh_frame_ptr); + + uint64_t offset_eh_frame_ptr = encoded_eh_offset (ptr_enc, section, 4, eh_frame_ptr); + if (offset_eh_frame_ptr != eh_frame_ptr) + printf (_(" (offset: %#" PRIx64 ")"), offset_eh_frame_ptr); + + printf ("\n"); + start += len; + + if (count_enc == DW_EH_PE_omit) + { + warn (_("It is suspicious to have a .eh_frame_hdr section with an empty search table\n")); + return 0; + } + + if (count_enc & 0xf0) + { + warn (_("The count field format should be absolute, not relative to an address\n")); + return 0; + } + + uint64_t fde_count = get_encoded_eh_value (count_enc, start, end, & len); + if (len == 0) + { + warn (_("unable to read fde_count field in .eh_frame_hdr section\n")); + return 0; + } + printf (_(" Entries in search table: %#" PRIx64), fde_count); + printf ("\n"); + start += len; + + if (fde_count != 0 && table_enc == DW_EH_PE_omit) + { + warn (_("It is suspicious to have a .eh_frame_hdr section an empty table but a non empty count field\n")); + return 0; + } + + uint64_t i; + /* Read and display the search table. */ + for (i = 0; i < fde_count; i++) + { + uint64_t location, address; + unsigned char * row_start = start; + + location = get_encoded_eh_value (table_enc, start, end, & len); + if (len == 0) + { + warn (_("Failed to read location field for entry %#" PRIx64 " in the .eh_frame_hdr's search table\n"), i); + return 0; + } + start += len; + + address = get_encoded_eh_value (table_enc, start, end, & len); + if (len == 0) + { + warn (_("Failed to read address field for entry %#" PRIx64 " in the .eh_frame_hdr's search table\n"), i); + return 0; + } + start += len; + + /* This format is intended to be compatible with the output of eu-readelf's -e option. */ + printf (" %#" PRIx64 " (offset: %#" PRIx64 ") -> %#" PRIx64 " fde=[ %5" PRIx64 "]\n", + location, + encoded_eh_offset (table_enc, section, row_start - section->start, location), + address, + encoded_eh_offset (table_enc, section, row_start - section->start, address) - offset_eh_frame_ptr); + } + + printf ("\n"); + return 1; +} + static int display_debug_frames (struct dwarf_section *section, void *file ATTRIBUTE_UNUSED) @@ -12472,6 +12731,7 @@ struct dwarf_section_display debug_displays[] = { { ".debug_pubnames", ".zdebug_pubnames", ".dwpbnms", NO_ABBREVS }, display_debug_pubnames, &do_debug_pubnames, false }, { { ".debug_gnu_pubnames", ".zdebug_gnu_pubnames", "", NO_ABBREVS }, display_debug_gnu_pubnames, &do_debug_pubnames, false }, { { ".eh_frame", "", "", NO_ABBREVS }, display_debug_frames, &do_debug_frames, true }, + { { ".eh_frame_hdr", "", "", NO_ABBREVS }, display_eh_frame_hdr, &do_debug_frames, true }, { { ".debug_macinfo", ".zdebug_macinfo", "", NO_ABBREVS }, display_debug_macinfo, &do_debug_macinfo, false }, { { ".debug_macro", ".zdebug_macro", ".dwmac", NO_ABBREVS }, display_debug_macro, &do_debug_macinfo, true }, { { ".debug_str", ".zdebug_str", ".dwstr", NO_ABBREVS }, display_debug_str, &do_debug_str, false }, diff --git a/binutils/dwarf.h b/binutils/dwarf.h index 75c4282930f..e81028f2b88 100644 --- a/binutils/dwarf.h +++ b/binutils/dwarf.h @@ -83,6 +83,7 @@ enum dwarf_section_display_enum pubnames, gnu_pubnames, eh_frame, + eh_frame_hdr, macinfo, macro, str, diff --git a/binutils/readelf.c b/binutils/readelf.c index 5e4ad6ea6ad..e3bf68064c1 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -7888,6 +7888,8 @@ process_section_headers (Filedata * filedata) request_dump_bynumber (&filedata->dump, i, DEBUG_DUMP); else if (do_debug_frames && streq (name, ".eh_frame")) request_dump_bynumber (&filedata->dump, i, DEBUG_DUMP); + else if (do_debug_frames && streq (name, ".eh_frame_hdr")) + request_dump_bynumber (&filedata->dump, i, DEBUG_DUMP); else if (do_gdb_index && (streq (name, ".gdb_index") || streq (name, ".debug_names"))) request_dump_bynumber (&filedata->dump, i, DEBUG_DUMP); --=-=-=--