Commit | Line | Data |
---|---|---|
7176dfd2 JB |
1 | /* Target-dependent code for FreeBSD/arm. |
2 | ||
e2882c85 | 3 | Copyright (C) 2017-2018 Free Software Foundation, Inc. |
7176dfd2 JB |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "defs.h" | |
21 | ||
22 | #include "elf/common.h" | |
23 | #include "arm-tdep.h" | |
24 | #include "arm-fbsd-tdep.h" | |
25 | #include "auxv.h" | |
26 | #include "fbsd-tdep.h" | |
27 | #include "gdbcore.h" | |
28 | #include "osabi.h" | |
29 | #include "solib-svr4.h" | |
30 | #include "trad-frame.h" | |
31 | #include "tramp-frame.h" | |
32 | ||
33 | /* In a signal frame, sp points to a 'struct sigframe' which is | |
34 | defined as: | |
35 | ||
36 | struct sigframe { | |
37 | siginfo_t sf_si; | |
38 | ucontext_t sf_uc; | |
39 | mcontext_vfp_t sf_vfp; | |
40 | }; | |
41 | ||
42 | ucontext_t is defined as: | |
43 | ||
44 | struct __ucontext { | |
45 | sigset_t uc_sigmask; | |
46 | mcontext_t uc_mcontext; | |
47 | ... | |
48 | }; | |
49 | ||
50 | mcontext_t is defined as: | |
51 | ||
52 | struct { | |
53 | unsigned int __gregs[17]; | |
54 | size_t mc_vfp_size; | |
55 | void *mc_vfp_ptr; | |
56 | ... | |
57 | }; | |
58 | ||
59 | mcontext_vfp_t is defined as: | |
60 | ||
61 | struct { | |
62 | uint64_t mcv_reg[32]; | |
63 | uint32_t mcv_fpscr; | |
64 | }; | |
65 | ||
66 | If the VFP state is valid, then mc_vfp_ptr will point to sf_vfp in | |
67 | the sigframe, otherwise it is NULL. There is no non-VFP floating | |
68 | point register state saved in the signal frame. */ | |
69 | ||
70 | #define ARM_MCONTEXT_REG_SIZE 4 | |
71 | #define ARM_MCONTEXT_VFP_REG_SIZE 8 | |
72 | #define ARM_SIGFRAME_UCONTEXT_OFFSET 64 | |
73 | #define ARM_UCONTEXT_MCONTEXT_OFFSET 16 | |
74 | #define ARM_MCONTEXT_VFP_PTR_OFFSET 72 | |
75 | ||
76 | /* Implement the "init" method of struct tramp_frame. */ | |
77 | ||
78 | static void | |
79 | arm_fbsd_sigframe_init (const struct tramp_frame *self, | |
80 | struct frame_info *this_frame, | |
81 | struct trad_frame_cache *this_cache, | |
82 | CORE_ADDR func) | |
83 | { | |
84 | struct gdbarch *gdbarch = get_frame_arch (this_frame); | |
85 | enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
86 | CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM); | |
87 | CORE_ADDR mcontext_addr = (sp | |
88 | + ARM_SIGFRAME_UCONTEXT_OFFSET | |
89 | + ARM_UCONTEXT_MCONTEXT_OFFSET); | |
90 | ULONGEST mcontext_vfp_addr; | |
91 | ||
92 | for (int i = 0; i < 16; i++) | |
93 | { | |
94 | trad_frame_set_reg_addr (this_cache, | |
95 | ARM_A1_REGNUM + i, | |
96 | mcontext_addr + i * ARM_MCONTEXT_REG_SIZE); | |
97 | } | |
98 | trad_frame_set_reg_addr (this_cache, ARM_PS_REGNUM, | |
99 | mcontext_addr + 16 * ARM_MCONTEXT_REG_SIZE); | |
100 | ||
101 | if (safe_read_memory_unsigned_integer (mcontext_addr | |
102 | + ARM_MCONTEXT_VFP_PTR_OFFSET, 4, | |
103 | byte_order, | |
104 | &mcontext_vfp_addr) | |
105 | && mcontext_vfp_addr != 0) | |
106 | { | |
107 | for (int i = 0; i < 32; i++) | |
108 | { | |
109 | trad_frame_set_reg_addr (this_cache, ARM_D0_REGNUM + i, | |
110 | mcontext_vfp_addr | |
111 | + i * ARM_MCONTEXT_VFP_REG_SIZE); | |
112 | } | |
113 | trad_frame_set_reg_addr (this_cache, ARM_FPSCR_REGNUM, | |
114 | mcontext_vfp_addr | |
115 | + 32 * ARM_MCONTEXT_VFP_REG_SIZE); | |
116 | } | |
117 | ||
118 | trad_frame_set_id (this_cache, frame_id_build (sp, func)); | |
119 | } | |
120 | ||
121 | static const struct tramp_frame arm_fbsd_sigframe = | |
122 | { | |
123 | SIGTRAMP_FRAME, | |
124 | 4, | |
125 | { | |
126 | {0xe1a0000d, -1}, /* mov r0, sp */ | |
127 | {0xe2800040, -1}, /* add r0, r0, #SIGF_UC */ | |
128 | {0xe59f700c, -1}, /* ldr r7, [pc, #12] */ | |
129 | {0xef0001a1, -1}, /* swi SYS_sigreturn */ | |
130 | {TRAMP_SENTINEL_INSN, -1} | |
131 | }, | |
132 | arm_fbsd_sigframe_init | |
133 | }; | |
134 | ||
135 | /* Register maps. */ | |
136 | ||
137 | static const struct regcache_map_entry arm_fbsd_gregmap[] = | |
138 | { | |
139 | { 13, ARM_A1_REGNUM, 4 }, /* r0 ... r12 */ | |
140 | { 1, ARM_SP_REGNUM, 4 }, | |
141 | { 1, ARM_LR_REGNUM, 4 }, | |
142 | { 1, ARM_PC_REGNUM, 4 }, | |
143 | { 1, ARM_PS_REGNUM, 4 }, | |
144 | { 0 } | |
145 | }; | |
146 | ||
147 | static const struct regcache_map_entry arm_fbsd_vfpregmap[] = | |
148 | { | |
149 | { 32, ARM_D0_REGNUM, 8 }, /* d0 ... d31 */ | |
150 | { 1, ARM_FPSCR_REGNUM, 4 }, | |
151 | { 0 } | |
152 | }; | |
153 | ||
154 | /* Register set definitions. */ | |
155 | ||
156 | const struct regset arm_fbsd_gregset = | |
157 | { | |
158 | arm_fbsd_gregmap, | |
159 | regcache_supply_regset, regcache_collect_regset | |
160 | }; | |
161 | ||
162 | const struct regset arm_fbsd_vfpregset = | |
163 | { | |
164 | arm_fbsd_vfpregmap, | |
165 | regcache_supply_regset, regcache_collect_regset | |
166 | }; | |
167 | ||
168 | /* Implement the "regset_from_core_section" gdbarch method. */ | |
169 | ||
170 | static void | |
171 | arm_fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch, | |
172 | iterate_over_regset_sections_cb *cb, | |
173 | void *cb_data, | |
174 | const struct regcache *regcache) | |
175 | { | |
176 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
177 | ||
178 | cb (".reg", ARM_FBSD_SIZEOF_GREGSET, &arm_fbsd_gregset, NULL, cb_data); | |
179 | ||
180 | /* While FreeBSD/arm cores do contain a NT_FPREGSET / ".reg2" | |
181 | register set, it is not populated with register values by the | |
182 | kernel but just contains all zeroes. */ | |
183 | if (tdep->vfp_register_count > 0) | |
184 | cb (".reg-arm-vfp", ARM_FBSD_SIZEOF_VFPREGSET, &arm_fbsd_vfpregset, | |
185 | "VFP floating-point", cb_data); | |
186 | } | |
187 | ||
188 | /* Lookup a target description from a target's AT_HWCAP auxiliary | |
189 | vector. */ | |
190 | ||
191 | const struct target_desc * | |
192 | arm_fbsd_read_description_auxv (struct target_ops *target) | |
193 | { | |
194 | CORE_ADDR arm_hwcap = 0; | |
195 | ||
196 | if (target_auxv_search (target, AT_FREEBSD_HWCAP, &arm_hwcap) != 1) | |
197 | return NULL; | |
198 | ||
199 | if (arm_hwcap & HWCAP_VFP) | |
200 | { | |
201 | if (arm_hwcap & HWCAP_NEON) | |
202 | return tdesc_arm_with_neon; | |
203 | else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPD32)) | |
204 | == (HWCAP_VFPv3 | HWCAP_VFPD32)) | |
205 | return tdesc_arm_with_vfpv3; | |
206 | else | |
207 | return tdesc_arm_with_vfpv2; | |
208 | } | |
209 | ||
210 | return NULL; | |
211 | } | |
212 | ||
213 | /* Implement the "core_read_description" gdbarch method. */ | |
214 | ||
215 | static const struct target_desc * | |
216 | arm_fbsd_core_read_description (struct gdbarch *gdbarch, | |
217 | struct target_ops *target, | |
218 | bfd *abfd) | |
219 | { | |
220 | return arm_fbsd_read_description_auxv (target); | |
221 | } | |
222 | ||
223 | /* Implement the 'init_osabi' method of struct gdb_osabi_handler. */ | |
224 | ||
225 | static void | |
226 | arm_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | |
227 | { | |
228 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
229 | ||
230 | /* Generic FreeBSD support. */ | |
231 | fbsd_init_abi (info, gdbarch); | |
232 | ||
233 | if (tdep->fp_model == ARM_FLOAT_AUTO) | |
234 | tdep->fp_model = ARM_FLOAT_SOFT_VFP; | |
235 | ||
236 | tramp_frame_prepend_unwinder (gdbarch, &arm_fbsd_sigframe); | |
237 | ||
238 | set_solib_svr4_fetch_link_map_offsets | |
239 | (gdbarch, svr4_ilp32_fetch_link_map_offsets); | |
240 | ||
241 | tdep->jb_pc = 24; | |
242 | tdep->jb_elt_size = 4; | |
243 | ||
244 | set_gdbarch_iterate_over_regset_sections | |
245 | (gdbarch, arm_fbsd_iterate_over_regset_sections); | |
246 | set_gdbarch_core_read_description (gdbarch, arm_fbsd_core_read_description); | |
247 | ||
248 | /* Single stepping. */ | |
249 | set_gdbarch_software_single_step (gdbarch, arm_software_single_step); | |
250 | } | |
251 | ||
252 | void | |
253 | _initialize_arm_fbsd_tdep (void) | |
254 | { | |
255 | gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_FREEBSD, | |
256 | arm_fbsd_init_abi); | |
257 | } |