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