2002-06-21 Michal Ludvig <mludvig@suse.cz>

* dwarf2cfi.c (read_encoded_pointer): Don't handle pointer
	encoding anymore.
	(pointer_encoding, enum ptr_encoding): New.
	(execute_cfa_program): Take care about pointer encoding.
	(dwarf2_build_frame_info): Only call parse_frame_info for
	.debug_frame and .eh_frame.
	(parse_frame_info): New, derived from former dwarf2_build_frame_info.
	fixed augmentation handling, added relative addressing,
	ignore duplicate FDEs. Added comments.
This commit is contained in:
Michal Ludvig 2002-06-21 12:12:34 +00:00
parent 7b77dec665
commit 7f0c12ed44
2 changed files with 266 additions and 94 deletions

View File

@ -1,3 +1,15 @@
2002-06-21 Michal Ludvig <mludvig@suse.cz>
* dwarf2cfi.c (read_encoded_pointer): Don't handle pointer
encoding anymore.
(pointer_encoding, enum ptr_encoding): New.
(execute_cfa_program): Take care about pointer encoding.
(dwarf2_build_frame_info): Only call parse_frame_info for
.debug_frame and .eh_frame.
(parse_frame_info): New, derived from former dwarf2_build_frame_info.
fixed augmentation handling, added relative addressing,
ignore duplicate FDEs. Added comments.
2002-06-20 Elena Zannoni <ezannoni@redhat.com>
* event-top.c (command_handler): Don't use space_at_cmd_start

View File

