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