1/*
2 * Copyright (c) 2002 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/types.h>
30
31#include <assert.h>
32#include <dlfcn.h>
33#include <stdlib.h>
34
35#include <machine/elf.h>
36
37#ifndef PT_IA_64_UNWIND
38#define PT_IA_64_UNWIND         0x70000001
39#endif
40
41#define	SANITY	0
42
43struct ia64_unwind_entry
44{
45	Elf64_Addr start;
46	Elf64_Addr end;
47	Elf64_Addr descr;
48};
49
50struct ia64_unwind_entry *
51_Unwind_FindTableEntry(const void *pc, unsigned long *pseg, unsigned long *pgp)
52{
53	Dl_info info;
54	Elf_Dyn *dyn;
55	Elf_Ehdr *ehdr;
56	Elf_Phdr *phdr;
57	char *p, *p_top;
58	struct ia64_unwind_entry *unw, *res;
59	register unsigned long gp __asm__("gp");	/* XXX assumes gcc */
60	unsigned long reloc, vaddr;
61	size_t l, m, r;
62
63	if (!dladdr(pc, &info))
64		return NULL;
65
66	ehdr = (Elf_Ehdr*)info.dli_fbase;
67
68#if SANITY
69	assert(IS_ELF(*ehdr));
70	assert(ehdr->e_ident[EI_CLASS] == ELFCLASS64);
71	assert(ehdr->e_ident[EI_DATA] == ELFDATA2LSB);
72	assert(ehdr->e_machine == EM_IA_64);
73#endif
74
75	reloc = (ehdr->e_type == ET_DYN) ? (uintptr_t)info.dli_fbase : 0;
76	*pgp = gp;
77	*pseg = 0UL;
78	res = NULL;
79
80	p = (char*)info.dli_fbase + ehdr->e_phoff;
81	p_top = p + ehdr->e_phnum * ehdr->e_phentsize;
82	while (p < p_top) {
83		phdr = (Elf_Phdr*)p;
84		vaddr = phdr->p_vaddr + reloc;
85
86		switch (phdr->p_type) {
87		case PT_DYNAMIC:
88			dyn = (Elf_Dyn*)vaddr;
89			while (dyn->d_tag != DT_NULL) {
90				if (dyn->d_tag == DT_PLTGOT) {
91					*pgp = dyn->d_un.d_ptr + reloc;
92					break;
93				}
94				dyn++;
95			}
96			break;
97		case PT_LOAD:
98			if (pc >= (void*)vaddr &&
99			    pc < (void*)(vaddr + phdr->p_memsz))
100				*pseg = vaddr;
101			break;
102		case PT_IA_64_UNWIND:
103#if SANITY
104			assert(*pseg != 0UL);
105			assert(res == NULL);
106#endif
107			unw = (struct ia64_unwind_entry*)vaddr;
108			l = 0;
109			r = phdr->p_memsz / sizeof(struct ia64_unwind_entry);
110			while (l < r) {
111				m = (l + r) >> 1;
112				res = unw + m;
113				if (pc < (void*)(res->start + *pseg))
114					r = m;
115				else if (pc >= (void*)(res->end + *pseg))
116					l = m + 1;
117				else
118					break;	/* found */
119			}
120			if (l >= r)
121				res = NULL;
122			break;
123		}
124
125		p += ehdr->e_phentsize;
126	}
127
128	return res;
129}
130