From 3e4b5bbeca8987527c11a1ea048459a7ebd4ab5e Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Wed, 16 Jun 2010 03:40:56 -0700 Subject: [PATCH] Add new dwarf_cfi_validate_fde call. --- NEWS | 2 ++ libdw/ChangeLog | 9 +++++++++ libdw/cfi.c | 39 +++++++++++++++++++++++++++++++++++++++ libdw/cfi.h | 5 +++++ libdw/fde.c | 21 +++++++++++++-------- libdw/libdw.h | 14 ++++++++++++++ libdw/libdw.map | 5 +++++ 7 files changed, 87 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index fe141380a..9fc203e89 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ Version 0.148: libdw: Accept DWARF 4 format. +libdw: New function dwarf_cfi_validate_fde. + libdwfl: Fixes in core-file handling, support cores from PIEs. When working from build IDs, don't open a named file that mismatches. diff --git a/libdw/ChangeLog b/libdw/ChangeLog index f974e96d4..7a1078904 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,5 +1,14 @@ 2010-06-16 Roland McGrath + * fde.c (fde_by_offset): Renamed to ... + (__libdw_fde_by_offset): ... this, made global and internal_function. + Don't take ADDRESS argument. + (__libdw_find_fde): Update caller. Do address sanity check here. + * cfi.h: Declare __libdw_fde_by_offset. + * cfi.c (dwarf_cfi_validate_fde): New function. + * libdw.h: Declare it. + * libdw.map (ELFUTILS_0.148): Add it. + * cie.c (intern_new_cie): Canonicalize DW_EH_PE_absptr FDE encoding to either DW_EH_PE_udata8 or DW_EH_PE_udata4. diff --git a/libdw/cfi.c b/libdw/cfi.c index aeb48e697..e49335ddc 100644 --- a/libdw/cfi.c +++ b/libdw/cfi.c @@ -504,3 +504,42 @@ __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde, } return result; } + +int +dwarf_cfi_validate_fde (cache, offset, start, end, signalp, encoding) + Dwarf_CFI *cache; + Dwarf_Off offset; + Dwarf_Addr *start; + Dwarf_Addr *end; + bool *signalp; + uint8_t *encoding; +{ + if (cache == NULL) + return -1; + + struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset); + if (unlikely (fde == NULL)) + return -1; + + Dwarf_Frame *fs; + int result = __libdw_frame_at_address (cache, fde, fde->end, &fs); + if (unlikely (result != DWARF_E_NOERROR)) + { + __libdw_seterrno (result); + return -1; + } + + result = fs->nregs + 1; + free (fs); + + if (start != NULL) + *start = fde->start; + if (end != NULL) + *end = fde->end; + if (signalp != NULL) + *signalp = fde->cie->signal_frame; + if (encoding != NULL) + *encoding = fde->cie->fde_encoding; + + return result; +} diff --git a/libdw/cfi.h b/libdw/cfi.h index 6aaa9c8a7..ef9cd7e1b 100644 --- a/libdw/cfi.h +++ b/libdw/cfi.h @@ -227,6 +227,11 @@ extern struct dwarf_fde *__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address) __nonnull_attribute__ (1) internal_function; +/* Look for an FDE by its offset in the section. */ +extern struct dwarf_fde *__libdw_fde_by_offset (Dwarf_CFI *cache, + Dwarf_Off offset) + __nonnull_attribute__ (1) internal_function; + /* Process the FDE that contains the given PC address, to yield the frame state when stopped there. The return value is a DWARF_E_* error code. */ diff --git a/libdw/fde.c b/libdw/fde.c index c826114c2..5685252ba 100644 --- a/libdw/fde.c +++ b/libdw/fde.c @@ -1,5 +1,5 @@ /* FDE reading. - 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 @@ -139,8 +139,9 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry) return fde; } -static struct dwarf_fde * -fde_by_offset (Dwarf_CFI *cache, Dwarf_Addr address, Dwarf_Off offset) +struct dwarf_fde * +internal_function +__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset) { Dwarf_CFI_Entry entry; Dwarf_Off next_offset; @@ -167,10 +168,6 @@ fde_by_offset (Dwarf_CFI *cache, Dwarf_Addr address, Dwarf_Off offset) if (cache->next_offset == offset) cache->next_offset = next_offset; - /* Sanity check the address range. */ - if (address < fde->start || address >= fde->end) - goto invalid; - return fde; } @@ -254,7 +251,15 @@ __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; - return fde_by_offset (cache, address, offset); + struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset); + if (unlikely (fde != NULL) + /* Sanity check the address range. */ + && unlikely (address < fde->start || address >= fde->end)) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + return fde; } /* It's not there. Read more CFI entries until we find it. */ diff --git a/libdw/libdw.h b/libdw/libdw.h index 252361fc0..75998e1a0 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -799,6 +799,20 @@ extern int dwarf_frame_register (Dwarf_Frame *frame, int regno, Dwarf_Op **ops, size_t *nops) __nonnull_attribute__ (3, 4, 5); +/* Look up the FDE described at OFFSET bytes into the CFI section, + and validate it by decoding the FDE fully. Returns -1 for errors. + On success, returns the maximum DWARF register number that this + FDE describes and fills *START and *END with the PC address range + this FDE covers, *SIGNALP with whether this is a signal frame, and + *ENCODING with the pointer encoding used in this FDE. This is not + necessarily the exact encoding byte given in the augmentation string; + it will describe the exact address size used (DW_EH_PE_udata4 or + DW_EH_PE_udata8) if a DW_EH_PE_absptr encoding is being used. */ +extern int dwarf_cfi_validate_fde (Dwarf_CFI *cache, + Dwarf_Off offset, + Dwarf_Addr *start, Dwarf_Addr *end, + bool *signalp, uint8_t *encoding); + /* Return error code of last failing function call. This value is kept separately for each thread. */ diff --git a/libdw/libdw.map b/libdw/libdw.map index 1a9afb135..89897acdc 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -237,3 +237,8 @@ ELFUTILS_0.146 { global: dwfl_core_file_report; } ELFUTILS_0.144; + +ELFUTILS_0.148 { + global: + dwarf_cfi_validate_fde; +} ELFUTILS_0.146; -- 2.47.2