From 49727e46175419c638251b39091f24c575568bee Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Mon, 22 Sep 2014 17:53:15 +0930 Subject: [PATCH] Readelf: Handle forward references to CIEs The linker side of pr16563 was fixed with commit 18cd5bce, but unfortunately people continue to use older linkers with -flto. This means we have binaries with working .eh_frame that can't be dumped by readelf, and I'm seeing internal IBM bug reports about this fact. PR 16563 * dwarf.c (GET): Remove semicolon. (read_cie): New function, extracted from.. (display_debug_frames): ..here. Correctly handle signed offset from FDE to CIE in .eh_frame. Decode forward referenced CIEs too. --- binutils/ChangeLog | 8 ++ binutils/dwarf.c | 243 ++++++++++++++++++++++++++++++--------------- 2 files changed, 170 insertions(+), 81 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 223bf119b3..e8b2dafe20 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,11 @@ +2014-09-22 Alan Modra + + PR 16563 + * dwarf.c (GET): Remove semicolon. + (read_cie): New function, extracted from.. + (display_debug_frames): ..here. Correctly handle signed offset + from FDE to CIE in .eh_frame. Decode forward referenced CIEs too. + 2014-09-16 Nick Clifton * readelf.c (display_arm_attribute): Use unsigned int type for diff --git a/binutils/dwarf.c b/binutils/dwarf.c index 348e20ff4a..3f4095a26e 100644 --- a/binutils/dwarf.c +++ b/binutils/dwarf.c @@ -5232,10 +5232,97 @@ frame_display_row (Frame_Chunk *fc, int *need_col_headers, int *max_regs) printf ("\n"); } -#define GET(VAR, N) SAFE_BYTE_GET_AND_INC (VAR, start, N, end); +#define GET(VAR, N) SAFE_BYTE_GET_AND_INC (VAR, start, N, end) #define LEB() read_uleb128 (start, & length_return, end); start += length_return #define SLEB() read_sleb128 (start, & length_return, end); start += length_return +static unsigned char * +read_cie (unsigned char *start, unsigned char *end, + Frame_Chunk **p_cie, int *p_version, + unsigned long *p_aug_len, unsigned char **p_aug) +{ + int version; + Frame_Chunk *fc; + unsigned int length_return; + unsigned char *augmentation_data = NULL; + unsigned long augmentation_data_len = 0; + + fc = (Frame_Chunk *) xmalloc (sizeof (Frame_Chunk)); + memset (fc, 0, sizeof (Frame_Chunk)); + + fc->col_type = (short int *) xmalloc (sizeof (short int)); + fc->col_offset = (int *) xmalloc (sizeof (int)); + + version = *start++; + + fc->augmentation = (char *) start; + start = (unsigned char *) strchr ((char *) start, '\0') + 1; + + if (strcmp (fc->augmentation, "eh") == 0) + start += eh_addr_size; + + if (version >= 4) + { + GET (fc->ptr_size, 1); + GET (fc->segment_size, 1); + eh_addr_size = fc->ptr_size; + } + else + { + fc->ptr_size = eh_addr_size; + fc->segment_size = 0; + } + fc->code_factor = LEB (); + fc->data_factor = SLEB (); + if (version == 1) + { + GET (fc->ra, 1); + } + else + { + fc->ra = LEB (); + } + + if (fc->augmentation[0] == 'z') + { + augmentation_data_len = LEB (); + augmentation_data = start; + start += augmentation_data_len; + } + + if (augmentation_data_len) + { + unsigned char *p, *q; + p = (unsigned char *) fc->augmentation + 1; + q = augmentation_data; + + while (1) + { + if (*p == 'L') + q++; + else if (*p == 'P') + q += 1 + size_of_encoded_value (*q); + else if (*p == 'R') + fc->fde_encoding = *q++; + else if (*p == 'S') + ; + else + break; + p++; + } + } + + *p_cie = fc; + if (p_version) + *p_version = version; + if (p_aug_len) + { + *p_aug_len = augmentation_data_len; + *p_aug = augmentation_data; + } + return start; +} + static int display_debug_frames (struct dwarf_section *section, void *file ATTRIBUTE_UNUSED) @@ -5243,7 +5330,7 @@ display_debug_frames (struct dwarf_section *section, unsigned char *start = section->start; unsigned char *end = start + section->size; unsigned char *section_start = start; - Frame_Chunk *chunks = 0; + Frame_Chunk *chunks = 0, *forward_refs = 0; Frame_Chunk *remembered_state = 0; Frame_Chunk *rs; int is_eh = strcmp (section->name, ".eh_frame") == 0; @@ -5306,55 +5393,20 @@ display_debug_frames (struct dwarf_section *section, || (offset_size == 8 && cie_id == DW64_CIE_ID))) { int version; + int mreg; - fc = (Frame_Chunk *) xmalloc (sizeof (Frame_Chunk)); - memset (fc, 0, sizeof (Frame_Chunk)); - + start = read_cie (start, end, &cie, &version, + &augmentation_data_len, &augmentation_data); + fc = cie; fc->next = chunks; chunks = fc; fc->chunk_start = saved_start; - fc->ncols = 0; - fc->col_type = (short int *) xmalloc (sizeof (short int)); - fc->col_offset = (int *) xmalloc (sizeof (int)); - frame_need_space (fc, max_regs - 1); - - version = *start++; - - fc->augmentation = (char *) start; - start = (unsigned char *) strchr ((char *) start, '\0') + 1; - - if (strcmp (fc->augmentation, "eh") == 0) - start += eh_addr_size; - - if (version >= 4) - { - GET (fc->ptr_size, 1); - GET (fc->segment_size, 1); - eh_addr_size = fc->ptr_size; - } - else - { - fc->ptr_size = eh_addr_size; - fc->segment_size = 0; - } - fc->code_factor = LEB (); - fc->data_factor = SLEB (); - if (version == 1) - { - GET (fc->ra, 1); - } - else - { - fc->ra = LEB (); - } - - if (fc->augmentation[0] == 'z') - { - augmentation_data_len = LEB (); - augmentation_data = start; - start += augmentation_data_len; - } - cie = fc; + mreg = max_regs - 1; + if (mreg < fc->ra) + mreg = fc->ra; + frame_need_space (fc, mreg); + if (fc->fde_encoding) + encoded_ptr_size = size_of_encoded_value (fc->fde_encoding); printf ("\n%08lx ", (unsigned long) (saved_start - section_start)); print_dwarf_vma (length, fc->ptr_size); @@ -5389,33 +5441,6 @@ display_debug_frames (struct dwarf_section *section, } putchar ('\n'); } - - if (augmentation_data_len) - { - unsigned char *p, *q; - p = (unsigned char *) fc->augmentation + 1; - q = augmentation_data; - - while (1) - { - if (*p == 'L') - q++; - else if (*p == 'P') - q += 1 + size_of_encoded_value (*q); - else if (*p == 'R') - fc->fde_encoding = *q++; - else if (*p == 'S') - ; - else - break; - p++; - } - - if (fc->fde_encoding) - encoded_ptr_size = size_of_encoded_value (fc->fde_encoding); - } - - frame_need_space (fc, fc->ra); } else { @@ -5423,15 +5448,71 @@ display_debug_frames (struct dwarf_section *section, static Frame_Chunk fde_fc; unsigned long segment_selector; - fc = & fde_fc; + if (is_eh) + { + dwarf_vma sign = (dwarf_vma) 1 << (offset_size * 8 - 1); + look_for = start - 4 - ((cie_id ^ sign) - sign); + } + else + look_for = section_start + cie_id; + + if (look_for <= saved_start) + { + for (cie = chunks; cie ; cie = cie->next) + if (cie->chunk_start == look_for) + break; + } + else + { + for (cie = forward_refs; cie ; cie = cie->next) + if (cie->chunk_start == look_for) + break; + if (!cie) + { + unsigned int off_size; + unsigned char *cie_scan; + + cie_scan = look_for; + off_size = 4; + SAFE_BYTE_GET_AND_INC (length, cie_scan, 4, end); + if (length == 0xffffffff) + { + SAFE_BYTE_GET_AND_INC (length, cie_scan, 8, end); + off_size = 8; + } + if (length != 0) + { + dwarf_vma c_id; + + SAFE_BYTE_GET_AND_INC (c_id, cie_scan, off_size, end); + if (is_eh + ? c_id == 0 + : ((off_size == 4 && c_id == DW_CIE_ID) + || (off_size == 8 && c_id == DW64_CIE_ID))) + { + int version; + int mreg; + + read_cie (cie_scan, end, &cie, &version, + &augmentation_data_len, &augmentation_data); + cie->next = forward_refs; + forward_refs = cie; + cie->chunk_start = look_for; + mreg = max_regs - 1; + if (mreg < cie->ra) + mreg = cie->ra; + frame_need_space (cie, mreg); + if (cie->fde_encoding) + encoded_ptr_size + = size_of_encoded_value (cie->fde_encoding); + } + } + } + } + + fc = &fde_fc; memset (fc, 0, sizeof (Frame_Chunk)); - look_for = is_eh ? start - 4 - cie_id : section_start + cie_id; - - for (cie = chunks; cie ; cie = cie->next) - if (cie->chunk_start == look_for) - break; - if (!cie) { warn ("Invalid CIE pointer 0x%s in FDE at %#08lx\n",