+2010-06-23 Roland McGrath <roland@redhat.com>
+
+ * cfi.c (dwarf_cfi_frames): New function.
+ * libdw.h: Declare it.
+
+ * fde.c (__libdw_fde_by_offset): Take new argument NEXTOFF.
+ If non-null, return -1 marker without error at end of section,
+ skip CIEs, and store next offset. Maintain fde_tailp and next.
+ * cfi.h: Update decl.
+
+ * cfi.h (struct dwarf_fde): New member next.
+ (struct Dwarf_CFI_s): New members first_fde, fde_tailp.
+ * dwarf_getcfi.c (dwarf_getcfi): Initialize them.
+ * dwarf_getcfi_elf.c (allocate_cfi): Likewise.
+ * fde.c (intern_fde): Initialize next.
+
+ * cfi.c (clear_prev): New function, broken out of ...
+ (execute_cfi): ... here. Call it. Take new flag argument; when
+ false, stop at the first PC change and don't clear_prev.
+ (cie_cache_initial_state, __libdw_frame_at_address): Update callers.
+
+ * cfi.h (struct Dwarf_Frame_s): New member fde_pc.
+ * cfi.c (execute_cfi): Set it to pending PROGRAM pointer at return.
+
2010-06-22 Roland McGrath <roland@redhat.com>
* dwarf_getlocation.c (check_constant_offset): data[48] are constant.
return copy;
}
+static void
+clear_prev (Dwarf_Frame *fs)
+{
+ while (fs->prev != NULL)
+ {
+ Dwarf_Frame *prev = fs->prev;
+ fs->prev = prev->prev;
+ free (prev);
+ }
+}
+
/* Returns a DWARF_E_* error code, usually NOERROR or INVALID_CFI.
Frees *STATE on failure. */
static int
execute_cfi (Dwarf_CFI *cache,
const struct dwarf_cie *cie,
- Dwarf_Frame **state,
+ Dwarf_Frame **state, bool searching,
const uint8_t *program, const uint8_t *const end, bool abi_cfi,
Dwarf_Addr loc, Dwarf_Addr find_pc)
{
/* We get here only for the cases that have just moved LOC. */
cfi_assert (cie->initial_state != NULL);
- if (find_pc >= loc)
+ if (searching && find_pc >= loc)
/* This advance has not yet reached FIND_PC. */
fs->start = loc;
else
/* We have just advanced past the address we're looking for.
The state currently described is what we want to see. */
fs->end = loc;
+ fs->fde_pc = program;
break;
}
}
out:
- /* Pop any remembered states left on the stack. */
- while (fs->prev != NULL)
- {
- Dwarf_Frame *prev = fs->prev;
- fs->prev = prev->prev;
- free (prev);
- }
+ if (searching || unlikely (result != DWARF_E_NOERROR))
+ /* Pop any remembered states left on the stack. */
+ clear_prev (fs);
if (likely (result == DWARF_E_NOERROR))
*state = fs;
.code_alignment_factor = abi_info.code_alignment_factor,
.data_alignment_factor = abi_info.data_alignment_factor,
};
- result = execute_cfi (cache, &abi_cie, &cie_fs,
+ result = execute_cfi (cache, &abi_cie, &cie_fs, true,
abi_info.initial_instructions,
abi_info.initial_instructions_end, true,
0, (Dwarf_Addr) -1l);
/* Now run the CIE's initial instructions. */
if (cie->initial_instructions_end > cie->initial_instructions
&& likely (result == DWARF_E_NOERROR))
- result = execute_cfi (cache, cie, &cie_fs,
+ result = execute_cfi (cache, cie, &cie_fs, true,
cie->initial_instructions,
cie->initial_instructions_end, false,
0, (Dwarf_Addr) -1l);
fs->start = fde->start;
fs->end = fde->end;
- result = execute_cfi (cache, fde->cie, &fs,
+ result = execute_cfi (cache, fde->cie, &fs, true,
fde->instructions, fde->instructions_end, false,
fde->start, address);
if (likely (result == DWARF_E_NOERROR))
return result;
}
+ptrdiff_t
+dwarf_cfi_frames (Dwarf_CFI *cache, ptrdiff_t offset,
+ void **state, Dwarf_Frame **framep)
+{
+ if (cache == NULL)
+ return -1;
+
+ if (offset < 0)
+ {
+ /* Special case call for cleanup. */
+
+ if (*state != NULL)
+ {
+ clear_prev (*state);
+ free (*state);
+ *state = NULL;
+ }
+ return 0;
+ }
+
+ Dwarf_Frame *fs = *state;
+
+ struct dwarf_fde *fde;
+ if (offset == 0)
+ {
+ /* Start at the beginning. */
+ assert (fs == NULL);
+ fde = cache->first_fde ?: (void *) -1l;
+ }
+ else
+ {
+ /* Resume from the last iteration. */
+ assert (fs != NULL);
+ fde = fs->fde;
+ if (fs->fde_pc == fde->instructions_end)
+ {
+ /* We've hit the end of this FDE. Move to the next one. */
+ clear_prev (fs);
+ free (fs);
+ *state = fs = NULL;
+ fde = fde->next;
+ }
+ }
+
+ if (fs == NULL)
+ {
+ /* We're starting fresh on a new FDE. */
+
+ if (fde == (void *) -1l)
+ {
+ /* No cached next FDE. We have to intern the next one. */
+
+ fde = __libdw_fde_by_offset (cache, offset, &offset);
+ if (fde == (void *) -1l)
+ /* End of the line. */
+ return 0;
+ }
+
+ /* Start from this FDE's CIE's initial state. */
+ int result = cie_cache_initial_state (cache, fde->cie);
+ if (likely (result == DWARF_E_NOERROR))
+ {
+ fs = duplicate_frame_state (fde->cie->initial_state, NULL);
+ if (unlikely (fs == NULL))
+ result = DWARF_E_NOMEM;
+ }
+ if (unlikely (result != DWARF_E_NOERROR))
+ {
+ __libdw_seterrno (result);
+ return -1;
+ }
+
+ fs->fde_pc = fde->instructions;
+ *state = fs;
+ }
+
+ /* Now play forward from the last position in the FDE. */
+
+ assert (fs->fde == fde);
+ assert (fs->fde_pc < fde->instructions_end);
+ int result = execute_cfi (cache, fde->cie, &fs, false,
+ fs->fde_pc, fde->instructions_end, false,
+ fs->end, 0);
+ if (likely (result == DWARF_E_NOERROR))
+ {
+ *framep = duplicate_frame_state (fs, NULL);
+ if (unlikely (*framep == NULL))
+ {
+ clear_prev (fs);
+ free (fs);
+ fs = NULL;
+ result = DWARF_E_NOMEM;
+ }
+ }
+
+ *state = fs;
+
+ if (unlikely (result != DWARF_E_NOERROR))
+ {
+ __libdw_seterrno (result);
+ offset = -1;
+ }
+
+ return offset;
+}
+
int
dwarf_cfi_validate_fde (cache, offset, start, end, signalp, encoding)
Dwarf_CFI *cache;
if (cache == NULL)
return -1;
- struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
+ struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset, NULL);
if (unlikely (fde == NULL))
return -1;
struct dwarf_fde
{
struct dwarf_cie *cie;
+ struct dwarf_fde *next; /* Chain from cie->first_fde. */
/* This FDE describes PC values in [start, end). */
Dwarf_Addr start;
/* Search tree for the FDEs, indexed by PC address. */
void *fde_tree;
+ /* Linked list of FDEs read so far, in file order.
+ fde_tailp will get the FDE at or after next_offset. */
+ struct dwarf_fde *first_fde;
+ struct dwarf_fde **fde_tailp;
+
/* Search tree for parsed DWARF expressions, indexed by raw pointer. */
void *expr_tree;
which has the return_address_register and signal_frame flag. */
struct dwarf_fde *fde;
+ /* Next instruction in that FDE to execute after this state. */
+ const uint8_t *fde_pc;
+
/* The CFA is unknown, is R+N, or is computed by a DWARF expression.
A bogon in the CFI can indicate an invalid/incalculable rule.
We store that as cfa_invalid rather than barfing when processing it,
/* Look for an FDE by its offset in the section. */
extern struct dwarf_fde *__libdw_fde_by_offset (Dwarf_CFI *cache,
- Dwarf_Off offset)
+ Dwarf_Off offset,
+ ptrdiff_t *next_offset)
__nonnull_attribute__ (1) internal_function;
/* Process the FDE that contains the given PC address,
/* Get CFI from DWARF file.
- Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2009-2010 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
cfi->next_offset = 0;
cfi->cie_tree = cfi->fde_tree = cfi->expr_tree = NULL;
+ cfi->first_fde = NULL;
+ cfi->fde_tailp = &cfi->first_fde;
cfi->ebl = NULL;
cfi->textrel = 0; /* XXX ? */
cfi->datarel = 0; /* XXX ? */
+ cfi->fde_tailp = &cfi->first_fde;
+
return cfi;
}
return NULL;
}
+ fde->next = NULL;
+
fde->instructions = entry->start;
fde->instructions_end = entry->end;
if (unlikely (read_encoded_value (cache, cie->fde_encoding,
struct dwarf_fde *
internal_function
-__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
+__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset, ptrdiff_t *nextoff)
{
Dwarf_CFI_Entry entry;
- Dwarf_Off next_offset;
- int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
- &cache->data->d, CFI_IS_EH (cache),
- offset, &next_offset, &entry);
- if (result != 0)
+ while (1)
{
- if (result > 0)
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return NULL;
- }
+ Dwarf_Off next_offset;
+ int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
+ &cache->data->d, CFI_IS_EH (cache),
+ offset, &next_offset, &entry);
+ if (result != 0)
+ {
+ if (result > 0)
+ {
+ if (nextoff != NULL)
+ return (void *) -1l;
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ }
+ return NULL;
+ }
+
+ /* If this happened to be what we would have read next, notice it. */
+ if (cache->next_offset == offset)
+ cache->next_offset = next_offset;
+
+ offset = next_offset;
+
+ if (!dwarf_cfi_cie_p (&entry))
+ {
+ if (nextoff != NULL)
+ *nextoff = next_offset;
+ break;
+ }
- if (unlikely (dwarf_cfi_cie_p (&entry)))
- goto invalid;
+ if (nextoff == NULL)
+ goto invalid;
+ }
/* We have a new FDE to consider. */
struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
if (fde == (void *) -1l || fde == NULL)
return NULL;
- /* If this happened to be what we would have read next, notice it. */
- if (cache->next_offset == offset)
- cache->next_offset = next_offset;
+ if (offset == cache->next_offset)
+ {
+ *cache->fde_tailp = fde;
+ cache->fde_tailp = &fde->next;
+ }
return fde;
}
Dwarf_Off offset = binary_search_fde (cache, address);
if (offset == (Dwarf_Off) -1l)
goto no_match;
- struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
+ struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset, NULL);
if (unlikely (fde != NULL)
/* Sanity check the address range. */
&& unlikely (address < fde->start || address >= fde->end))
Dwarf_Addr address, Dwarf_Frame **frame)
__nonnull_attribute__ (3);
+/* Iterate through the distinct CFI states. In the first call, OFFSET
+ is zero and *STATE is null. Each subsequent call gets the previous
+ call's return value as OFFSET and *STATE as the previous call left
+ it. Returns -1 for errors, or 0 when there are no more states to
+ fetch; these cases clean up *STATE and leave it null. A positive
+ return value leaves some iteration state in *STATE and fills *FRAME
+ with the malloc'd pointer for one distinct CFI state. To abort
+ iteration before getting a nonpositive return value, pass -1 as
+ OFFSET to clean up *STATE and leave it null. */
+extern ptrdiff_t dwarf_cfi_frames (Dwarf_CFI *cache, ptrdiff_t offset,
+ void **state, Dwarf_Frame **frame)
+ __nonnull_attribute__ (3, 4);
+
+
/* Return the DWARF register number used in FRAME to denote
the return address in FRAME's caller frame. The remaining
arguments can be non-null to fill in more information.