@ -34,7 +34,7 @@
Frame Descriptors. */
struct cie_unit
{
/* Offset of this unit in dwarf_frame_buffer. */
/* Offset of this unit in .debug_frame or .eh_frame. */
ULONGEST offset;
/* A null-terminated string that identifies the augmentation to this CIE or
@ -176,6 +176,14 @@ struct frame_state
struct objfile *objfile;
};
enum ptr_encoding {
PE_absptr = DW_EH_PE_absptr,
PE_pcrel = DW_EH_PE_pcrel,
PE_textrel = DW_EH_PE_textrel,
PE_datarel = DW_EH_PE_datarel,
PE_funcrel = DW_EH_PE_funcrel
};
#define UNWIND_CONTEXT(fi) ((struct context *) (fi->context))
@ -188,8 +196,6 @@ extern file_ptr dwarf_frame_offset;
extern unsigned int dwarf_frame_size;
extern file_ptr dwarf_eh_frame_offset;
extern unsigned int dwarf_eh_frame_size;
static char *dwarf_frame_buffer;
extern char *dwarf2_read_section (struct objfile *objfile, file_ptr offset,
@ -219,6 +225,7 @@ static LONGEST read_sleb128 (bfd * abfd, char **p);
static CORE_ADDR read_pointer (bfd * abfd, char **p);
static CORE_ADDR read_encoded_pointer (bfd * abfd, char **p,
unsigned char encoding);
static enum ptr_encoding pointer_encoding (unsigned char encoding);
static LONGEST read_initial_length (bfd * abfd, char *buf, int *bytes_read);
static ULONGEST read_length (bfd * abfd, char *buf, int *bytes_read,
@ -494,6 +501,9 @@ read_pointer (bfd * abfd, char **p)
}
}
/* This functions only reads appropriate amount of data from *p
* and returns the resulting value. Calling function must handle
* different encoding possibilities itself! */
static CORE_ADDR
read_encoded_pointer (bfd * abfd, char **p, unsigned char encoding)
{
@ -537,22 +547,34 @@ read_encoded_pointer (bfd * abfd, char **p, unsigned char encoding)
"read_encoded_pointer: unknown pointer encoding");
}
if (ret != 0)
switch (encoding & 0xf0)
{
case DW_EH_PE_absptr:
break;
case DW_EH_PE_pcrel:
ret += (CORE_ADDR) * p;
break;
case DW_EH_PE_textrel:
case DW_EH_PE_datarel:
case DW_EH_PE_funcrel:
default:
internal_error (__FILE__, __LINE__,
"read_encoded_pointer: unknown pointer encoding");
}
return ret;
}
/* Variable 'encoding' carries 3 different flags:
* - encoding & 0x0f : size of the address (handled in read_encoded_pointer())
* - encoding & 0x70 : type (absolute, relative, ...)
* - encoding & 0x80 : indirect flag (DW_EH_PE_indirect == 0x80). */
enum ptr_encoding
pointer_encoding (unsigned char encoding)
{
int ret;
if (encoding & DW_EH_PE_indirect)
warning ("CFI: Unsupported pointer encoding: DW_EH_PE_indirect");
switch (encoding & 0x70)
{
case DW_EH_PE_absptr:
case DW_EH_PE_pcrel:
case DW_EH_PE_textrel:
case DW_EH_PE_datarel:
case DW_EH_PE_funcrel:
ret = encoding & 0x70;
break;
default:
internal_error (__FILE__, __LINE__,
"CFI: unknown pointer encoding");
}
return ret;
}
@ -627,6 +649,10 @@ execute_cfa_program (struct objfile *objfile, char *insn_ptr, char *insn_end,
case DW_CFA_set_loc:
fs->pc = read_encoded_pointer (objfile->obfd, &insn_ptr,
fs->addr_encoding);
if (pointer_encoding (fs->addr_encoding) != PE_absptr)
warning ("CFI: DW_CFA_set_loc uses relative addressing");
break;
case DW_CFA_advance_loc1:
@ -1380,39 +1406,46 @@ compare_fde_unit (const void *a, const void *b)
}
/* Build the cie_chunks and fde_chunks tables from informations
in .debug_frame section. */
void
dwarf2_build_frame_info (struct objfile *objfile)
found in .debug_frame and .eh_frame sections. */
/* We can handle both of these sections almost in the same way, however there
are some exceptions:
- CIE ID is -1 in debug_frame, but 0 in eh_frame
- eh_frame may contain some more information that are used only by gcc
(eg. personality pointer, LSDA pointer, ...). Most of them we can ignore.
- In debug_frame FDE's item cie_id contains offset of it's parent CIE.
In eh_frame FDE's item cie_id is a relative pointer to the parent CIE.
Anyway we don't need to bother with this, because we are smart enough
to keep the pointer to the parent CIE of oncomming FDEs in 'last_cie'.
- Although debug_frame items can contain Augmentation as well as
eh_frame ones, I have never seen them non-empty. Thus only in eh_frame
we can encounter for example non-absolute pointers (Aug. 'R').
-- mludvig */
static void
parse_frame_info (struct objfile *objfile, file_ptr frame_offset,
unsigned int frame_size, int eh_frame)
{
bfd *abfd = objfile->obfd;
asection *curr_section_ptr;
char *start = NULL;
char *end = NULL;
int from_eh = 0;
char *frame_buffer = NULL;
char *curr_section_name, *aug_data;
struct cie_unit *last_cie = NULL;
int last_dup_fde = 0;
int aug_len, i;
CORE_ADDR curr_section_vma = 0;
unwind_tmp_obstack_init ();
dwarf_frame_buffer = 0;
frame_buffer = dwarf2_read_section (objfile, frame_offset, frame_size);
if (dwarf_frame_offset)
{
dwarf_frame_buffer = dwarf2_read_section (objfile,
dwarf_frame_offset,
dwarf_frame_size);
start = frame_buffer;
end = frame_buffer + frame_size;
start = dwarf_frame_buffer;
end = dwarf_frame_buffer + dwarf_frame_size;
}
else if (dwarf_eh_frame_offset)
{
dwarf_frame_buffer = dwarf2_read_section (objfile,
dwarf_eh_frame_offset,
dwarf_eh_frame_size);
start = dwarf_frame_buffer;
end = dwarf_frame_buffer + dwarf_eh_frame_size;
from_eh = 1;
}
curr_section_name = eh_frame ? ".eh_frame" : ".debug_frame";
curr_section_ptr = bfd_get_section_by_name (abfd, curr_section_name);
if (curr_section_ptr)
curr_section_vma = curr_section_ptr->vma;
if (start)
{
@ -1420,9 +1453,8 @@ dwarf2_build_frame_info (struct objfile *objfile)
{
unsigned long length;
ULONGEST cie_id;
ULONGEST unit_offset = start - dwarf_frame_buffer;
int bytes_read;
int dwarf64;
ULONGEST unit_offset = start - frame_buffer;
int bytes_read, dwarf64, flag_pcrel;
char *block_end;
length = read_initial_length (abfd, start, &bytes_read);
@ -1430,10 +1462,16 @@ dwarf2_build_frame_info (struct objfile *objfile)
dwarf64 = (bytes_read == 12);
block_end = start + length;
if (length == 0)
{
start = block_end;
continue;
}
cie_id = read_length (abfd, start, &bytes_read, dwarf64);
start += bytes_read;
if ((from_eh && cie_id == 0) || is_cie (cie_id, dwarf64))
if ((eh_frame && cie_id == 0) || is_cie (cie_id, dwarf64))
{
struct cie_unit *cie = cie_unit_alloc ();
char *aug;
@ -1449,87 +1487,186 @@ dwarf2_build_frame_info (struct objfile *objfile)
start++; /* version */
cie->augmentation = aug = start;
while (*start)
start++;
start++; /* skip past NUL */
while (*start++); /* Skips last NULL as well */
cie->code_align = read_uleb128 (abfd, &start);
cie->data_align = read_sleb128 (abfd, &start);
cie->ra = read_1u (abfd, &start);
/* Augmentation:
z Indicates that a uleb128 is present to size the
augmentation section.
L Indicates the encoding (and thus presence) of
an LSDA pointer in the FDE augmentation.
R Indicates a non-default pointer encoding for
FDE code pointers.
P Indicates the presence of an encoding + language
personality routine in the CIE augmentation.
[This info comes from GCC's dwarf2out.c]
*/
if (*aug == 'z')
{
int xtra = read_uleb128 (abfd, &start);
start += xtra;
aug_len = read_uleb128 (abfd, &start);
aug_data = start;
start += aug_len;
++aug;
}
cie->data = start;
cie->data_length = block_end - cie->data;
while (*aug != '\0')
{
if (aug[0] == 'e' && aug[1] == 'h')
{
start += sizeof (void *);
aug += 2;
aug_data += sizeof (void *);
aug++;
}
else if (aug[0] == 'R')
{
cie->addr_encoding = *start++;
aug += 1;
}
cie->addr_encoding = *aug_data++;
else if (aug[0] == 'P')
{
CORE_ADDR ptr;
ptr = read_encoded_pointer (abfd, &start,
cie->addr_encoding);
aug += 1;
CORE_ADDR pers_addr;
int pers_addr_enc;
pers_addr_enc = *aug_data++;
/* We don't need pers_addr value and so we
don't care about it's encoding. */
pers_addr = read_encoded_pointer (abfd, &aug_data,
pers_addr_enc);
}
else if (aug[0] == 'L' && eh_frame)
{
int lsda_addr_enc;
/* Perhaps we should save this to CIE for later use?
Do we need it for something in GDB? */
lsda_addr_enc = *aug_data++;
}
else
warning ("%s(): unknown augmentation", __func__);
warning ("CFI warning: unknown augmentation \"%c\""
" in \"%s\" of\n"
"\t%s", aug[0], curr_section_name,
objfile->name);
aug++;
}
cie->data = start;
cie->data_length = block_end - start;
last_cie = cie;
}
else
{
struct fde_unit *fde;
struct cie_unit *cie;
int dup = 0;
CORE_ADDR init_loc;
fde_chunks_need_space ();
fde = fde_unit_alloc ();
fde_chunks.array[fde_chunks.elems++] = fde;
fde->initial_location = read_pointer (abfd, &start)
+ ANOFFSET (objfile->section_offsets,
SECT_OFF_TEXT (objfile));
fde->address_range = read_pointer (abfd, &start);
cie = cie_chunks;
while (cie)
/* We assume that debug_frame is in order
CIE,FDE,CIE,FDE,FDE,... and thus the CIE for this FDE
should be stored in last_cie pointer. If not, we'll
try to find it by the older way. */
if (last_cie)
cie = last_cie;
else
{
if (cie->objfile == objfile)
{
if (from_eh
&& (cie->offset ==
(unit_offset + bytes_read - cie_id)))
break;
if (!from_eh && (cie->offset == cie_id))
break;
}
warning ("CFI: last_cie == NULL. "
"Perhaps a malformed %s section in '%s'...?\n",
curr_section_name, objfile->name);
cie = cie->next;
cie = cie_chunks;
while (cie)
{
if (cie->objfile == objfile)
{
if (eh_frame &&
(cie->offset ==
(unit_offset + bytes_read - cie_id)))
break;
if (!eh_frame && (cie->offset == cie_id))
break;
}
cie = cie->next;
}
if (!cie)
error ("CFI: can't find CIE pointer");
}
if (!cie)
error ("%s(): can't find CIE pointer", __func__);
fde->cie_ptr = cie;
init_loc = read_encoded_pointer (abfd, &start,
cie->addr_encoding);
if (cie->augmentation[0] == 'z')
read_uleb128 (abfd, &start);
switch (pointer_encoding (cie->addr_encoding))
{
case PE_absptr:
break;
case PE_pcrel:
/* start-frame_buffer gives offset from
the beginning of actual section. */
init_loc += curr_section_vma + start - frame_buffer;
break;
default:
warning ("CFI: Unsupported pointer encoding\n");
}
fde->data = start;
fde->data_length = block_end - start;
/* For relocatable objects we must add an offset telling
where the section is actually mapped in the memory. */
init_loc += ANOFFSET (objfile->section_offsets,
SECT_OFF_TEXT (objfile));
/* If we have both .debug_frame and .eh_frame present in
a file, we must eliminate duplicate FDEs. For now we'll
run through all entries in fde_chunks and check it one
by one. Perhaps in the future we can implement a faster
searching algorithm. */
/* eh_frame==2 indicates, that this file has an already
parsed .debug_frame too. When eh_frame==1 it means, that no
.debug_frame is present and thus we don't need to check for
duplicities. eh_frame==0 means, that we parse .debug_frame
and don't need to care about duplicate FDEs, because
.debug_frame is parsed first. */
if (eh_frame == 2)
for (i = 0; eh_frame == 2 && i < fde_chunks.elems; i++)
{
/* We assume that FDEs in .debug_frame and .eh_frame
have the same order (if they are present, of course).
If we find a duplicate entry for one FDE and save
it's index to last_dup_fde it's very likely, that
we'll find an entry for the following FDE right after
the previous one. Thus in many cases we'll run this
loop only once. */
last_dup_fde = (last_dup_fde + i) % fde_chunks.elems;
if (fde_chunks.array[last_dup_fde]->initial_location
== init_loc)
{
dup = 1;
break;
}
}
/* Allocate a new entry only if this FDE isn't a duplicate of
something we have already seen. */
if (!dup)
{
fde_chunks_need_space ();
fde = fde_unit_alloc ();
fde_chunks.array[fde_chunks.elems++] = fde;
fde->initial_location = init_loc;
fde->address_range = read_encoded_pointer (abfd, &start,
cie->
addr_encoding);
fde->cie_ptr = cie;
/* Here we intentionally ignore augmentation data
from FDE, because we don't need them. */
if (cie->augmentation[0] == 'z')
start += read_uleb128 (abfd, &start);
fde->data = start;
fde->data_length = block_end - start;
}
}
start = block_end;
}
@ -1537,7 +1674,30 @@ dwarf2_build_frame_info (struct objfile *objfile)
sizeof (struct fde_unit *), compare_fde_unit);
}
}
/* We must parse both .debug_frame section and .eh_frame because
* not all frames must be present in both of these sections. */
void
dwarf2_build_frame_info (struct objfile *objfile)
{
int after_debug_frame = 0;
/* If we have .debug_frame then the parser is called with
eh_frame==0 for .debug_frame and eh_frame==2 for .eh_frame,
otherwise it's only called once for .eh_frame with argument
eh_frame==1. */
if (dwarf_frame_offset)
{
parse_frame_info (objfile, dwarf_frame_offset,
dwarf_frame_size, 0 /* = debug_frame */);
after_debug_frame = 1;
}
if (dwarf_eh_frame_offset)
parse_frame_info (objfile, dwarf_eh_frame_offset, dwarf_eh_frame_size,
1 /* = eh_frame */ + after_debug_frame);
}
/* Return the frame address. */
CORE_ADDR