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