Commit | Line | Data |
---|---|---|
1ae3db19 MS |
1 | /* Target-dependent code for GNU/Linux AArch64. |
2 | ||
ecd75fc8 | 3 | Copyright (C) 2009-2014 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 | ||
218 | static const struct regset * | |
219 | aarch64_linux_regset_from_core_section (struct gdbarch *gdbarch, | |
220 | const char *sect_name, | |
221 | size_t sect_size) | |
222 | { | |
1ae3db19 MS |
223 | if (strcmp (sect_name, ".reg") == 0 |
224 | && sect_size == AARCH64_LINUX_SIZEOF_GREGSET) | |
a069a2bd | 225 | return &aarch64_linux_gregset; |
1ae3db19 MS |
226 | |
227 | if (strcmp (sect_name, ".reg2") == 0 | |
228 | && sect_size == AARCH64_LINUX_SIZEOF_FPREGSET) | |
a069a2bd AA |
229 | return &aarch64_linux_fpregset; |
230 | ||
1ae3db19 MS |
231 | return NULL; |
232 | } | |
233 | ||
08248ca9 SDJ |
234 | /* Implementation of `gdbarch_stap_is_single_operand', as defined in |
235 | gdbarch.h. */ | |
236 | ||
237 | static int | |
238 | aarch64_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) | |
239 | { | |
240 | return (*s == '#' || isdigit (*s) /* Literal number. */ | |
241 | || *s == '[' /* Register indirection. */ | |
242 | || isalpha (*s)); /* Register value. */ | |
243 | } | |
244 | ||
245 | /* This routine is used to parse a special token in AArch64's assembly. | |
246 | ||
247 | The special tokens parsed by it are: | |
248 | ||
249 | - Register displacement (e.g, [fp, #-8]) | |
250 | ||
251 | It returns one if the special token has been parsed successfully, | |
252 | or zero if the current token is not considered special. */ | |
253 | ||
254 | static int | |
255 | aarch64_stap_parse_special_token (struct gdbarch *gdbarch, | |
256 | struct stap_parse_info *p) | |
257 | { | |
258 | if (*p->arg == '[') | |
259 | { | |
260 | /* Temporary holder for lookahead. */ | |
261 | const char *tmp = p->arg; | |
262 | char *endp; | |
263 | /* Used to save the register name. */ | |
264 | const char *start; | |
265 | char *regname; | |
266 | int len; | |
267 | int got_minus = 0; | |
268 | long displacement; | |
269 | struct stoken str; | |
270 | ||
271 | ++tmp; | |
272 | start = tmp; | |
273 | ||
274 | /* Register name. */ | |
275 | while (isalnum (*tmp)) | |
276 | ++tmp; | |
277 | ||
278 | if (*tmp != ',') | |
279 | return 0; | |
280 | ||
281 | len = tmp - start; | |
282 | regname = alloca (len + 2); | |
283 | ||
284 | strncpy (regname, start, len); | |
285 | regname[len] = '\0'; | |
286 | ||
287 | if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) | |
288 | error (_("Invalid register name `%s' on expression `%s'."), | |
289 | regname, p->saved_arg); | |
290 | ||
291 | ++tmp; | |
292 | tmp = skip_spaces_const (tmp); | |
293 | /* Now we expect a number. It can begin with '#' or simply | |
294 | a digit. */ | |
295 | if (*tmp == '#') | |
296 | ++tmp; | |
297 | ||
298 | if (*tmp == '-') | |
299 | { | |
300 | ++tmp; | |
301 | got_minus = 1; | |
302 | } | |
303 | else if (*tmp == '+') | |
304 | ++tmp; | |
305 | ||
306 | if (!isdigit (*tmp)) | |
307 | return 0; | |
308 | ||
309 | displacement = strtol (tmp, &endp, 10); | |
310 | tmp = endp; | |
311 | ||
312 | /* Skipping last `]'. */ | |
313 | if (*tmp++ != ']') | |
314 | return 0; | |
315 | ||
316 | /* The displacement. */ | |
410a0ff2 SDJ |
317 | write_exp_elt_opcode (&p->pstate, OP_LONG); |
318 | write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long); | |
319 | write_exp_elt_longcst (&p->pstate, displacement); | |
320 | write_exp_elt_opcode (&p->pstate, OP_LONG); | |
08248ca9 | 321 | if (got_minus) |
410a0ff2 | 322 | write_exp_elt_opcode (&p->pstate, UNOP_NEG); |
08248ca9 SDJ |
323 | |
324 | /* The register name. */ | |
410a0ff2 | 325 | write_exp_elt_opcode (&p->pstate, OP_REGISTER); |
08248ca9 SDJ |
326 | str.ptr = regname; |
327 | str.length = len; | |
410a0ff2 SDJ |
328 | write_exp_string (&p->pstate, str); |
329 | write_exp_elt_opcode (&p->pstate, OP_REGISTER); | |
08248ca9 | 330 | |
410a0ff2 | 331 | write_exp_elt_opcode (&p->pstate, BINOP_ADD); |
08248ca9 SDJ |
332 | |
333 | /* Casting to the expected type. */ | |
410a0ff2 SDJ |
334 | write_exp_elt_opcode (&p->pstate, UNOP_CAST); |
335 | write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type)); | |
336 | write_exp_elt_opcode (&p->pstate, UNOP_CAST); | |
08248ca9 | 337 | |
410a0ff2 | 338 | write_exp_elt_opcode (&p->pstate, UNOP_IND); |
08248ca9 SDJ |
339 | |
340 | p->arg = tmp; | |
341 | } | |
342 | else | |
343 | return 0; | |
344 | ||
345 | return 1; | |
346 | } | |
347 | ||
1ae3db19 MS |
348 | static void |
349 | aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | |
350 | { | |
08248ca9 SDJ |
351 | static const char *const stap_integer_prefixes[] = { "#", "", NULL }; |
352 | static const char *const stap_register_prefixes[] = { "", NULL }; | |
353 | static const char *const stap_register_indirection_prefixes[] = { "[", | |
354 | NULL }; | |
355 | static const char *const stap_register_indirection_suffixes[] = { "]", | |
356 | NULL }; | |
1ae3db19 MS |
357 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
358 | ||
359 | tdep->lowest_pc = 0x8000; | |
360 | ||
05feb193 WN |
361 | linux_init_abi (info, gdbarch); |
362 | ||
1ae3db19 MS |
363 | set_solib_svr4_fetch_link_map_offsets (gdbarch, |
364 | svr4_lp64_fetch_link_map_offsets); | |
365 | ||
45e25a36 MS |
366 | /* Enable TLS support. */ |
367 | set_gdbarch_fetch_tls_load_module_address (gdbarch, | |
368 | svr4_fetch_objfile_link_map); | |
369 | ||
1ae3db19 MS |
370 | /* Shared library handling. */ |
371 | set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); | |
372 | ||
373 | set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); | |
374 | tramp_frame_prepend_unwinder (gdbarch, &aarch64_linux_rt_sigframe); | |
375 | ||
376 | /* Enable longjmp. */ | |
377 | tdep->jb_pc = 11; | |
378 | ||
379 | set_gdbarch_regset_from_core_section (gdbarch, | |
380 | aarch64_linux_regset_from_core_section); | |
08248ca9 SDJ |
381 | |
382 | /* SystemTap related. */ | |
383 | set_gdbarch_stap_integer_prefixes (gdbarch, stap_integer_prefixes); | |
384 | set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes); | |
385 | set_gdbarch_stap_register_indirection_prefixes (gdbarch, | |
386 | stap_register_indirection_prefixes); | |
387 | set_gdbarch_stap_register_indirection_suffixes (gdbarch, | |
388 | stap_register_indirection_suffixes); | |
389 | set_gdbarch_stap_is_single_operand (gdbarch, aarch64_stap_is_single_operand); | |
390 | set_gdbarch_stap_parse_special_token (gdbarch, | |
391 | aarch64_stap_parse_special_token); | |
1ae3db19 MS |
392 | } |
393 | ||
394 | /* Provide a prototype to silence -Wmissing-prototypes. */ | |
395 | extern initialize_file_ftype _initialize_aarch64_linux_tdep; | |
396 | ||
397 | void | |
398 | _initialize_aarch64_linux_tdep (void) | |
399 | { | |
400 | gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX, | |
401 | aarch64_linux_init_abi); | |
402 | } |