diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c index e69de29b..a095c3e4 100644 --- a/src/dwarf/Gfde.c +++ b/src/dwarf/Gfde.c @@ -0,0 +1,287 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include + +#include "dwarf_i.h" +#include "tdep.h" + +/* Note: we don't need to keep track of more than the first four + characters of the augmentation string, because we (a) ignore any + augmentation string contents once we find an unrecognized character + and (b) those characters that we do recognize, can't be + repeated. */ +static inline int +parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_proc_info_t *pi, unw_dyn_dwarf_fde_info_t *dfi, + int *lsda_encodingp, void *arg) +{ + uint8_t version, ch, augstr[5], fde_encoding, handler_encoding; + unw_word_t len, cie_end_addr, aug_size, handler = 0; + uint32_t u32val; + uint64_t u64val; + size_t i; + int ret; +# define STR2(x) #x +# define STR(x) STR2(x) + + fde_encoding = DW_EH_PE_omit; + *lsda_encodingp = DW_EH_PE_omit; + dfi->flags = 0; + + if ((ret = dwarf_readu32 (as, a, addr, &u32val, arg)) < 0) + return ret; + + if (u32val != 0xffffffff) + { + /* the CIE is in the 32-bit DWARF format */ + uint32_t cie_id; + + len = u32val; + cie_end_addr = *addr + len; + if ((ret = dwarf_readu32 (as, a, addr, &cie_id, arg)) < 0) + return ret; + /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */ + if (cie_id != 0) + { + Debug (1, "Unexpected CIE id %x\n", cie_id); + return -UNW_EINVAL; + } + } + else + { + /* the CIE is in the 64-bit DWARF format */ + uint64_t cie_id; + + if ((ret = dwarf_readu64 (as, a, addr, &u64val, arg)) < 0) + return ret; + len = u64val; + cie_end_addr = *addr + len; + if ((ret = dwarf_readu64 (as, a, addr, &cie_id, arg)) < 0) + return ret; + /* DWARF says CIE id should be 0xffffffffffffffff, but in + .eh_frame, it's 0 */ + if (cie_id != 0) + { + Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id); + return -UNW_EINVAL; + } + } + dfi->cie_instr_end = cie_end_addr; + + if ((ret = dwarf_readu8 (as, a, addr, &version, arg)) < 0) + return ret; + + if (version != 1 && version != DWARF_CIE_VERSION) + { + Debug (1, "Got CIE version %u, expected version 1 or " + STR (DWARF_CIE_VERSION) "\n", version); + return -UNW_EBADVERSION; + } + + /* read and parse the augmentation string: */ + memset (augstr, 0, sizeof (augstr)); + for (i = 0;;) + { + if ((ret = dwarf_readu8 (as, a, addr, &ch, arg)) < 0) + return ret; + + if (!ch) + break; /* end of augmentation string */ + + if (i < sizeof (augstr) - 1) + augstr[i++] = ch; + } + + if ((ret = dwarf_read_uleb128 (as, a, addr, &dfi->code_align, arg)) < 0 + || (ret = dwarf_read_sleb128 (as, a, addr, &dfi->data_align, arg)) < 0) + return ret; + + /* Read the return-address column either as a u8 or as a uleb128. */ + if (version == 1) + { + if ((ret = dwarf_readu8 (as, a, addr, &ch, arg)) < 0) + return ret; + dfi->ret_addr_column = ch; + } + else if ((ret = dwarf_read_uleb128 (as, a, addr, &dfi->ret_addr_column, arg)) + < 0) + return ret; + + if (augstr[0] == 'z') + { + dfi->flags |= UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE; + if ((ret = dwarf_read_uleb128 (as, a, addr, &aug_size, arg)) + < 0) + return ret; + } + + for (i = 1; i < sizeof (augstr) && augstr[i]; ++i) + switch (augstr[i]) + { + case 'L': + /* read the LSDA pointer-encoding format. */ + if ((ret = dwarf_readu8 (as, a, addr, &ch, arg)) < 0) + return ret; + *lsda_encodingp = ch; + break; + + case 'R': + /* read the FDE pointer-encoding format. */ + if ((ret = dwarf_readu8 (as, a, addr, &fde_encoding, arg)) < 0) + return ret; + break; + + case 'P': + /* read the personality-routine pointer-encoding format. */ + if ((ret = dwarf_readu8 (as, a, addr, &handler_encoding, arg)) < 0) + return ret; + if ((ret = dwarf_read_encoded_pointer (as, a, addr, handler_encoding, + pi, &handler, arg)) < 0) + return ret; + break; + + default: + if (dfi->flags & UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE) + /* If we have the size of the augmentation body, we can skip + over the parts that we don't understand, so we're OK. */ + return 0; + else + { + Debug (1, "Unexpected augmentation string `%s'\n", augstr); + return -UNW_EINVAL; + } + } + dfi->flags |= fde_encoding & UNW_DYN_DFI_FLAG_FDE_PE_MASK; + pi->handler = handler; + Debug (16, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx\n", + augstr, (long) handler); + dfi->cie_instr_start = *addr; + return 0; +} + +HIDDEN int +dwarf_parse_fde (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_proc_info_t *pi, unw_dyn_dwarf_fde_info_t *dfi, void *arg) +{ + unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0; + int ret, fde_encoding, ip_range_encoding, lsda_encoding; + unw_word_t start_ip, ip_range, aug_size; + uint64_t u64val; + uint32_t u32val; + + Debug (12, "FDE @ 0x%lx\n", (long) *addr); + + /* Parse enough of the FDE to get the procedure info (LSDA and handler). */ + + if ((ret = dwarf_readu32 (as, a, addr, &u32val, arg)) < 0) + return ret; + + if (u32val != 0xffffffff) + { + uint32_t cie_offset; + + /* the FDE is in the 32-bit DWARF format */ + + fde_end_addr = *addr + u32val; + cie_offset_addr = *addr; + + if ((ret = dwarf_reads32 (as, a, addr, &cie_offset, arg)) < 0) + return ret; + + /* DWARF says that the CIE_pointer in the FDE is a + .debug_frame-relative offset, but the GCC-generated .eh_frame + sections instead store a "pcrelative" offset, which is just + as fine as it's self-contained. */ + cie_addr = cie_offset_addr - cie_offset; + } + else + { + uint64_t cie_offset; + + /* the FDE is in the 64-bit DWARF format */ + + if ((ret = dwarf_readu64 (as, a, addr, &u64val, arg)) < 0) + return ret; + + fde_end_addr = *addr + u64val; + cie_offset_addr = *addr; + + if ((ret = dwarf_reads64 (as, a, addr, &cie_offset, arg)) < 0) + return ret; + + /* DWARF says that the CIE_pointer in the FDE is a + .debug_frame-relative offset, but the GCC-generated .eh_frame + sections instead store a "pcrelative" offset, which is just + as fine as it's self-contained. */ + cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); + } + pi->extra.dwarf_info.fde_instr_end = fde_end_addr; + + if ((ret = parse_cie (as, a, &cie_addr, pi, &pi->extra.dwarf_info, + &lsda_encoding, arg)) < 0) + return ret; + + fde_encoding = pi->extra.dwarf_info.flags & UNW_DYN_DFI_FLAG_FDE_PE_MASK; + /* IP-range has same encoding as FDE pointers, except that it's + always an absolute value: */ + ip_range_encoding = fde_encoding & DW_EH_PE_FORMAT_MASK; + + if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding, + pi, &start_ip, arg)) < 0 + || (ret = dwarf_read_encoded_pointer (as, a, addr, ip_range_encoding, + pi, &ip_range, arg)) < 0) + return ret; + pi->start_ip = start_ip; + pi->end_ip = start_ip + ip_range; + + if (pi->extra.dwarf_info.flags & UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE) + { + if ((ret = dwarf_read_uleb128 (as, a, addr, &aug_size, arg)) + < 0) + return ret; + aug_end_addr = *addr + aug_size; + } + + if ((ret = dwarf_read_encoded_pointer (as, a, addr, lsda_encoding, + pi, &pi->lsda, arg)) < 0) + return ret; + + if (pi->extra.dwarf_info.flags & UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE) + pi->extra.dwarf_info.fde_instr_start = aug_end_addr; + else + pi->extra.dwarf_info.fde_instr_start = *addr; + + /* Always set the unwind info, whether or not need_unwind_info is + set. We had to do all the work anyhow, so there is no point in + not doing so. */ + pi->format = UNW_INFO_FORMAT_DWARF_FDE; + pi->unwind_info_size = sizeof (pi->extra.dwarf_info) / sizeof (unw_word_t); + pi->unwind_info = &pi->extra.dwarf_info; + + Debug (16, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n", + (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda); + return 0; +}