Commit | Line | Data |
---|---|---|
1ae3db19 MS |
1 | /* Target-dependent code for GNU/Linux AArch64. |
2 | ||
3 | Copyright (C) 2009-2013 Free Software Foundation, Inc. | |
4 | Contributed by ARM Ltd. | |
5 | ||
6 | This file is part of GDB. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "defs.h" | |
22 | ||
23 | #include "gdbarch.h" | |
24 | #include "glibc-tdep.h" | |
25 | #include "linux-tdep.h" | |
26 | #include "aarch64-tdep.h" | |
27 | #include "aarch64-linux-tdep.h" | |
28 | #include "osabi.h" | |
29 | #include "solib-svr4.h" | |
30 | #include "symtab.h" | |
31 | #include "tramp-frame.h" | |
32 | #include "trad-frame.h" | |
33 | ||
34 | #include "inferior.h" | |
35 | #include "regcache.h" | |
36 | #include "regset.h" | |
37 | ||
38 | /* The general-purpose regset consists of 31 X registers, plus SP, PC, | |
b5dbc8d4 YZ |
39 | and PSTATE registers, as defined in the AArch64 port of the Linux |
40 | kernel. */ | |
41 | #define AARCH64_LINUX_SIZEOF_GREGSET (34 * X_REGISTER_SIZE) | |
1ae3db19 MS |
42 | |
43 | /* The fp regset consists of 32 V registers, plus FPCR and FPSR which | |
44 | are 4 bytes wide each, and the whole structure is padded to 128 bit | |
45 | alignment. */ | |
46 | #define AARCH64_LINUX_SIZEOF_FPREGSET (33 * V_REGISTER_SIZE) | |
47 | ||
48 | /* Signal frame handling. | |
49 | ||
50 | +----------+ ^ | |
51 | | saved lr | | | |
52 | +->| saved fp |--+ | |
53 | | | | | |
54 | | | | | |
55 | | +----------+ | |
56 | | | saved lr | | |
57 | +--| saved fp | | |
58 | ^ | | | |
59 | | | | | |
60 | | +----------+ | |
61 | ^ | | | |
62 | | | signal | | |
63 | | | | | |
64 | | | saved lr |-->interrupted_function_pc | |
65 | +--| saved fp | | |
66 | | +----------+ | |
67 | | | saved lr |--> default_restorer (movz x8, NR_sys_rt_sigreturn; svc 0) | |
68 | +--| saved fp |<- FP | |
69 | | | | |
70 | | |<- SP | |
71 | +----------+ | |
72 | ||
73 | On signal delivery, the kernel will create a signal handler stack | |
74 | frame and setup the return address in LR to point at restorer stub. | |
75 | The signal stack frame is defined by: | |
76 | ||
77 | struct rt_sigframe | |
78 | { | |
79 | siginfo_t info; | |
80 | struct ucontext uc; | |
81 | }; | |
82 | ||
83 | typedef struct | |
84 | { | |
85 | ... 128 bytes | |
86 | } siginfo_t; | |
87 | ||
88 | The ucontext has the following form: | |
89 | struct ucontext | |
90 | { | |
91 | unsigned long uc_flags; | |
92 | struct ucontext *uc_link; | |
93 | stack_t uc_stack; | |
94 | sigset_t uc_sigmask; | |
95 | struct sigcontext uc_mcontext; | |
96 | }; | |
97 | ||
98 | typedef struct sigaltstack | |
99 | { | |
100 | void *ss_sp; | |
101 | int ss_flags; | |
102 | size_t ss_size; | |
103 | } stack_t; | |
104 | ||
105 | struct sigcontext | |
106 | { | |
107 | unsigned long fault_address; | |
108 | unsigned long regs[31]; | |
109 | unsigned long sp; / * 31 * / | |
110 | unsigned long pc; / * 32 * / | |
111 | unsigned long pstate; / * 33 * / | |
112 | __u8 __reserved[4096] | |
113 | }; | |
114 | ||
115 | The restorer stub will always have the form: | |
116 | ||
117 | d28015a8 movz x8, #0xad | |
118 | d4000001 svc #0x0 | |
119 | ||
120 | We detect signal frames by snooping the return code for the restorer | |
121 | instruction sequence. | |
122 | ||
123 | The handler then needs to recover the saved register set from | |
124 | ucontext.uc_mcontext. */ | |
125 | ||
126 | /* These magic numbers need to reflect the layout of the kernel | |
127 | defined struct rt_sigframe and ucontext. */ | |
128 | #define AARCH64_SIGCONTEXT_REG_SIZE 8 | |
129 | #define AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET 128 | |
130 | #define AARCH64_UCONTEXT_SIGCONTEXT_OFFSET 176 | |
131 | #define AARCH64_SIGCONTEXT_XO_OFFSET 8 | |
132 | ||
133 | /* Implement the "init" method of struct tramp_frame. */ | |
134 | ||
135 | static void | |
136 | aarch64_linux_sigframe_init (const struct tramp_frame *self, | |
137 | struct frame_info *this_frame, | |
138 | struct trad_frame_cache *this_cache, | |
139 | CORE_ADDR func) | |
140 | { | |
141 | struct gdbarch *gdbarch = get_frame_arch (this_frame); | |
142 | CORE_ADDR sp = get_frame_register_unsigned (this_frame, AARCH64_SP_REGNUM); | |
143 | CORE_ADDR fp = get_frame_register_unsigned (this_frame, AARCH64_FP_REGNUM); | |
144 | CORE_ADDR sigcontext_addr = | |
145 | sp | |
146 | + AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET | |
147 | + AARCH64_UCONTEXT_SIGCONTEXT_OFFSET; | |
148 | int i; | |
149 | ||
150 | for (i = 0; i < 31; i++) | |
151 | { | |
152 | trad_frame_set_reg_addr (this_cache, | |
153 | AARCH64_X0_REGNUM + i, | |
154 | sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET | |
155 | + i * AARCH64_SIGCONTEXT_REG_SIZE); | |
156 | } | |
157 | ||
158 | trad_frame_set_reg_addr (this_cache, AARCH64_FP_REGNUM, fp); | |
159 | trad_frame_set_reg_addr (this_cache, AARCH64_LR_REGNUM, fp + 8); | |
160 | trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM, fp + 8); | |
161 | ||
162 | trad_frame_set_id (this_cache, frame_id_build (fp, func)); | |
163 | } | |
164 | ||
165 | static const struct tramp_frame aarch64_linux_rt_sigframe = | |
166 | { | |
167 | SIGTRAMP_FRAME, | |
168 | 4, | |
169 | { | |
170 | /* movz x8, 0x8b (S=1,o=10,h=0,i=0x8b,r=8) | |
171 | Soo1 0010 1hhi iiii iiii iiii iiir rrrr */ | |
172 | {0xd2801168, -1}, | |
173 | ||
174 | /* svc 0x0 (o=0, l=1) | |
175 | 1101 0100 oooi iiii iiii iiii iii0 00ll */ | |
176 | {0xd4000001, -1}, | |
177 | {TRAMP_SENTINEL_INSN, -1} | |
178 | }, | |
179 | aarch64_linux_sigframe_init | |
180 | }; | |
181 | ||
182 | /* Fill GDB's register array with the general-purpose register values | |
183 | in the buffer pointed by GREGS_BUF. */ | |
184 | ||
185 | void | |
186 | aarch64_linux_supply_gregset (struct regcache *regcache, | |
187 | const gdb_byte *gregs_buf) | |
188 | { | |
189 | int regno; | |
190 | ||
191 | for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++) | |
192 | regcache_raw_supply (regcache, regno, | |
193 | gregs_buf + X_REGISTER_SIZE | |
194 | * (regno - AARCH64_X0_REGNUM)); | |
195 | } | |
196 | ||
197 | /* The "supply_regset" function for the general-purpose register set. */ | |
198 | ||
199 | static void | |
200 | supply_gregset_from_core (const struct regset *regset, | |
201 | struct regcache *regcache, | |
202 | int regnum, const void *regbuf, size_t len) | |
203 | { | |
204 | aarch64_linux_supply_gregset (regcache, (const gdb_byte *) regbuf); | |
205 | } | |
206 | ||
207 | /* Fill GDB's register array with the floating-point register values | |
208 | in the buffer pointed by FPREGS_BUF. */ | |
209 | ||
210 | void | |
211 | aarch64_linux_supply_fpregset (struct regcache *regcache, | |
212 | const gdb_byte *fpregs_buf) | |
213 | { | |
214 | int regno; | |
215 | ||
216 | for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++) | |
217 | regcache_raw_supply (regcache, regno, | |
218 | fpregs_buf + V_REGISTER_SIZE | |
219 | * (regno - AARCH64_V0_REGNUM)); | |
220 | ||
221 | regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, | |
222 | fpregs_buf + V_REGISTER_SIZE * 32); | |
223 | regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, | |
224 | fpregs_buf + V_REGISTER_SIZE * 32 + 4); | |
225 | } | |
226 | ||
227 | /* The "supply_regset" function for the floating-point register set. */ | |
228 | ||
229 | static void | |
230 | supply_fpregset_from_core (const struct regset *regset, | |
231 | struct regcache *regcache, | |
232 | int regnum, const void *regbuf, size_t len) | |
233 | { | |
234 | aarch64_linux_supply_fpregset (regcache, (const gdb_byte *) regbuf); | |
235 | } | |
236 | ||
237 | /* Implement the "regset_from_core_section" gdbarch method. */ | |
238 | ||
239 | static const struct regset * | |
240 | aarch64_linux_regset_from_core_section (struct gdbarch *gdbarch, | |
241 | const char *sect_name, | |
242 | size_t sect_size) | |
243 | { | |
244 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
245 | ||
246 | if (strcmp (sect_name, ".reg") == 0 | |
247 | && sect_size == AARCH64_LINUX_SIZEOF_GREGSET) | |
248 | { | |
249 | if (tdep->gregset == NULL) | |
250 | tdep->gregset = regset_alloc (gdbarch, supply_gregset_from_core, | |
251 | NULL); | |
252 | return tdep->gregset; | |
253 | } | |
254 | ||
255 | if (strcmp (sect_name, ".reg2") == 0 | |
256 | && sect_size == AARCH64_LINUX_SIZEOF_FPREGSET) | |
257 | { | |
258 | if (tdep->fpregset == NULL) | |
259 | tdep->fpregset = regset_alloc (gdbarch, supply_fpregset_from_core, | |
260 | NULL); | |
261 | return tdep->fpregset; | |
262 | } | |
263 | return NULL; | |
264 | } | |
265 | ||
266 | static void | |
267 | aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | |
268 | { | |
269 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
270 | ||
271 | tdep->lowest_pc = 0x8000; | |
272 | ||
273 | set_solib_svr4_fetch_link_map_offsets (gdbarch, | |
274 | svr4_lp64_fetch_link_map_offsets); | |
275 | ||
45e25a36 MS |
276 | /* Enable TLS support. */ |
277 | set_gdbarch_fetch_tls_load_module_address (gdbarch, | |
278 | svr4_fetch_objfile_link_map); | |
279 | ||
1ae3db19 MS |
280 | /* Shared library handling. */ |
281 | set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); | |
282 | ||
283 | set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); | |
284 | tramp_frame_prepend_unwinder (gdbarch, &aarch64_linux_rt_sigframe); | |
285 | ||
286 | /* Enable longjmp. */ | |
287 | tdep->jb_pc = 11; | |
288 | ||
289 | set_gdbarch_regset_from_core_section (gdbarch, | |
290 | aarch64_linux_regset_from_core_section); | |
291 | } | |
292 | ||
293 | /* Provide a prototype to silence -Wmissing-prototypes. */ | |
294 | extern initialize_file_ftype _initialize_aarch64_linux_tdep; | |
295 | ||
296 | void | |
297 | _initialize_aarch64_linux_tdep (void) | |
298 | { | |
299 | gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX, | |
300 | aarch64_linux_init_abi); | |
301 | } |