123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- /* libunwind - a platform-independent unwind library
- Copyright (c) 2001-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 <assert.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include "unwind_i.h"
- #ifdef HAVE_IA64INTRIN_H
- # include <ia64intrin.h>
- #endif
- extern unw_addr_space_t _ULia64_local_addr_space;
- struct ia64_table_entry
- {
- uint64_t start_offset;
- uint64_t end_offset;
- uint64_t info_offset;
- };
- #ifdef UNW_LOCAL_ONLY
- static inline int
- is_local_addr_space (unw_addr_space_t as)
- {
- return 1;
- }
- static inline int
- read_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, void *arg)
- {
- *valp = *(unw_word_t *) addr;
- return 0;
- }
- #else /* !UNW_LOCAL_ONLY */
- static inline int
- is_local_addr_space (unw_addr_space_t as)
- {
- return as == unw_local_addr_space;
- }
- static inline int
- read_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, void *arg)
- {
- unw_accessors_t *a = unw_get_accessors (as);
- return (*a->access_mem) (as, addr, valp, 0, arg);
- }
- /* Helper macro for reading an ia64_table_entry from remote memory. */
- #define remote_read(addr, member) \
- (*a->access_mem) (as, (addr) + offsetof (struct ia64_table_entry, \
- member), &member, 0, arg)
- /* Lookup an unwind-table entry in remote memory. Returns 1 if an
- entry is found, 0 if no entry is found, negative if an error
- occurred reading remote memory. */
- static int
- remote_lookup (unw_addr_space_t as,
- unw_word_t table, size_t table_size, unw_word_t rel_ip,
- struct ia64_table_entry *e, void *arg)
- {
- unw_word_t e_addr = 0, start_offset, end_offset, info_offset;
- unw_accessors_t *a = unw_get_accessors (as);
- unsigned long lo, hi, mid;
- int ret;
- /* do a binary search for right entry: */
- for (lo = 0, hi = table_size / sizeof (struct ia64_table_entry); lo < hi;)
- {
- mid = (lo + hi) / 2;
- e_addr = table + mid * sizeof (struct ia64_table_entry);
- if ((ret = remote_read (e_addr, start_offset)) < 0)
- return ret;
- if (rel_ip < start_offset)
- hi = mid;
- else
- {
- if ((ret = remote_read (e_addr, end_offset)) < 0)
- return ret;
- if (rel_ip >= end_offset)
- lo = mid + 1;
- else
- break;
- }
- }
- if (rel_ip < start_offset || rel_ip >= end_offset)
- return 0;
- e->start_offset = start_offset;
- e->end_offset = end_offset;
- if ((ret = remote_read (e_addr, info_offset)) < 0)
- return ret;
- e->info_offset = info_offset;
- return 1;
- }
- HIDDEN void
- tdep_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg)
- {
- if (!pi->unwind_info)
- return;
- if (is_local_addr_space (as))
- {
- free (pi->unwind_info);
- pi->unwind_info = NULL;
- }
- }
- PROTECTED unw_word_t
- _Uia64_find_dyn_list (unw_addr_space_t as, unw_dyn_info_t *di, void *arg)
- {
- unw_word_t hdr_addr, info_addr, hdr, directives, pers, cookie, off;
- unw_word_t start_offset, end_offset, info_offset, segbase;
- struct ia64_table_entry *e;
- size_t table_size;
- unw_word_t gp = di->gp;
- int ret;
- switch (di->format)
- {
- case UNW_INFO_FORMAT_DYNAMIC:
- default:
- return 0;
- case UNW_INFO_FORMAT_TABLE:
- e = (struct ia64_table_entry *) di->u.ti.table_data;
- table_size = di->u.ti.table_len * sizeof (di->u.ti.table_data[0]);
- segbase = di->u.ti.segbase;
- if (table_size < sizeof (struct ia64_table_entry))
- return 0;
- start_offset = e[0].start_offset;
- end_offset = e[0].end_offset;
- info_offset = e[0].info_offset;
- break;
- case UNW_INFO_FORMAT_REMOTE_TABLE:
- {
- unw_accessors_t *a = unw_get_accessors (as);
- unw_word_t e_addr = di->u.rti.table_data;
- table_size = di->u.rti.table_len * sizeof (unw_word_t);
- segbase = di->u.rti.segbase;
- if (table_size < sizeof (struct ia64_table_entry))
- return 0;
- if ( (ret = remote_read (e_addr, start_offset) < 0)
- || (ret = remote_read (e_addr, end_offset) < 0)
- || (ret = remote_read (e_addr, info_offset) < 0))
- return ret;
- }
- break;
- }
- if (start_offset != end_offset)
- /* dyn-list entry cover a zero-length "procedure" and should be
- first entry (note: technically a binary could contain code
- below the segment base, but this doesn't happen for normal
- binaries and certainly doesn't happen when libunwind is a
- separate shared object. For weird cases, the application may
- have to provide its own (slower) version of this routine. */
- return 0;
- hdr_addr = info_offset + segbase;
- info_addr = hdr_addr + 8;
- /* read the header word: */
- if ((ret = read_mem (as, hdr_addr, &hdr, arg)) < 0)
- return ret;
- if (IA64_UNW_VER (hdr) != 1
- || IA64_UNW_FLAG_EHANDLER (hdr) || IA64_UNW_FLAG_UHANDLER (hdr))
- /* dyn-list entry must be version 1 and doesn't have ehandler
- or uhandler */
- return 0;
- if (IA64_UNW_LENGTH (hdr) != 1)
- /* dyn-list entry must consist of a single word of NOP directives */
- return 0;
- if ( ((ret = read_mem (as, info_addr, &directives, arg)) < 0)
- || ((ret = read_mem (as, info_addr + 0x08, &pers, arg)) < 0)
- || ((ret = read_mem (as, info_addr + 0x10, &cookie, arg)) < 0)
- || ((ret = read_mem (as, info_addr + 0x18, &off, arg)) < 0))
- return 0;
- if (directives != 0 || pers != 0
- || (!as->big_endian && cookie != 0x7473696c2d6e7964ULL)
- || ( as->big_endian && cookie != 0x64796e2d6c697374ULL))
- return 0;
- /* OK, we ran the gauntlet and found it: */
- return off + gp;
- }
- #endif /* !UNW_LOCAL_ONLY */
- static inline const struct ia64_table_entry *
- lookup (struct ia64_table_entry *table, size_t table_size, unw_word_t rel_ip)
- {
- const struct ia64_table_entry *e = 0;
- unsigned long lo, hi, mid;
- /* do a binary search for right entry: */
- for (lo = 0, hi = table_size / sizeof (struct ia64_table_entry); lo < hi;)
- {
- mid = (lo + hi) / 2;
- e = table + mid;
- if (rel_ip < e->start_offset)
- hi = mid;
- else if (rel_ip >= e->end_offset)
- lo = mid + 1;
- else
- break;
- }
- if (rel_ip < e->start_offset || rel_ip >= e->end_offset)
- return NULL;
- return e;
- }
- PROTECTED int
- unw_search_ia64_unwind_table (unw_addr_space_t as, unw_word_t ip,
- unw_dyn_info_t *di, unw_proc_info_t *pi,
- int need_unwind_info, void *arg)
- {
- unw_word_t addr, hdr_addr, info_addr, info_end_addr, hdr, *wp;
- const struct ia64_table_entry *e = NULL;
- unw_word_t handler_offset, segbase = 0;
- struct ia64_table_entry ent;
- int ret, is_local;
- assert ((di->format == UNW_INFO_FORMAT_TABLE
- || di->format == UNW_INFO_FORMAT_REMOTE_TABLE)
- && (ip >= di->start_ip && ip < di->end_ip));
- pi->flags = 0;
- pi->unwind_info = 0;
- pi->handler = 0;
- if (likely (di->format == UNW_INFO_FORMAT_TABLE))
- {
- segbase = di->u.ti.segbase;
- e = lookup ((struct ia64_table_entry *) di->u.ti.table_data,
- di->u.ti.table_len * sizeof (unw_word_t),
- ip - segbase);
- }
- #ifndef UNW_LOCAL_ONLY
- else
- {
- segbase = di->u.rti.segbase;
- if ((ret = remote_lookup (as, di->u.rti.table_data,
- di->u.rti.table_len * sizeof (unw_word_t),
- ip - segbase, &ent, arg)) < 0)
- return ret;
- if (ret)
- e = &ent;
- }
- #endif
- if (!e)
- {
- /* IP is inside this table's range, but there is no explicit
- unwind info => use default conventions (i.e., this is NOT an
- error). */
- memset (pi, 0, sizeof (*pi));
- pi->start_ip = 0;
- pi->end_ip = 0;
- pi->gp = di->gp;
- pi->lsda = 0;
- return 0;
- }
- pi->start_ip = e->start_offset + segbase;
- pi->end_ip = e->end_offset + segbase;
- hdr_addr = e->info_offset + segbase;
- info_addr = hdr_addr + 8;
- /* Read the header word. Note: the actual unwind-info is always
- assumed to reside in memory, independent of whether di->format is
- UNW_INFO_FORMAT_TABLE or UNW_INFO_FORMAT_REMOTE_TABLE. */
- if ((ret = read_mem (as, hdr_addr, &hdr, arg)) < 0)
- return ret;
- if (IA64_UNW_VER (hdr) != 1)
- {
- Debug (1, "Unknown header version %ld (hdr word=0x%lx @ 0x%lx)\n",
- IA64_UNW_VER (hdr), (unsigned long) hdr,
- (unsigned long) hdr_addr);
- return -UNW_EBADVERSION;
- }
- info_end_addr = info_addr + 8 * IA64_UNW_LENGTH (hdr);
- is_local = is_local_addr_space (as);
- /* If we must have the unwind-info, return it. Also, if we are in
- the local address-space, return the unwind-info because it's so
- cheap to do so and it may come in handy later on. */
- if (need_unwind_info || is_local)
- {
- pi->unwind_info_size = 8 * IA64_UNW_LENGTH (hdr);
- if (is_local)
- pi->unwind_info = (void *) (uintptr_t) info_addr;
- else
- {
- /* Internalize unwind info. Note: since we're doing this
- only for non-local address spaces, there is no
- signal-safety issue and it is OK to use malloc()/free(). */
- pi->unwind_info = malloc (8 * IA64_UNW_LENGTH (hdr));
- if (!pi->unwind_info)
- return -UNW_ENOMEM;
- wp = (unw_word_t *) pi->unwind_info;
- for (addr = info_addr; addr < info_end_addr; addr += 8, ++wp)
- {
- if ((ret = read_mem (as, addr, wp, arg)) < 0)
- {
- free (pi->unwind_info);
- return ret;
- }
- }
- }
- }
- if (IA64_UNW_FLAG_EHANDLER (hdr) || IA64_UNW_FLAG_UHANDLER (hdr))
- {
- /* read the personality routine address (address is gp-relative): */
- if ((ret = read_mem (as, info_end_addr, &handler_offset, arg)) < 0)
- return ret;
- Debug (4, "handler ptr @ offset=%lx, gp=%lx\n", handler_offset, di->gp);
- if ((read_mem (as, handler_offset + di->gp, &pi->handler, arg)) < 0)
- return ret;
- }
- pi->lsda = info_end_addr + 8;
- pi->gp = di->gp;
- pi->format = di->format;
- return 0;
- }
- #ifndef UNW_REMOTE_ONLY
- # if defined(HAVE_DL_ITERATE_PHDR)
- # include <link.h>
- # include <stdlib.h>
- # if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \
- || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG))
- # error You need GLIBC 2.2.4 or later on IA-64 Linux
- # endif
- # if defined(HAVE_GETUNWIND)
- extern unsigned long getunwind (void *buf, size_t len);
- # else /* HAVE_GETUNWIND */
- # include <unistd.h>
- # include <sys/syscall.h>
- # ifndef __NR_getunwind
- # define __NR_getunwind 1215
- # endif
- static unsigned long
- getunwind (void *buf, size_t len)
- {
- return syscall (SYS_getunwind, buf, len);
- }
- # endif /* HAVE_GETUNWIND */
- static unw_dyn_info_t kernel_table;
- static int
- get_kernel_table (unw_dyn_info_t *di)
- {
- struct ia64_table_entry *ktab, *etab;
- size_t size;
- Debug (16, "getting kernel table");
- size = getunwind (NULL, 0);
- ktab = sos_alloc (size);
- if (!ktab)
- {
- Dprintf (__FILE__".%s: failed to allocate %zu bytes",
- __FUNCTION__, size);
- return -UNW_ENOMEM;
- }
- getunwind (ktab, size);
- /* Determine length of kernel's unwind table & relocate its entries. */
- for (etab = ktab; etab->start_offset; ++etab)
- etab->info_offset += (uint64_t) ktab;
- di->format = UNW_INFO_FORMAT_TABLE;
- di->gp = 0;
- di->start_ip = ktab[0].start_offset;
- di->end_ip = etab[-1].end_offset;
- di->u.ti.name_ptr = (unw_word_t) "<kernel>";
- di->u.ti.segbase = 0;
- di->u.ti.table_len = ((char *) etab - (char *) ktab) / sizeof (unw_word_t);
- di->u.ti.table_data = (unw_word_t *) ktab;
- Debug (16, "found table `%s': [%lx-%lx) segbase=%lx len=%lu\n",
- (char *) di->u.ti.name_ptr, di->start_ip, di->end_ip,
- di->u.ti.segbase, di->u.ti.table_len);
- return 0;
- }
- # ifndef UNW_LOCAL_ONLY
- /* This is exported for the benefit of libunwind-ptrace.a. */
- PROTECTED int
- _Uia64_get_kernel_table (unw_dyn_info_t *di)
- {
- int ret;
- if (!kernel_table.u.ti.table_data)
- if ((ret = get_kernel_table (&kernel_table)) < 0)
- return ret;
- memcpy (di, &kernel_table, sizeof (*di));
- return 0;
- }
- # endif /* !UNW_LOCAL_ONLY */
- static inline unsigned long
- current_gp (void)
- {
- # if defined(__GNUC__) && !defined(__INTEL_COMPILER)
- register unsigned long gp __asm__("gp");
- return gp;
- # elif HAVE_IA64INTRIN_H
- return __getReg (_IA64_REG_GP);
- # else
- # error Implement me.
- # endif
- }
- static int
- callback (struct dl_phdr_info *info, size_t size, void *ptr)
- {
- unw_dyn_info_t *di = ptr;
- const Elf64_Phdr *phdr, *p_unwind, *p_dynamic, *p_text;
- long n;
- Elf64_Addr load_base, segbase = 0;
- /* Make sure struct dl_phdr_info is at least as big as we need. */
- if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
- + sizeof (info->dlpi_phnum))
- return -1;
- Debug (16, "checking `%s' (load_base=%lx)\n",
- info->dlpi_name, info->dlpi_addr);
- phdr = info->dlpi_phdr;
- load_base = info->dlpi_addr;
- p_text = NULL;
- p_unwind = NULL;
- p_dynamic = NULL;
- /* See if PC falls into one of the loaded segments. Find the unwind
- segment at the same time. */
- for (n = info->dlpi_phnum; --n >= 0; phdr++)
- {
- if (phdr->p_type == PT_LOAD)
- {
- Elf64_Addr vaddr = phdr->p_vaddr + load_base;
- if (di->u.ti.segbase >= vaddr
- && di->u.ti.segbase < vaddr + phdr->p_memsz)
- p_text = phdr;
- }
- else if (phdr->p_type == PT_IA_64_UNWIND)
- p_unwind = phdr;
- else if (phdr->p_type == PT_DYNAMIC)
- p_dynamic = phdr;
- }
- if (!p_text || !p_unwind)
- return 0;
- if (likely (p_unwind->p_vaddr >= p_text->p_vaddr
- && p_unwind->p_vaddr < p_text->p_vaddr + p_text->p_memsz))
- /* normal case: unwind table is inside text segment */
- segbase = p_text->p_vaddr + load_base;
- else
- {
- /* Special case: unwind table is in some other segment; this
- happens for the Linux kernel's gate DSO, for example. */
- phdr = info->dlpi_phdr;
- for (n = info->dlpi_phnum; --n >= 0; phdr++)
- {
- if (phdr->p_type == PT_LOAD && p_unwind->p_vaddr >= phdr->p_vaddr
- && p_unwind->p_vaddr < phdr->p_vaddr + phdr->p_memsz)
- {
- segbase = phdr->p_vaddr + load_base;
- break;
- }
- }
- }
- if (p_dynamic)
- {
- /* For dynamicly linked executables and shared libraries,
- DT_PLTGOT is the gp value for that object. */
- Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base);
- for (; dyn->d_tag != DT_NULL; ++dyn)
- if (dyn->d_tag == DT_PLTGOT)
- {
- /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it. */
- di->gp = dyn->d_un.d_ptr;
- break;
- }
- }
- else
- /* Otherwise this is a static executable with no _DYNAMIC.
- The gp is constant program-wide. */
- di->gp = current_gp();
- di->format = UNW_INFO_FORMAT_TABLE;
- di->start_ip = p_text->p_vaddr + load_base;
- di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz;
- di->u.ti.name_ptr = (unw_word_t) info->dlpi_name;
- di->u.ti.table_data = (void *) (p_unwind->p_vaddr + load_base);
- di->u.ti.table_len = p_unwind->p_memsz / sizeof (unw_word_t);
- di->u.ti.segbase = segbase;
- Debug (16, "found table `%s': segbase=%lx, len=%lu, gp=%lx, "
- "table_data=%p\n", (char *) di->u.ti.name_ptr, di->u.ti.segbase,
- di->u.ti.table_len, di->gp, di->u.ti.table_data);
- return 1;
- }
- # ifdef HAVE_DL_PHDR_REMOVALS_COUNTER
- static inline int
- validate_cache (unw_addr_space_t as)
- {
- /* Note: we don't need to serialize here with respect to
- dl_iterate_phdr() because if somebody were to remove an object
- that is required to complete the unwind on whose behalf we're
- validating the cache here, we'd be hosed anyhow. What we're
- guarding against here is the case where library FOO gets mapped,
- unwind info for FOO gets cached, FOO gets unmapped, BAR gets
- mapped in the place where FOO was and then we unwind across a
- function in FOO. Since no thread can execute in BAR before FOO
- has been removed, we are guaranteed that
- dl_phdr_removals_counter() would have been incremented before we
- get here. */
- unsigned long long removals = dl_phdr_removals_counter ();
- if (removals == as->shared_object_removals)
- return 1;
- as->shared_object_removals = removals;
- unw_flush_cache (as, 0, 0);
- return -1;
- }
- # else /* !HAVE_DL_PHDR_REMOVALS_COUNTER */
- /* Check whether any phdrs have been removed since we last flushed the
- cache. If so we flush the cache and return -1, if not, we do
- nothing and return 1. */
- static int
- check_callback (struct dl_phdr_info *info, size_t size, void *ptr)
- {
- # ifdef HAVE_STRUCT_DL_PHDR_INFO_DLPI_SUBS
- unw_addr_space_t as = ptr;
- if (size <
- offsetof (struct dl_phdr_info, dlpi_subs) + sizeof (info->dlpi_subs))
- /* It would be safer to flush the cache here, but that would
- disable caching for older libc's which would be incompatible
- with the behavior of older versions of libunwind so we return 1
- instead and hope nobody runs into stale cache info... */
- return 1;
- if (info->dlpi_subs == as->shared_object_removals)
- return 1;
- as->shared_object_removals = info->dlpi_subs;
- unw_flush_cache (as, 0, 0);
- return -1; /* indicate that there were removals */
- # else
- return 1;
- # endif
- }
- static inline int
- validate_cache (unw_addr_space_t as)
- {
- intrmask_t saved_mask;
- int ret;
- SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
- ret = dl_iterate_phdr (check_callback, as);
- SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
- return ret;
- }
- # endif /* HAVE_DL_PHDR_REMOVALS_COUNTER */
- # elif defined(HAVE_DLMODINFO)
- /* Support for HP-UX-style dlmodinfo() */
- # include <dlfcn.h>
- static inline int
- validate_cache (unw_addr_space_t as)
- {
- return 1;
- }
- # endif /* !HAVE_DLMODINFO */
- HIDDEN int
- tdep_find_proc_info (unw_addr_space_t as, unw_word_t ip,
- unw_proc_info_t *pi, int need_unwind_info, void *arg)
- {
- # if defined(HAVE_DL_ITERATE_PHDR)
- unw_dyn_info_t di, *dip = &di;
- intrmask_t saved_mask;
- int ret;
- di.u.ti.segbase = ip; /* this is cheap... */
- SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
- ret = dl_iterate_phdr (callback, &di);
- SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
- if (ret <= 0)
- {
- if (!kernel_table.u.ti.table_data)
- {
- if ((ret = get_kernel_table (&kernel_table)) < 0)
- return ret;
- }
- if (ip < kernel_table.start_ip || ip >= kernel_table.end_ip)
- return -UNW_ENOINFO;
- dip = &kernel_table;
- }
- # elif defined(HAVE_DLMODINFO)
- # define UNWIND_TBL_32BIT 0x8000000000000000
- struct load_module_desc lmd;
- unw_dyn_info_t di, *dip = &di;
- struct unwind_header
- {
- uint64_t header_version;
- uint64_t start_offset;
- uint64_t end_offset;
- }
- *uhdr;
- if (!dlmodinfo (ip, &lmd, sizeof (lmd), NULL, 0, 0))
- return -UNW_ENOINFO;
- di.format = UNW_INFO_FORMAT_TABLE;
- di.start_ip = lmd.text_base;
- di.end_ip = lmd.text_base + lmd.text_size;
- di.gp = lmd.linkage_ptr;
- di.u.ti.name_ptr = 0; /* no obvious table-name available */
- di.u.ti.segbase = lmd.text_base;
- uhdr = (struct unwind_header *) lmd.unwind_base;
- if ((uhdr->header_version & ~UNWIND_TBL_32BIT) != 1
- && (uhdr->header_version & ~UNWIND_TBL_32BIT) != 2)
- {
- Debug (1, "encountered unknown unwind header version %ld\n",
- (long) (uhdr->header_version & ~UNWIND_TBL_32BIT));
- return -UNW_EBADVERSION;
- }
- if (uhdr->header_version & UNWIND_TBL_32BIT)
- {
- Debug (1, "32-bit unwind tables are not supported yet\n");
- return -UNW_EINVAL;
- }
- di.u.ti.table_data = (unw_word_t *) (di.u.ti.segbase + uhdr->start_offset);
- di.u.ti.table_len = ((uhdr->end_offset - uhdr->start_offset)
- / sizeof (unw_word_t));
- Debug (16, "found table `%s': segbase=%lx, len=%lu, gp=%lx, "
- "table_data=%p\n", (char *) di.u.ti.name_ptr, di.u.ti.segbase,
- di.u.ti.table_len, di.gp, di.u.ti.table_data);
- # endif
- /* now search the table: */
- return tdep_search_unwind_table (as, ip, dip, pi, need_unwind_info, arg);
- }
- /* Returns 1 if the cache is up-to-date or -1 if the cache contained
- stale data and had to be flushed. */
- HIDDEN int
- ia64_local_validate_cache (unw_addr_space_t as, void *arg)
- {
- return validate_cache (as);
- }
- #endif /* !UNW_REMOTE_ONLY */
|