summaryrefslogtreecommitdiff
path: root/src/pal/src/libunwind/src/dwarf/Gfde.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src/libunwind/src/dwarf/Gfde.c')
-rw-r--r--src/pal/src/libunwind/src/dwarf/Gfde.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/src/pal/src/libunwind/src/dwarf/Gfde.c b/src/pal/src/libunwind/src/dwarf/Gfde.c
new file mode 100644
index 0000000000..9250b895ea
--- /dev/null
+++ b/src/pal/src/libunwind/src/dwarf/Gfde.c
@@ -0,0 +1,359 @@
+/* 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,
+ int is_debug_frame, 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 = (is_debug_frame) ? 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 = (is_debug_frame) ? 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;
+
+ /* GCC emits version 1??? */
+ if (version != 1 && (version < DWARF_CIE_VERSION || version > DWARF_CIE_VERSION_MAX))
+ {
+ Debug (1, "Got CIE version %u, expected version 1 or between "
+ STR (DWARF_CIE_VERSION) " and " STR (DWARF_CIE_VERSION_MAX) "\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,
+ unw_word_t base,
+ int need_unwind_info, int is_debug_frame,
+ 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 = 0;
+
+ /* 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, is_debug_frame))
+ /* ignore CIEs (happens during linear searches) */
+ return 0;
+
+ if (is_debug_frame)
+ 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 = 0;
+
+ /* 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, is_debug_frame))
+ /* ignore CIEs (happens during linear searches) */
+ return 0;
+
+ if (is_debug_frame)
+ 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, is_debug_frame, 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;
+}