diff options
Diffstat (limited to 'libdw/cfi.c')
-rw-r--r-- | libdw/cfi.c | 138 |
1 files changed, 126 insertions, 12 deletions
diff --git a/libdw/cfi.c b/libdw/cfi.c index f59f17d6..d9221375 100644 --- a/libdw/cfi.c +++ b/libdw/cfi.c @@ -55,12 +55,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) { @@ -349,7 +360,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 @@ -357,6 +368,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; } } @@ -375,13 +387,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; @@ -434,7 +442,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); @@ -443,7 +451,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); @@ -475,7 +483,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)) @@ -483,3 +491,109 @@ __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; +} |