From: Roland McGrath Date: Wed, 23 Jun 2010 09:44:15 +0000 (-0700) Subject: Add dwarf_cfi_frames iterator. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2c011f25376c44f6721a4ddc89dfae4c40313d44;p=thirdparty%2Felfutils.git Add dwarf_cfi_frames iterator. --- diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 17658581d..c22540aa8 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,27 @@ +2010-06-23 Roland McGrath + + * 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 * dwarf_getlocation.c (check_constant_offset): data[48] are constant. diff --git a/libdw/cfi.c b/libdw/cfi.c index e49335ddc..2ea7cfdd9 100644 --- a/libdw/cfi.c +++ b/libdw/cfi.c @@ -76,12 +76,23 @@ duplicate_frame_state (const Dwarf_Frame *original, 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) { @@ -370,7 +381,7 @@ execute_cfi (Dwarf_CFI *cache, /* 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 @@ -378,6 +389,7 @@ execute_cfi (Dwarf_CFI *cache, /* 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; } } @@ -396,13 +408,9 @@ execute_cfi (Dwarf_CFI *cache, 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; @@ -455,7 +463,7 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct dwarf_cie *cie) .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); @@ -464,7 +472,7 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct dwarf_cie *cie) /* 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); @@ -496,7 +504,7 @@ __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde, 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)) @@ -505,6 +513,112 @@ __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde, 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; @@ -517,7 +631,7 @@ dwarf_cfi_validate_fde (cache, offset, start, end, signalp, encoding) 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; diff --git a/libdw/cfi.h b/libdw/cfi.h index ef9cd7e1b..b300b73df 100644 --- a/libdw/cfi.h +++ b/libdw/cfi.h @@ -82,6 +82,7 @@ struct dwarf_cie 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; @@ -116,6 +117,11 @@ struct Dwarf_CFI_s /* 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; @@ -189,6 +195,9 @@ struct Dwarf_Frame_s 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, @@ -229,7 +238,8 @@ extern struct dwarf_fde *__libdw_find_fde (Dwarf_CFI *cache, /* 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, diff --git a/libdw/dwarf_getcfi.c b/libdw/dwarf_getcfi.c index c935631eb..91e179f37 100644 --- a/libdw/dwarf_getcfi.c +++ b/libdw/dwarf_getcfi.c @@ -1,5 +1,5 @@ /* 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 @@ -83,6 +83,8 @@ dwarf_getcfi (dbg) 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; diff --git a/libdw/dwarf_getcfi_elf.c b/libdw/dwarf_getcfi_elf.c index 64a2a8855..50015202e 100644 --- a/libdw/dwarf_getcfi_elf.c +++ b/libdw/dwarf_getcfi_elf.c @@ -87,6 +87,8 @@ allocate_cfi (Elf *elf, GElf_Addr vaddr) cfi->textrel = 0; /* XXX ? */ cfi->datarel = 0; /* XXX ? */ + cfi->fde_tailp = &cfi->first_fde; + return cfi; } diff --git a/libdw/fde.c b/libdw/fde.c index 5685252ba..14e9395da 100644 --- a/libdw/fde.c +++ b/libdw/fde.c @@ -98,6 +98,8 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry) return NULL; } + fde->next = NULL; + fde->instructions = entry->start; fde->instructions_end = entry->end; if (unlikely (read_encoded_value (cache, cie->fde_encoding, @@ -141,32 +143,54 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry) 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; } @@ -251,7 +275,7 @@ __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address) 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)) diff --git a/libdw/libdw.h b/libdw/libdw.h index d3e7a5abe..67eea1329 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -780,6 +780,20 @@ extern int dwarf_cfi_addrframe (Dwarf_CFI *cache, 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.