* alpha-linux-tdep.c (alpha_linux_pc_in_sigtramp): New function.
[platform/upstream/binutils.git] / gdb / alphanbsd-tdep.c
1 /* Target-dependent code for NetBSD/Alpha.
2    Copyright 2002 Free Software Foundation, Inc.
3    Contributed by Wasabi Systems, Inc.
4
5    This file is part of GDB.
6
7    This program 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 of the License, or
10    (at your option) any later version.
11
12    This program 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 this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21
22 #include "defs.h"
23 #include "gdbcore.h"
24 #include "value.h"
25 #include "solib-svr4.h"
26
27 #include "alpha-tdep.h"
28
29 /* Fetch (and possibly build) an appropriate link_map_offsets
30    structure for NetBSD/alpha targets using the struct offsets
31    defined in <link.h> (but without actual reference to that file).
32
33    This makes it possible to access NetBSD/alpha shared libraries
34    from a GDB that was not built on a NetBSD/alpha host (for cross
35    debugging).  */
36
37 static struct link_map_offsets *
38 alphanbsd_solib_svr4_fetch_link_map_offsets (void)
39 {
40   static struct link_map_offsets lmo;
41   static struct link_map_offsets *lmp = NULL;
42
43   if (lmp == NULL)
44     {
45       lmp = &lmo;
46
47       lmo.r_debug_size = 32;
48
49       lmo.r_map_offset = 8;
50       lmo.r_map_size   = 8;
51
52       lmo.link_map_size = 40;
53
54       lmo.l_addr_offset = 0;
55       lmo.l_addr_size   = 8;
56
57       lmo.l_name_offset = 8;
58       lmo.l_name_size   = 8;
59
60       lmo.l_next_offset = 24;
61       lmo.l_next_size   = 8;
62
63       lmo.l_prev_offset = 32;
64       lmo.l_prev_size   = 8;
65     }
66
67   return lmp;
68 }
69
70 /* Under NetBSD/alpha, signal handler invocations can be identified by the
71    designated code sequence that is used to return from a signal handler.
72    In particular, the return address of a signal handler points to the
73    following code sequence:
74
75         ldq     a0, 0(sp)
76         lda     sp, 16(sp)
77         lda     v0, 295(zero)   # __sigreturn14
78         call_pal callsys
79
80    Each instruction has a unique encoding, so we simply attempt to match
81    the instruction the PC is pointing to with any of the above instructions.
82    If there is a hit, we know the offset to the start of the designated
83    sequence and can then check whether we really are executing in the
84    signal trampoline.  If not, -1 is returned, otherwise the offset from the
85    start of the return sequence is returned.  */
86 static const unsigned int sigtramp_retcode[] =
87 {
88   0xa61e0000,           /* ldq a0, 0(sp) */
89   0x23de0010,           /* lda sp, 16(sp) */
90   0x201f0127,           /* lda v0, 295(zero) */
91   0x00000083,           /* call_pal callsys */
92 };
93 #define RETCODE_NWORDS \
94   (sizeof (sigtramp_retcode) / sizeof (sigtramp_retcode[0]))
95
96 LONGEST
97 alphanbsd_sigtramp_offset (CORE_ADDR pc)
98 {
99   unsigned int ret[4], w;
100   LONGEST off;
101   int i;
102
103   if (read_memory_nobpt (pc, (char *) &w, 4) != 0)
104     return -1;
105
106   for (i = 0; i < RETCODE_NWORDS; i++)
107     {
108       if (w == sigtramp_retcode[i])
109         break;
110     }
111   if (i == RETCODE_NWORDS)
112     return (-1);
113
114   off = i * 4;
115   pc -= off;
116
117   if (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
118     return -1;
119
120   if (memcmp (ret, sigtramp_retcode, sizeof (sigtramp_retcode)) == 0)
121     return off;
122
123   return -1;
124 }
125
126 static int
127 alphanbsd_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
128 {
129   return (alphanbsd_sigtramp_offset (pc) >= 0);
130 }
131
132 static void
133 alphanbsd_init_abi (struct gdbarch_info info,
134                     struct gdbarch *gdbarch)
135 {
136   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
137
138   set_gdbarch_pc_in_sigtramp (gdbarch, alphanbsd_pc_in_sigtramp);
139
140   /* NetBSD/alpha does not provide single step support via ptrace(2); we
141      must use software single-stepping.  */
142   set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
143
144   set_solib_svr4_fetch_link_map_offsets (gdbarch,
145                                   alphanbsd_solib_svr4_fetch_link_map_offsets);
146
147   tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
148 }
149
150 void
151 _initialize_alphanbsd_tdep (void)
152 {
153   alpha_gdbarch_register_os_abi (ALPHA_ABI_NETBSD, alphanbsd_init_abi);
154 }