123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /* libunwind - a platform-independent unwind library
- Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
- Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
- 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 "dwarf_i.h"
- static inline int
- is_cie_id (unw_word_t val, int is_debug_frame)
- {
- /* The CIE ID is normally 0xffffffff (for 32-bit ELF) or
- 0xffffffffffffffff (for 64-bit ELF). However, .eh_frame
- uses 0. */
- if (is_debug_frame)
- return (val == - (uint32_t) 1 || val == - (uint64_t) 1);
- else
- return (val == 0);
- }
- /* 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,
- const unw_proc_info_t *pi, struct dwarf_cie_info *dci,
- unw_word_t base, void *arg)
- {
- uint8_t version, ch, augstr[5], fde_encoding, handler_encoding;
- unw_word_t len, cie_end_addr, aug_size;
- uint32_t u32val;
- uint64_t u64val;
- size_t i;
- int ret;
- # define STR2(x) #x
- # define STR(x) STR2(x)
- /* Pick appropriate default for FDE-encoding. DWARF spec says
- start-IP (initial_location) and the code-size (address_range) are
- "address-unit sized constants". The `R' augmentation can be used
- to override this, but by default, we pick an address-sized unit
- for fde_encoding. */
- switch (dwarf_addr_size (as))
- {
- case 4: fde_encoding = DW_EH_PE_udata4; break;
- case 8: fde_encoding = DW_EH_PE_udata8; break;
- default: fde_encoding = DW_EH_PE_omit; break;
- }
- dci->lsda_encoding = DW_EH_PE_omit;
- dci->handler = 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;
- /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
- const uint32_t expected_id = (base) ? 0xffffffff : 0;
- len = u32val;
- cie_end_addr = addr + len;
- if ((ret = dwarf_readu32 (as, a, &addr, &cie_id, arg)) < 0)
- return ret;
- if (cie_id != expected_id)
- {
- 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;
- /* DWARF says CIE id should be 0xffffffffffffffff, but in
- .eh_frame, it's 0 */
- const uint64_t expected_id = (base) ? 0xffffffffffffffffull : 0;
- 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;
- if (cie_id != expected_id)
- {
- Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id);
- return -UNW_EINVAL;
- }
- }
- dci->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, &dci->code_align, arg)) < 0
- || (ret = dwarf_read_sleb128 (as, a, &addr, &dci->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;
- dci->ret_addr_column = ch;
- }
- else if ((ret = dwarf_read_uleb128 (as, a, &addr, &dci->ret_addr_column,
- arg)) < 0)
- return ret;
- i = 0;
- if (augstr[0] == 'z')
- {
- dci->sized_augmentation = 1;
- if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
- return ret;
- i++;
- }
- for (; 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;
- dci->lsda_encoding = 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, &dci->handler, arg)) < 0)
- return ret;
- break;
- case 'S':
- /* This is a signal frame. */
- dci->signal_frame = 1;
- /* Temporarily set it to one so dwarf_parse_fde() knows that
- it should fetch the actual ABI/TAG pair from the FDE. */
- dci->have_abi_marker = 1;
- break;
- default:
- Debug (1, "Unexpected augmentation string `%s'\n", augstr);
- if (dci->sized_augmentation)
- /* If we have the size of the augmentation body, we can skip
- over the parts that we don't understand, so we're OK. */
- goto done;
- else
- return -UNW_EINVAL;
- }
- done:
- dci->fde_encoding = fde_encoding;
- dci->cie_instr_start = addr;
- Debug (15, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx\n",
- augstr, (long) dci->handler);
- return 0;
- }
- /* Extract proc-info from the FDE starting at adress ADDR.
-
- Pass BASE as zero for eh_frame behaviour, or a pointer to
- debug_frame base for debug_frame behaviour. */
- HIDDEN int
- dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
- unw_word_t *addrp, unw_proc_info_t *pi,
- int need_unwind_info, unw_word_t base,
- void *arg)
- {
- unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
- unw_word_t start_ip, ip_range, aug_size, addr = *addrp;
- int ret, ip_range_encoding;
- struct dwarf_cie_info dci;
- uint64_t u64val;
- uint32_t u32val;
- Debug (12, "FDE @ 0x%lx\n", (long) addr);
- memset (&dci, 0, sizeof (dci));
- if ((ret = dwarf_readu32 (as, a, &addr, &u32val, arg)) < 0)
- return ret;
- if (u32val != 0xffffffff)
- {
- int32_t cie_offset;
- /* In some configurations, an FDE with a 0 length indicates the
- end of the FDE-table. */
- if (u32val == 0)
- return -UNW_ENOINFO;
- /* the FDE is in the 32-bit DWARF format */
- *addrp = fde_end_addr = addr + u32val;
- cie_offset_addr = addr;
- if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
- return ret;
- if (is_cie_id (cie_offset, base != 0))
- /* ignore CIEs (happens during linear searches) */
- return 0;
- if (base != 0)
- cie_addr = base + cie_offset;
- else
- /* 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
- {
- int64_t cie_offset;
- /* the FDE is in the 64-bit DWARF format */
- if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
- return ret;
- *addrp = fde_end_addr = addr + u64val;
- cie_offset_addr = addr;
- if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
- return ret;
- if (is_cie_id (cie_offset, base != 0))
- /* ignore CIEs (happens during linear searches) */
- return 0;
- if (base != 0)
- cie_addr = base + cie_offset;
- else
- /* 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);
- }
- Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);
- if ((ret = parse_cie (as, a, cie_addr, pi, &dci, base, arg)) < 0)
- return ret;
- /* IP-range has same encoding as FDE pointers, except that it's
- always an absolute value: */
- ip_range_encoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK;
- if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.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;
- pi->handler = dci.handler;
- if (dci.sized_augmentation)
- {
- 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, dci.lsda_encoding,
- pi, &pi->lsda, arg)) < 0)
- return ret;
- Debug (15, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n",
- (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda);
- if (need_unwind_info)
- {
- pi->format = UNW_INFO_FORMAT_TABLE;
- pi->unwind_info_size = sizeof (dci);
- pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool);
- if (!pi->unwind_info)
- return -UNW_ENOMEM;
- if (dci.have_abi_marker)
- {
- if ((ret = dwarf_readu16 (as, a, &addr, &dci.abi, arg)) < 0
- || (ret = dwarf_readu16 (as, a, &addr, &dci.tag, arg)) < 0)
- return ret;
- Debug (13, "Found ABI marker = (abi=%u, tag=%u)\n",
- dci.abi, dci.tag);
- }
- if (dci.sized_augmentation)
- dci.fde_instr_start = aug_end_addr;
- else
- dci.fde_instr_start = addr;
- dci.fde_instr_end = fde_end_addr;
- memcpy (pi->unwind_info, &dci, sizeof (dci));
- }
- return 0;
- }
|