00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #ifndef _GNU_SOURCE
00032 #define _GNU_SOURCE
00033 #endif
00034 #include "config.h"
00035 #include <stddef.h>
00036 #include <stdlib.h>
00037 #include <link.h>
00038 #include "unwind-ia64.h"
00039
00040 #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \
00041 || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG))
00042 # error You need GLIBC 2.2.4 or later on IA-64 Linux
00043 #endif
00044
00045 struct unw_ia64_callback_data
00046 {
00047 Elf64_Addr pc;
00048 unsigned long *segment_base;
00049 unsigned long *gp;
00050 struct unw_table_entry *ret;
00051 };
00052
00053 static int
00054 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
00055 {
00056 struct unw_ia64_callback_data *data = (struct unw_ia64_callback_data *) ptr;
00057 const Elf64_Phdr *phdr, *p_unwind, *p_dynamic;
00058 long n, match;
00059 Elf64_Addr load_base, seg_base;
00060 struct unw_table_entry *f_base, *f;
00061 size_t lo, hi;
00062
00063
00064 if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
00065 + sizeof (info->dlpi_phnum))
00066 return -1;
00067
00068 match = 0;
00069 phdr = info->dlpi_phdr;
00070 load_base = info->dlpi_addr;
00071 p_unwind = NULL;
00072 p_dynamic = NULL;
00073 seg_base = ~(Elf64_Addr) 0;
00074
00075
00076
00077 for (n = info->dlpi_phnum; --n >= 0; phdr++)
00078 {
00079 if (phdr->p_type == PT_LOAD)
00080 {
00081 Elf64_Addr vaddr = phdr->p_vaddr + load_base;
00082 if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
00083 match = 1;
00084 if (vaddr < seg_base)
00085 seg_base = vaddr;
00086 }
00087 else if (phdr->p_type == PT_IA_64_UNWIND)
00088 p_unwind = phdr;
00089 else if (phdr->p_type == PT_DYNAMIC)
00090 p_dynamic = phdr;
00091 }
00092 if (!match || !p_unwind)
00093 return 0;
00094
00095
00096
00097 f_base = (struct unw_table_entry *) (p_unwind->p_vaddr + load_base);
00098 lo = 0;
00099 hi = p_unwind->p_memsz / sizeof (struct unw_table_entry);
00100
00101 while (lo < hi)
00102 {
00103 size_t mid = (lo + hi) / 2;
00104
00105 f = f_base + mid;
00106 if (data->pc < f->start_offset + seg_base)
00107 hi = mid;
00108 else if (data->pc >= f->end_offset + seg_base)
00109 lo = mid + 1;
00110 else
00111 goto found;
00112 }
00113
00114
00115 return 1;
00116
00117 found:
00118 *data->segment_base = seg_base;
00119 *data->gp = 0;
00120 data->ret = f;
00121
00122 if (p_dynamic)
00123 {
00124
00125
00126 Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base);
00127 for (; dyn->d_tag != DT_NULL ; dyn++)
00128 if (dyn->d_tag == DT_PLTGOT)
00129 {
00130
00131 *data->gp = dyn->d_un.d_ptr;
00132 break;
00133 }
00134 }
00135 else
00136 {
00137
00138
00139 register unsigned long gp __asm__("gp");
00140 *data->gp = gp;
00141 }
00142
00143 return 1;
00144 }
00145
00146
00147
00148
00149 struct unw_table_entry *
00150 _Unwind_FindTableEntry (void *pc, unsigned long *segment_base,
00151 unsigned long *gp)
00152 {
00153 struct unw_ia64_callback_data data;
00154
00155 data.pc = (Elf64_Addr) pc;
00156 data.segment_base = segment_base;
00157 data.gp = gp;
00158 data.ret = NULL;
00159
00160 if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
00161 return NULL;
00162
00163 return data.ret;
00164 }