]>
Commit | Line | Data |
---|---|---|
7176dfd2 JB |
1 | /* Target-dependent code for FreeBSD/arm. |
2 | ||
213516ef | 3 | Copyright (C) 2017-2023 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 | ||
4de283e4 | 22 | #include "elf/common.h" |
d105cce5 AH |
23 | #include "target-descriptions.h" |
24 | #include "aarch32-tdep.h" | |
d55e5aa6 | 25 | #include "arm-tdep.h" |
4de283e4 | 26 | #include "arm-fbsd-tdep.h" |
7176dfd2 JB |
27 | #include "auxv.h" |
28 | #include "fbsd-tdep.h" | |
29 | #include "gdbcore.h" | |
2e686a74 | 30 | #include "inferior.h" |
7176dfd2 JB |
31 | #include "osabi.h" |
32 | #include "solib-svr4.h" | |
33 | #include "trad-frame.h" | |
34 | #include "tramp-frame.h" | |
35 | ||
7054e2ff JB |
36 | /* Register maps. */ |
37 | ||
38 | static const struct regcache_map_entry arm_fbsd_gregmap[] = | |
39 | { | |
40 | { 13, ARM_A1_REGNUM, 4 }, /* r0 ... r12 */ | |
41 | { 1, ARM_SP_REGNUM, 4 }, | |
42 | { 1, ARM_LR_REGNUM, 4 }, | |
43 | { 1, ARM_PC_REGNUM, 4 }, | |
44 | { 1, ARM_PS_REGNUM, 4 }, | |
45 | { 0 } | |
46 | }; | |
47 | ||
48 | static const struct regcache_map_entry arm_fbsd_vfpregmap[] = | |
49 | { | |
50 | { 32, ARM_D0_REGNUM, 8 }, /* d0 ... d31 */ | |
51 | { 1, ARM_FPSCR_REGNUM, 4 }, | |
52 | { 0 } | |
53 | }; | |
54 | ||
759bbcb9 JB |
55 | /* Register numbers are relative to tdep->tls_regnum. */ |
56 | ||
57 | static const struct regcache_map_entry arm_fbsd_tls_regmap[] = | |
58 | { | |
59 | { 1, 0, 4 }, /* tpidruro */ | |
60 | { 0 } | |
61 | }; | |
62 | ||
7176dfd2 JB |
63 | /* In a signal frame, sp points to a 'struct sigframe' which is |
64 | defined as: | |
65 | ||
66 | struct sigframe { | |
67 | siginfo_t sf_si; | |
68 | ucontext_t sf_uc; | |
69 | mcontext_vfp_t sf_vfp; | |
70 | }; | |
71 | ||
72 | ucontext_t is defined as: | |
73 | ||
74 | struct __ucontext { | |
75 | sigset_t uc_sigmask; | |
76 | mcontext_t uc_mcontext; | |
77 | ... | |
78 | }; | |
79 | ||
80 | mcontext_t is defined as: | |
81 | ||
82 | struct { | |
83 | unsigned int __gregs[17]; | |
84 | size_t mc_vfp_size; | |
85 | void *mc_vfp_ptr; | |
86 | ... | |
87 | }; | |
88 | ||
89 | mcontext_vfp_t is defined as: | |
90 | ||
91 | struct { | |
92 | uint64_t mcv_reg[32]; | |
93 | uint32_t mcv_fpscr; | |
94 | }; | |
95 | ||
96 | If the VFP state is valid, then mc_vfp_ptr will point to sf_vfp in | |
97 | the sigframe, otherwise it is NULL. There is no non-VFP floating | |
98 | point register state saved in the signal frame. */ | |
99 | ||
7176dfd2 JB |
100 | #define ARM_SIGFRAME_UCONTEXT_OFFSET 64 |
101 | #define ARM_UCONTEXT_MCONTEXT_OFFSET 16 | |
102 | #define ARM_MCONTEXT_VFP_PTR_OFFSET 72 | |
103 | ||
104 | /* Implement the "init" method of struct tramp_frame. */ | |
105 | ||
106 | static void | |
107 | arm_fbsd_sigframe_init (const struct tramp_frame *self, | |
bd2b40ac | 108 | frame_info_ptr this_frame, |
7176dfd2 JB |
109 | struct trad_frame_cache *this_cache, |
110 | CORE_ADDR func) | |
111 | { | |
112 | struct gdbarch *gdbarch = get_frame_arch (this_frame); | |
113 | enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
114 | CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM); | |
115 | CORE_ADDR mcontext_addr = (sp | |
116 | + ARM_SIGFRAME_UCONTEXT_OFFSET | |
117 | + ARM_UCONTEXT_MCONTEXT_OFFSET); | |
118 | ULONGEST mcontext_vfp_addr; | |
119 | ||
7054e2ff JB |
120 | trad_frame_set_reg_regmap (this_cache, arm_fbsd_gregmap, mcontext_addr, |
121 | regcache_map_entry_size (arm_fbsd_gregmap)); | |
7176dfd2 JB |
122 | |
123 | if (safe_read_memory_unsigned_integer (mcontext_addr | |
124 | + ARM_MCONTEXT_VFP_PTR_OFFSET, 4, | |
125 | byte_order, | |
126 | &mcontext_vfp_addr) | |
127 | && mcontext_vfp_addr != 0) | |
7054e2ff JB |
128 | trad_frame_set_reg_regmap (this_cache, arm_fbsd_vfpregmap, mcontext_vfp_addr, |
129 | regcache_map_entry_size (arm_fbsd_vfpregmap)); | |
7176dfd2 JB |
130 | |
131 | trad_frame_set_id (this_cache, frame_id_build (sp, func)); | |
132 | } | |
133 | ||
134 | static const struct tramp_frame arm_fbsd_sigframe = | |
135 | { | |
136 | SIGTRAMP_FRAME, | |
137 | 4, | |
138 | { | |
7bc02706 TT |
139 | {0xe1a0000d, ULONGEST_MAX}, /* mov r0, sp */ |
140 | {0xe2800040, ULONGEST_MAX}, /* add r0, r0, #SIGF_UC */ | |
141 | {0xe59f700c, ULONGEST_MAX}, /* ldr r7, [pc, #12] */ | |
142 | {0xef0001a1, ULONGEST_MAX}, /* swi SYS_sigreturn */ | |
143 | {TRAMP_SENTINEL_INSN, ULONGEST_MAX} | |
7176dfd2 JB |
144 | }, |
145 | arm_fbsd_sigframe_init | |
146 | }; | |
147 | ||
7176dfd2 JB |
148 | /* Register set definitions. */ |
149 | ||
150 | const struct regset arm_fbsd_gregset = | |
151 | { | |
152 | arm_fbsd_gregmap, | |
153 | regcache_supply_regset, regcache_collect_regset | |
154 | }; | |
155 | ||
156 | const struct regset arm_fbsd_vfpregset = | |
157 | { | |
158 | arm_fbsd_vfpregmap, | |
159 | regcache_supply_regset, regcache_collect_regset | |
160 | }; | |
161 | ||
759bbcb9 JB |
162 | static void |
163 | arm_fbsd_supply_tls_regset (const struct regset *regset, | |
164 | struct regcache *regcache, | |
165 | int regnum, const void *buf, size_t size) | |
166 | { | |
167 | struct gdbarch *gdbarch = regcache->arch (); | |
168 | arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); | |
169 | ||
170 | regcache->supply_regset (regset, tdep->tls_regnum, regnum, buf, size); | |
171 | } | |
172 | ||
173 | static void | |
174 | arm_fbsd_collect_tls_regset (const struct regset *regset, | |
175 | const struct regcache *regcache, | |
176 | int regnum, void *buf, size_t size) | |
177 | { | |
178 | struct gdbarch *gdbarch = regcache->arch (); | |
179 | arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); | |
180 | ||
181 | regcache->collect_regset (regset, tdep->tls_regnum, regnum, buf, size); | |
182 | } | |
183 | ||
184 | const struct regset arm_fbsd_tls_regset = | |
185 | { | |
186 | arm_fbsd_tls_regmap, | |
187 | arm_fbsd_supply_tls_regset, arm_fbsd_collect_tls_regset | |
188 | }; | |
189 | ||
89203d40 | 190 | /* Implement the "iterate_over_regset_sections" gdbarch method. */ |
7176dfd2 JB |
191 | |
192 | static void | |
193 | arm_fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch, | |
194 | iterate_over_regset_sections_cb *cb, | |
195 | void *cb_data, | |
196 | const struct regcache *regcache) | |
197 | { | |
08106042 | 198 | arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
7176dfd2 | 199 | |
a616bb94 AH |
200 | cb (".reg", ARM_FBSD_SIZEOF_GREGSET, ARM_FBSD_SIZEOF_GREGSET, |
201 | &arm_fbsd_gregset, NULL, cb_data); | |
7176dfd2 | 202 | |
099fbce0 | 203 | if (tdep->tls_regnum > 0) |
759bbcb9 JB |
204 | cb (".reg-aarch-tls", ARM_FBSD_SIZEOF_TLSREGSET, ARM_FBSD_SIZEOF_TLSREGSET, |
205 | &arm_fbsd_tls_regset, NULL, cb_data); | |
099fbce0 | 206 | |
7176dfd2 JB |
207 | /* While FreeBSD/arm cores do contain a NT_FPREGSET / ".reg2" |
208 | register set, it is not populated with register values by the | |
209 | kernel but just contains all zeroes. */ | |
210 | if (tdep->vfp_register_count > 0) | |
a616bb94 AH |
211 | cb (".reg-arm-vfp", ARM_FBSD_SIZEOF_VFPREGSET, ARM_FBSD_SIZEOF_VFPREGSET, |
212 | &arm_fbsd_vfpregset, "VFP floating-point", cb_data); | |
7176dfd2 JB |
213 | } |
214 | ||
82d23ca8 | 215 | /* See arm-fbsd-tdep.h. */ |
7176dfd2 JB |
216 | |
217 | const struct target_desc * | |
82d23ca8 SM |
218 | arm_fbsd_read_description_auxv (const gdb::optional<gdb::byte_vector> &auxv, |
219 | target_ops *target, gdbarch *gdbarch, bool tls) | |
7176dfd2 JB |
220 | { |
221 | CORE_ADDR arm_hwcap = 0; | |
222 | ||
82d23ca8 SM |
223 | if (!auxv.has_value () |
224 | || target_auxv_search (*auxv, target, gdbarch, AT_FREEBSD_HWCAP, | |
225 | &arm_hwcap) != 1) | |
099fbce0 | 226 | return arm_read_description (ARM_FP_TYPE_NONE, tls); |
7176dfd2 JB |
227 | |
228 | if (arm_hwcap & HWCAP_VFP) | |
229 | { | |
230 | if (arm_hwcap & HWCAP_NEON) | |
d105cce5 | 231 | return aarch32_read_description (); |
7176dfd2 | 232 | else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPD32)) |
01add95b | 233 | == (HWCAP_VFPv3 | HWCAP_VFPD32)) |
099fbce0 | 234 | return arm_read_description (ARM_FP_TYPE_VFPV3, tls); |
7176dfd2 | 235 | else |
099fbce0 | 236 | return arm_read_description (ARM_FP_TYPE_VFPV2, tls); |
7176dfd2 JB |
237 | } |
238 | ||
099fbce0 | 239 | return arm_read_description (ARM_FP_TYPE_NONE, tls); |
7176dfd2 JB |
240 | } |
241 | ||
82d23ca8 SM |
242 | /* See arm-fbsd-tdep.h. */ |
243 | ||
244 | const struct target_desc * | |
245 | arm_fbsd_read_description_auxv (bool tls) | |
246 | { | |
247 | gdb::optional<gdb::byte_vector> auxv = target_read_auxv (); | |
248 | return arm_fbsd_read_description_auxv (auxv, | |
249 | current_inferior ()->top_target (), | |
250 | current_inferior ()->gdbarch, | |
251 | tls); | |
252 | } | |
253 | ||
7176dfd2 JB |
254 | /* Implement the "core_read_description" gdbarch method. */ |
255 | ||
256 | static const struct target_desc * | |
257 | arm_fbsd_core_read_description (struct gdbarch *gdbarch, | |
258 | struct target_ops *target, | |
259 | bfd *abfd) | |
260 | { | |
099fbce0 JB |
261 | asection *tls = bfd_get_section_by_name (abfd, ".reg-aarch-tls"); |
262 | ||
1639fab3 | 263 | gdb::optional<gdb::byte_vector> auxv = target_read_auxv_raw (target); |
82d23ca8 | 264 | return arm_fbsd_read_description_auxv (auxv, target, gdbarch, tls != nullptr); |
7176dfd2 JB |
265 | } |
266 | ||
2e686a74 JB |
267 | /* Implement the get_thread_local_address gdbarch method. */ |
268 | ||
269 | static CORE_ADDR | |
270 | arm_fbsd_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, | |
271 | CORE_ADDR lm_addr, CORE_ADDR offset) | |
272 | { | |
08106042 | 273 | arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
2e686a74 JB |
274 | struct regcache *regcache; |
275 | ||
276 | regcache = get_thread_arch_regcache (current_inferior ()->process_target (), | |
277 | ptid, gdbarch); | |
278 | ||
279 | target_fetch_registers (regcache, tdep->tls_regnum); | |
280 | ||
281 | ULONGEST tpidruro; | |
282 | if (regcache->cooked_read (tdep->tls_regnum, &tpidruro) != REG_VALID) | |
283 | error (_("Unable to fetch %%tpidruro")); | |
284 | ||
285 | /* %tpidruro points to the TCB whose first member is the dtv | |
286 | pointer. */ | |
287 | CORE_ADDR dtv_addr = tpidruro; | |
288 | return fbsd_get_thread_local_address (gdbarch, dtv_addr, lm_addr, offset); | |
289 | } | |
290 | ||
7176dfd2 JB |
291 | /* Implement the 'init_osabi' method of struct gdb_osabi_handler. */ |
292 | ||
293 | static void | |
294 | arm_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | |
295 | { | |
08106042 | 296 | arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
7176dfd2 JB |
297 | |
298 | /* Generic FreeBSD support. */ | |
299 | fbsd_init_abi (info, gdbarch); | |
300 | ||
301 | if (tdep->fp_model == ARM_FLOAT_AUTO) | |
302 | tdep->fp_model = ARM_FLOAT_SOFT_VFP; | |
303 | ||
304 | tramp_frame_prepend_unwinder (gdbarch, &arm_fbsd_sigframe); | |
305 | ||
306 | set_solib_svr4_fetch_link_map_offsets | |
307 | (gdbarch, svr4_ilp32_fetch_link_map_offsets); | |
308 | ||
309 | tdep->jb_pc = 24; | |
310 | tdep->jb_elt_size = 4; | |
311 | ||
312 | set_gdbarch_iterate_over_regset_sections | |
313 | (gdbarch, arm_fbsd_iterate_over_regset_sections); | |
314 | set_gdbarch_core_read_description (gdbarch, arm_fbsd_core_read_description); | |
315 | ||
2e686a74 JB |
316 | if (tdep->tls_regnum > 0) |
317 | { | |
318 | set_gdbarch_fetch_tls_load_module_address (gdbarch, | |
319 | svr4_fetch_objfile_link_map); | |
320 | set_gdbarch_get_thread_local_address (gdbarch, | |
321 | arm_fbsd_get_thread_local_address); | |
322 | } | |
323 | ||
7176dfd2 JB |
324 | /* Single stepping. */ |
325 | set_gdbarch_software_single_step (gdbarch, arm_software_single_step); | |
326 | } | |
327 | ||
6c265988 | 328 | void _initialize_arm_fbsd_tdep (); |
7176dfd2 | 329 | void |
6c265988 | 330 | _initialize_arm_fbsd_tdep () |
7176dfd2 JB |
331 | { |
332 | gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_FREEBSD, | |
333 | arm_fbsd_init_abi); | |
334 | } |