OSDN Git Service

ia64-aix port from Tim Wall
[pf3gnuchains/gcc-fork.git] / gcc / config / ia64 / unwind-aix.c
1 /* Implements unwind table entry lookup for AIX (cf. fde-glibc.c). 
2    Copyright (C) 2001 Free Software Foundation, Inc.
3    Contributed by Timothy Wall <twall@redhat.com>
4
5    This file is part of GNU CC.
6
7    GNU CC is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11
12    GNU CC is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GNU CC; see the file COPYING.  If not, write to
19    the Free Software Foundation, 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21
22 #include "tconfig.h"
23 #include "tsystem.h"
24 #include "unwind.h"
25 #include "unwind-ia64.h"
26
27 #include <dlfcn.h>
28 #include <link.h>
29 #include <sys/mman.h>
30
31 static struct unw_table_entry *
32 find_fde_for_dso (Elf64_Addr pc, rt_link_map *map,
33                   unsigned long* pseg_base, unsigned long* pgp)
34 {
35   rt_segment *seg;
36   Elf64_Addr seg_base;
37   struct unw_table_entry *f_base;
38   size_t lo, hi;
39   
40   /* See if PC falls into one of the loaded segments.  */
41   for (seg = map->l_segments; seg; seg = (rt_segment *)seg->s_next) 
42     {
43       if (pc >= seg->s_map_addr && pc < seg->s_map_addr + seg->s_mapsz)
44         break;
45     }
46   if (!seg) 
47     return NULL;
48   
49   /* Search for the entry within the unwind table.  */
50   f_base = (struct unw_table_entry *) (map->l_unwind_table);
51   seg_base = (Elf64_Addr) seg->s_map_addr;
52   lo = 0;
53   hi = map->l_unwind_sz / sizeof (struct unw_table_entry);
54
55   while (lo < hi)
56     {
57       size_t mid = (lo + hi) / 2;
58       struct unw_table_entry *f = f_base + mid;
59
60       if (pc < f->start_offset + seg_base)
61         hi = mid;
62       else if (pc >= f->end_offset + seg_base)
63         lo = mid + 1;
64       else {
65         /* AIX executables are *always* dynamic.  Look up GP for this
66            object. */ 
67         Elf64_Dyn *dyn = map->l_ld;
68         *pgp = 0;
69         for (; dyn->d_tag != DT_NULL ; dyn++) 
70           {
71             if (dyn->d_tag == DT_PLTGOT)
72               {
73                 *pgp = dyn->d_un.d_ptr;
74                 break;
75               }
76           }
77         *pseg_base = seg_base;
78         return f;
79       }
80     }
81   return NULL;
82 }
83
84 /* Return a pointer to the unwind table entry for the function containing
85    PC. */  
86 struct unw_table_entry *
87 _Unwind_FindTableEntry (void *pc, unsigned long *pseg_base, unsigned long *pgp)
88 {
89   extern rt_r_debug _r_debug;
90   struct unw_table_entry *ret;
91   rt_link_map *map = _r_debug.r_map; /* address of link map */
92
93   /* Check the main application first, hoping that most of the user's
94      code is there instead of in some library.  */
95   ret = find_fde_for_dso ((Elf64_Addr)pc, map, pseg_base, pgp);
96   if (ret) 
97     {
98       /* If we're in the main application, use the current GP value. */
99       register unsigned long gp __asm__("gp");
100       *pgp = gp;
101       return ret;
102     }
103
104   /* FIXME need a DSO lock mechanism for AIX here, to ensure shared
105      libraries aren't changed while we're examining them.  */
106
107   for (map = _r_debug.r_map; map; map = map->l_next)
108     {
109       /* Skip the main application's entry.  */
110       if (!map->l_name)
111       continue;
112       ret = find_fde_for_dso ((Elf64_Addr)pc, map, pseg_base, pgp);
113       if (ret)
114       break;
115     }
116
117   /* FIXME need a DSO unlock mechanism for AIX here.  */
118
119   return ret;
120 }