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