Commit | Line | Data |
---|---|---|
1ae3db19 MS |
1 | /* Target-dependent code for GNU/Linux AArch64. |
2 | ||
32d0add0 | 3 | Copyright (C) 2009-2015 Free Software Foundation, Inc. |
1ae3db19 MS |
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 | ||
08248ca9 SDJ |
38 | #include "cli/cli-utils.h" |
39 | #include "stap-probe.h" | |
40 | #include "parser-defs.h" | |
41 | #include "user-regs.h" | |
42 | #include <ctype.h> | |
43 | ||
1ae3db19 MS |
44 | /* Signal frame handling. |
45 | ||
f2205de0 HZ |
46 | +------------+ ^ |
47 | | saved lr | | | |
48 | +->| saved fp |--+ | |
49 | | | | | |
50 | | | | | |
51 | | +------------+ | |
52 | | | saved lr | | |
53 | +--| saved fp | | |
54 | ^ | | | |
55 | | | | | |
56 | | +------------+ | |
57 | ^ | | | |
58 | | | signal | | |
59 | | | | SIGTRAMP_FRAME (struct rt_sigframe) | |
60 | | | saved regs | | |
61 | +--| saved sp |--> interrupted_sp | |
62 | | | saved pc |--> interrupted_pc | |
63 | | | | | |
64 | | +------------+ | |
65 | | | saved lr |--> default_restorer (movz x8, NR_sys_rt_sigreturn; svc 0) | |
66 | +--| saved fp |<- FP | |
67 | | | NORMAL_FRAME | |
68 | | |<- SP | |
69 | +------------+ | |
1ae3db19 MS |
70 | |
71 | On signal delivery, the kernel will create a signal handler stack | |
72 | frame and setup the return address in LR to point at restorer stub. | |
73 | The signal stack frame is defined by: | |
74 | ||
75 | struct rt_sigframe | |
76 | { | |
77 | siginfo_t info; | |
78 | struct ucontext uc; | |
79 | }; | |
80 | ||
81 | typedef struct | |
82 | { | |
83 | ... 128 bytes | |
84 | } siginfo_t; | |
85 | ||
86 | The ucontext has the following form: | |
87 | struct ucontext | |
88 | { | |
89 | unsigned long uc_flags; | |
90 | struct ucontext *uc_link; | |
91 | stack_t uc_stack; | |
92 | sigset_t uc_sigmask; | |
93 | struct sigcontext uc_mcontext; | |
94 | }; | |
95 | ||
96 | typedef struct sigaltstack | |
97 | { | |
98 | void *ss_sp; | |
99 | int ss_flags; | |
100 | size_t ss_size; | |
101 | } stack_t; | |
102 | ||
103 | struct sigcontext | |
104 | { | |
105 | unsigned long fault_address; | |
106 | unsigned long regs[31]; | |
107 | unsigned long sp; / * 31 * / | |
108 | unsigned long pc; / * 32 * / | |
109 | unsigned long pstate; / * 33 * / | |
110 | __u8 __reserved[4096] | |
111 | }; | |
112 | ||
113 | The restorer stub will always have the form: | |
114 | ||
115 | d28015a8 movz x8, #0xad | |
116 | d4000001 svc #0x0 | |
117 | ||
f2205de0 HZ |
118 | This is a system call sys_rt_sigreturn. |
119 | ||
1ae3db19 MS |
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); | |
1ae3db19 MS |
143 | CORE_ADDR sigcontext_addr = |
144 | sp | |
145 | + AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET | |
146 | + AARCH64_UCONTEXT_SIGCONTEXT_OFFSET; | |
147 | int i; | |
148 | ||
149 | for (i = 0; i < 31; i++) | |
150 | { | |
151 | trad_frame_set_reg_addr (this_cache, | |
152 | AARCH64_X0_REGNUM + i, | |
153 | sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET | |
154 | + i * AARCH64_SIGCONTEXT_REG_SIZE); | |
155 | } | |
f2205de0 HZ |
156 | trad_frame_set_reg_addr (this_cache, AARCH64_SP_REGNUM, |
157 | sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET | |
158 | + 31 * AARCH64_SIGCONTEXT_REG_SIZE); | |
159 | trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM, | |
160 | sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET | |
161 | + 32 * AARCH64_SIGCONTEXT_REG_SIZE); | |
162 | ||
163 | trad_frame_set_id (this_cache, frame_id_build (sp, func)); | |
1ae3db19 MS |
164 | } |
165 | ||
166 | static const struct tramp_frame aarch64_linux_rt_sigframe = | |
167 | { | |
168 | SIGTRAMP_FRAME, | |
169 | 4, | |
170 | { | |
171 | /* movz x8, 0x8b (S=1,o=10,h=0,i=0x8b,r=8) | |
172 | Soo1 0010 1hhi iiii iiii iiii iiir rrrr */ | |
173 | {0xd2801168, -1}, | |
174 | ||
175 | /* svc 0x0 (o=0, l=1) | |
176 | 1101 0100 oooi iiii iiii iiii iii0 00ll */ | |
177 | {0xd4000001, -1}, | |
178 | {TRAMP_SENTINEL_INSN, -1} | |
179 | }, | |
180 | aarch64_linux_sigframe_init | |
181 | }; | |
182 | ||
d4d793bf | 183 | /* Register maps. */ |
1ae3db19 | 184 | |
d4d793bf AA |
185 | static const struct regcache_map_entry aarch64_linux_gregmap[] = |
186 | { | |
187 | { 31, AARCH64_X0_REGNUM, 8 }, /* x0 ... x30 */ | |
188 | { 1, AARCH64_SP_REGNUM, 8 }, | |
189 | { 1, AARCH64_PC_REGNUM, 8 }, | |
190 | { 1, AARCH64_CPSR_REGNUM, 8 }, | |
191 | { 0 } | |
192 | }; | |
1ae3db19 | 193 | |
d4d793bf AA |
194 | static const struct regcache_map_entry aarch64_linux_fpregmap[] = |
195 | { | |
196 | { 32, AARCH64_V0_REGNUM, 16 }, /* v0 ... v31 */ | |
197 | { 1, AARCH64_FPSR_REGNUM, 4 }, | |
198 | { 1, AARCH64_FPCR_REGNUM, 4 }, | |
199 | { 0 } | |
200 | }; | |
1ae3db19 | 201 | |
d4d793bf | 202 | /* Register set definitions. */ |
a069a2bd | 203 | |
d4d793bf | 204 | const struct regset aarch64_linux_gregset = |
a069a2bd | 205 | { |
d4d793bf AA |
206 | aarch64_linux_gregmap, |
207 | regcache_supply_regset, regcache_collect_regset | |
a069a2bd AA |
208 | }; |
209 | ||
d4d793bf | 210 | const struct regset aarch64_linux_fpregset = |
a069a2bd | 211 | { |
d4d793bf AA |
212 | aarch64_linux_fpregmap, |
213 | regcache_supply_regset, regcache_collect_regset | |
a069a2bd AA |
214 | }; |
215 | ||
1ae3db19 MS |
216 | /* Implement the "regset_from_core_section" gdbarch method. */ |
217 | ||
4108500a AA |
218 | static void |
219 | aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, | |
220 | iterate_over_regset_sections_cb *cb, | |
221 | void *cb_data, | |
222 | const struct regcache *regcache) | |
1ae3db19 | 223 | { |
4108500a AA |
224 | cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, &aarch64_linux_gregset, |
225 | NULL, cb_data); | |
226 | cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, &aarch64_linux_fpregset, | |
227 | NULL, cb_data); | |
1ae3db19 MS |
228 | } |
229 | ||
08248ca9 SDJ |
230 | /* Implementation of `gdbarch_stap_is_single_operand', as defined in |
231 | gdbarch.h. */ | |
232 | ||
233 | static int | |
234 | aarch64_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) | |
235 | { | |
236 | return (*s == '#' || isdigit (*s) /* Literal number. */ | |
237 | || *s == '[' /* Register indirection. */ | |
238 | || isalpha (*s)); /* Register value. */ | |
239 | } | |
240 | ||
241 | /* This routine is used to parse a special token in AArch64's assembly. | |
242 | ||
243 | The special tokens parsed by it are: | |
244 | ||
245 | - Register displacement (e.g, [fp, #-8]) | |
246 | ||
247 | It returns one if the special token has been parsed successfully, | |
248 | or zero if the current token is not considered special. */ | |
249 | ||
250 | static int | |
251 | aarch64_stap_parse_special_token (struct gdbarch *gdbarch, | |
252 | struct stap_parse_info *p) | |
253 | { | |
254 | if (*p->arg == '[') | |
255 | { | |
256 | /* Temporary holder for lookahead. */ | |
257 | const char *tmp = p->arg; | |
258 | char *endp; | |
259 | /* Used to save the register name. */ | |
260 | const char *start; | |
261 | char *regname; | |
262 | int len; | |
263 | int got_minus = 0; | |
264 | long displacement; | |
265 | struct stoken str; | |
266 | ||
267 | ++tmp; | |
268 | start = tmp; | |
269 | ||
270 | /* Register name. */ | |
271 | while (isalnum (*tmp)) | |
272 | ++tmp; | |
273 | ||
274 | if (*tmp != ',') | |
275 | return 0; | |
276 | ||
277 | len = tmp - start; | |
278 | regname = alloca (len + 2); | |
279 | ||
280 | strncpy (regname, start, len); | |
281 | regname[len] = '\0'; | |
282 | ||
283 | if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) | |
284 | error (_("Invalid register name `%s' on expression `%s'."), | |
285 | regname, p->saved_arg); | |
286 | ||
287 | ++tmp; | |
288 | tmp = skip_spaces_const (tmp); | |
289 | /* Now we expect a number. It can begin with '#' or simply | |
290 | a digit. */ | |
291 | if (*tmp == '#') | |
292 | ++tmp; | |
293 | ||
294 | if (*tmp == '-') | |
295 | { | |
296 | ++tmp; | |
297 | got_minus = 1; | |
298 | } | |
299 | else if (*tmp == '+') | |
300 | ++tmp; | |
301 | ||
302 | if (!isdigit (*tmp)) | |
303 | return 0; | |
304 | ||
305 | displacement = strtol (tmp, &endp, 10); | |
306 | tmp = endp; | |
307 | ||
308 | /* Skipping last `]'. */ | |
309 | if (*tmp++ != ']') | |
310 | return 0; | |
311 | ||
312 | /* The displacement. */ | |
410a0ff2 SDJ |
313 | write_exp_elt_opcode (&p->pstate, OP_LONG); |
314 | write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long); | |
315 | write_exp_elt_longcst (&p->pstate, displacement); | |
316 | write_exp_elt_opcode (&p->pstate, OP_LONG); | |
08248ca9 | 317 | if (got_minus) |
410a0ff2 | 318 | write_exp_elt_opcode (&p->pstate, UNOP_NEG); |
08248ca9 SDJ |
319 | |
320 | /* The register name. */ | |
410a0ff2 | 321 | write_exp_elt_opcode (&p->pstate, OP_REGISTER); |
08248ca9 SDJ |
322 | str.ptr = regname; |
323 | str.length = len; | |
410a0ff2 SDJ |
324 | write_exp_string (&p->pstate, str); |
325 | write_exp_elt_opcode (&p->pstate, OP_REGISTER); | |
08248ca9 | 326 | |
410a0ff2 | 327 | write_exp_elt_opcode (&p->pstate, BINOP_ADD); |
08248ca9 SDJ |
328 | |
329 | /* Casting to the expected type. */ | |
410a0ff2 SDJ |
330 | write_exp_elt_opcode (&p->pstate, UNOP_CAST); |
331 | write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type)); | |
332 | write_exp_elt_opcode (&p->pstate, UNOP_CAST); | |
08248ca9 | 333 | |
410a0ff2 | 334 | write_exp_elt_opcode (&p->pstate, UNOP_IND); |
08248ca9 SDJ |
335 | |
336 | p->arg = tmp; | |
337 | } | |
338 | else | |
339 | return 0; | |
340 | ||
341 | return 1; | |
342 | } | |
343 | ||
1ae3db19 MS |
344 | static void |
345 | aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | |
346 | { | |
08248ca9 SDJ |
347 | static const char *const stap_integer_prefixes[] = { "#", "", NULL }; |
348 | static const char *const stap_register_prefixes[] = { "", NULL }; | |
349 | static const char *const stap_register_indirection_prefixes[] = { "[", | |
350 | NULL }; | |
351 | static const char *const stap_register_indirection_suffixes[] = { "]", | |
352 | NULL }; | |
1ae3db19 MS |
353 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
354 | ||
355 | tdep->lowest_pc = 0x8000; | |
356 | ||
05feb193 WN |
357 | linux_init_abi (info, gdbarch); |
358 | ||
1ae3db19 MS |
359 | set_solib_svr4_fetch_link_map_offsets (gdbarch, |
360 | svr4_lp64_fetch_link_map_offsets); | |
361 | ||
45e25a36 MS |
362 | /* Enable TLS support. */ |
363 | set_gdbarch_fetch_tls_load_module_address (gdbarch, | |
364 | svr4_fetch_objfile_link_map); | |
365 | ||
1ae3db19 MS |
366 | /* Shared library handling. */ |
367 | set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); | |
368 | ||
369 | set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); | |
370 | tramp_frame_prepend_unwinder (gdbarch, &aarch64_linux_rt_sigframe); | |
371 | ||
372 | /* Enable longjmp. */ | |
373 | tdep->jb_pc = 11; | |
374 | ||
4108500a AA |
375 | set_gdbarch_iterate_over_regset_sections |
376 | (gdbarch, aarch64_linux_iterate_over_regset_sections); | |
08248ca9 SDJ |
377 | |
378 | /* SystemTap related. */ | |
379 | set_gdbarch_stap_integer_prefixes (gdbarch, stap_integer_prefixes); | |
380 | set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes); | |
381 | set_gdbarch_stap_register_indirection_prefixes (gdbarch, | |
382 | stap_register_indirection_prefixes); | |
383 | set_gdbarch_stap_register_indirection_suffixes (gdbarch, | |
384 | stap_register_indirection_suffixes); | |
385 | set_gdbarch_stap_is_single_operand (gdbarch, aarch64_stap_is_single_operand); | |
386 | set_gdbarch_stap_parse_special_token (gdbarch, | |
387 | aarch64_stap_parse_special_token); | |
1ae3db19 MS |
388 | } |
389 | ||
390 | /* Provide a prototype to silence -Wmissing-prototypes. */ | |
391 | extern initialize_file_ftype _initialize_aarch64_linux_tdep; | |
392 | ||
393 | void | |
394 | _initialize_aarch64_linux_tdep (void) | |
395 | { | |
396 | gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX, | |
397 | aarch64_linux_init_abi); | |
398 | } |