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.
This commit is contained in:
parent
aa8f4d1e5e
commit
49727e4617
2 changed files with 170 additions and 81 deletions
|
@ -1,3 +1,11 @@
|
|||
2014-09-22 Alan Modra <amodra@gmail.com>
|
||||
|
||||
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 <nickc@redhat.com>
|
||||
|
||||
* readelf.c (display_arm_attribute): Use unsigned int type for
|
||||
|
|
243
binutils/dwarf.c
243
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",
|
||||
|
|
Loading…
Reference in a new issue