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:
Alan Modra 2014-09-22 17:53:15 +09:30
parent aa8f4d1e5e
commit 49727e4617
2 changed files with 170 additions and 81 deletions

View file

@ -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

View file

@ -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",