/* Target-dependent code for FreeBSD/i386. Copyright (C) 2003-2021 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "defs.h" #include "arch-utils.h" #include "gdbcore.h" #include "osabi.h" #include "regcache.h" #include "regset.h" #include "i386-fbsd-tdep.h" #include "gdbsupport/x86-xstate.h" #include "i386-tdep.h" #include "i387-tdep.h" #include "fbsd-tdep.h" #include "solib-svr4.h" #include "inferior.h" /* Support for signal handlers. */ /* Return whether THIS_FRAME corresponds to a FreeBSD sigtramp routine. */ /* FreeBSD/i386 supports three different signal trampolines, one for versions before 4.0, a second for 4.x, and a third for 5.0 and later. To complicate matters, FreeBSD/i386 binaries running under an amd64 kernel use a different set of trampolines. These trampolines differ from the i386 kernel trampolines in that they omit a middle section that conditionally restores %gs. */ static const gdb_byte i386fbsd_sigtramp_start[] = { 0x8d, 0x44, 0x24, 0x20, /* lea SIGF_UC(%esp),%eax */ 0x50 /* pushl %eax */ }; static const gdb_byte i386fbsd_sigtramp_middle[] = { 0xf7, 0x40, 0x54, 0x00, 0x00, 0x02, 0x00, /* testl $PSL_VM,UC_EFLAGS(%eax) */ 0x75, 0x03, /* jne +3 */ 0x8e, 0x68, 0x14 /* mov UC_GS(%eax),%gs */ }; static const gdb_byte i386fbsd_sigtramp_end[] = { 0xb8, 0xa1, 0x01, 0x00, 0x00, /* movl $SYS_sigreturn,%eax */ 0x50, /* pushl %eax */ 0xcd, 0x80 /* int $0x80 */ }; static const gdb_byte i386fbsd_freebsd4_sigtramp_start[] = { 0x8d, 0x44, 0x24, 0x14, /* lea SIGF_UC4(%esp),%eax */ 0x50 /* pushl %eax */ }; static const gdb_byte i386fbsd_freebsd4_sigtramp_middle[] = { 0xf7, 0x40, 0x54, 0x00, 0x00, 0x02, 0x00, /* testl $PSL_VM,UC4_EFLAGS(%eax) */ 0x75, 0x03, /* jne +3 */ 0x8e, 0x68, 0x14 /* mov UC4_GS(%eax),%gs */ }; static const gdb_byte i386fbsd_freebsd4_sigtramp_end[] = { 0xb8, 0x58, 0x01, 0x00, 0x00, /* movl $344,%eax */ 0x50, /* pushl %eax */ 0xcd, 0x80 /* int $0x80 */ }; static const gdb_byte i386fbsd_osigtramp_start[] = { 0x8d, 0x44, 0x24, 0x14, /* lea SIGF_SC(%esp),%eax */ 0x50 /* pushl %eax */ }; static const gdb_byte i386fbsd_osigtramp_middle[] = { 0xf7, 0x40, 0x18, 0x00, 0x00, 0x02, 0x00, /* testl $PSL_VM,SC_PS(%eax) */ 0x75, 0x03, /* jne +3 */ 0x8e, 0x68, 0x44 /* mov SC_GS(%eax),%gs */ }; static const gdb_byte i386fbsd_osigtramp_end[] = { 0xb8, 0x67, 0x00, 0x00, 0x00, /* movl $103,%eax */ 0x50, /* pushl %eax */ 0xcd, 0x80 /* int $0x80 */ }; /* The three different trampolines are all the same size. */ gdb_static_assert (sizeof i386fbsd_sigtramp_start == sizeof i386fbsd_freebsd4_sigtramp_start); gdb_static_assert (sizeof i386fbsd_sigtramp_start == sizeof i386fbsd_osigtramp_start); gdb_static_assert (sizeof i386fbsd_sigtramp_middle == sizeof i386fbsd_freebsd4_sigtramp_middle); gdb_static_assert (sizeof i386fbsd_sigtramp_middle == sizeof i386fbsd_osigtramp_middle); gdb_static_assert (sizeof i386fbsd_sigtramp_end == sizeof i386fbsd_freebsd4_sigtramp_end); gdb_static_assert (sizeof i386fbsd_sigtramp_end == sizeof i386fbsd_osigtramp_end); /* We assume that the middle is the largest chunk below. */ gdb_static_assert (sizeof i386fbsd_sigtramp_middle > sizeof i386fbsd_sigtramp_start); gdb_static_assert (sizeof i386fbsd_sigtramp_middle > sizeof i386fbsd_sigtramp_end); static int i386fbsd_sigtramp_p (struct frame_info *this_frame) { CORE_ADDR pc = get_frame_pc (this_frame); gdb_byte buf[sizeof i386fbsd_sigtramp_middle]; const gdb_byte *middle, *end; /* Look for a matching start. */ if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof i386fbsd_sigtramp_start)) return 0; if (memcmp (buf, i386fbsd_sigtramp_start, sizeof i386fbsd_sigtramp_start) == 0) { middle = i386fbsd_sigtramp_middle; end = i386fbsd_sigtramp_end; } else if (memcmp (buf, i386fbsd_freebsd4_sigtramp_start, sizeof i386fbsd_freebsd4_sigtramp_start) == 0) { middle = i386fbsd_freebsd4_sigtramp_middle; end = i386fbsd_freebsd4_sigtramp_end; } else if (memcmp (buf, i386fbsd_osigtramp_start, sizeof i386fbsd_osigtramp_start) == 0) { middle = i386fbsd_osigtramp_middle; end = i386fbsd_osigtramp_end; } else return 0; /* Since the end is shorter than the middle, check for a matching end next. */ pc += sizeof i386fbsd_sigtramp_start; if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof i386fbsd_sigtramp_end)) return 0; if (memcmp (buf, end, sizeof i386fbsd_sigtramp_end) == 0) return 1; /* If the end didn't match, check for a matching middle. */ if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof i386fbsd_sigtramp_middle)) return 0; if (memcmp (buf, middle, sizeof i386fbsd_sigtramp_middle) != 0) return 0; /* The middle matched, check for a matching end. */ pc += sizeof i386fbsd_sigtramp_middle; if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof i386fbsd_sigtramp_end)) return 0; if (memcmp (buf, end, sizeof i386fbsd_sigtramp_end) != 0) return 0; return 1; } /* FreeBSD 3.0-RELEASE or later. */ /* From . */ static int i386fbsd_r_reg_offset[] = { 9 * 4, 8 * 4, 7 * 4, 6 * 4, /* %eax, %ecx, %edx, %ebx */ 15 * 4, 4 * 4, /* %esp, %ebp */ 3 * 4, 2 * 4, /* %esi, %edi */ 12 * 4, 14 * 4, /* %eip, %eflags */ 13 * 4, 16 * 4, /* %cs, %ss */ 1 * 4, 0 * 4, -1, -1 /* %ds, %es, %fs, %gs */ }; /* Sigtramp routine location. */ CORE_ADDR i386fbsd_sigtramp_start_addr; CORE_ADDR i386fbsd_sigtramp_end_addr; /* From . */ int i386fbsd_sc_reg_offset[] = { 8 + 14 * 4, /* %eax */ 8 + 13 * 4, /* %ecx */ 8 + 12 * 4, /* %edx */ 8 + 11 * 4, /* %ebx */ 8 + 0 * 4, /* %esp */ 8 + 1 * 4, /* %ebp */ 8 + 10 * 4, /* %esi */ 8 + 9 * 4, /* %edi */ 8 + 3 * 4, /* %eip */ 8 + 4 * 4, /* %eflags */ 8 + 7 * 4, /* %cs */ 8 + 8 * 4, /* %ss */ 8 + 6 * 4, /* %ds */ 8 + 5 * 4, /* %es */ 8 + 15 * 4, /* %fs */ 8 + 16 * 4 /* %gs */ }; /* Get XSAVE extended state xcr0 from core dump. */ uint64_t i386fbsd_core_read_xcr0 (bfd *abfd) { asection *xstate = bfd_get_section_by_name (abfd, ".reg-xstate"); uint64_t xcr0; if (xstate) { size_t size = bfd_section_size (xstate); /* Check extended state size. */ if (size < X86_XSTATE_AVX_SIZE) xcr0 = X86_XSTATE_SSE_MASK; else { char contents[8]; if (! bfd_get_section_contents (abfd, xstate, contents, I386_FBSD_XSAVE_XCR0_OFFSET, 8)) { warning (_("Couldn't read `xcr0' bytes from " "`.reg-xstate' section in core file.")); return X86_XSTATE_SSE_MASK; } xcr0 = bfd_get_64 (abfd, contents); } } else xcr0 = X86_XSTATE_SSE_MASK; return xcr0; } /* Implement the core_read_description gdbarch method. */ static const struct target_desc * i386fbsd_core_read_description (struct gdbarch *gdbarch, struct target_ops *target, bfd *abfd) { return i386_target_description (i386fbsd_core_read_xcr0 (abfd), true); } /* Similar to i386_supply_fpregset, but use XSAVE extended state. */ static void i386fbsd_supply_xstateregset (const struct regset *regset, struct regcache *regcache, int regnum, const void *xstateregs, size_t len) { i387_supply_xsave (regcache, regnum, xstateregs); } /* Similar to i386_collect_fpregset, but use XSAVE extended state. */ static void i386fbsd_collect_xstateregset (const struct regset *regset, const struct regcache *regcache, int regnum, void *xstateregs, size_t len) { i387_collect_xsave (regcache, regnum, xstateregs, 1); } /* Register set definitions. */ static const struct regset i386fbsd_xstateregset = { NULL, i386fbsd_supply_xstateregset, i386fbsd_collect_xstateregset }; /* Iterate over core file register note sections. */ static void i386fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); cb (".reg", tdep->sizeof_gregset, tdep->sizeof_gregset, &i386_gregset, NULL, cb_data); cb (".reg2", tdep->sizeof_fpregset, tdep->sizeof_fpregset, &i386_fpregset, NULL, cb_data); if (tdep->xcr0 & X86_XSTATE_AVX) cb (".reg-xstate", X86_XSTATE_SIZE (tdep->xcr0), X86_XSTATE_SIZE (tdep->xcr0), &i386fbsd_xstateregset, "XSAVE extended state", cb_data); } /* Implement the get_thread_local_address gdbarch method. */ static CORE_ADDR i386fbsd_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, CORE_ADDR lm_addr, CORE_ADDR offset) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); struct regcache *regcache; if (tdep->fsbase_regnum == -1) error (_("Unable to fetch %%gsbase")); regcache = get_thread_arch_regcache (current_inferior ()->process_target (), ptid, gdbarch); target_fetch_registers (regcache, tdep->fsbase_regnum + 1); ULONGEST gsbase; if (regcache->cooked_read (tdep->fsbase_regnum + 1, &gsbase) != REG_VALID) error (_("Unable to fetch %%gsbase")); CORE_ADDR dtv_addr = gsbase + gdbarch_ptr_bit (gdbarch) / 8; return fbsd_get_thread_local_address (gdbarch, dtv_addr, lm_addr, offset); } static void i386fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); /* Obviously FreeBSD is BSD-based. */ i386bsd_init_abi (info, gdbarch); /* FreeBSD has a different `struct reg', and reserves some space for its FPU emulator in `struct fpreg'. */ tdep->gregset_reg_offset = i386fbsd_r_reg_offset; tdep->gregset_num_regs = ARRAY_SIZE (i386fbsd_r_reg_offset); tdep->sizeof_gregset = 18 * 4; tdep->sizeof_fpregset = 176; /* FreeBSD uses -freg-struct-return by default. */ tdep->struct_return = reg_struct_return; tdep->sigtramp_p = i386fbsd_sigtramp_p; /* FreeBSD uses a different memory layout. */ tdep->sigtramp_start = i386fbsd_sigtramp_start_addr; tdep->sigtramp_end = i386fbsd_sigtramp_end_addr; /* FreeBSD has a more complete `struct sigcontext'. */ tdep->sc_reg_offset = i386fbsd_sc_reg_offset; tdep->sc_num_regs = ARRAY_SIZE (i386fbsd_sc_reg_offset); i386_elf_init_abi (info, gdbarch); /* FreeBSD uses SVR4-style shared libraries. */ set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_ilp32_fetch_link_map_offsets); } /* FreeBSD 4.0-RELEASE or later. */ /* From . */ static int i386fbsd4_r_reg_offset[] = { 10 * 4, 9 * 4, 8 * 4, 7 * 4, /* %eax, %ecx, %edx, %ebx */ 16 * 4, 5 * 4, /* %esp, %ebp */ 4 * 4, 3 * 4, /* %esi, %edi */ 13 * 4, 15 * 4, /* %eip, %eflags */ 14 * 4, 17 * 4, /* %cs, %ss */ 2 * 4, 1 * 4, 0 * 4, 18 * 4 /* %ds, %es, %fs, %gs */ }; /* From . */ int i386fbsd4_sc_reg_offset[] = { 20 + 11 * 4, /* %eax */ 20 + 10 * 4, /* %ecx */ 20 + 9 * 4, /* %edx */ 20 + 8 * 4, /* %ebx */ 20 + 17 * 4, /* %esp */ 20 + 6 * 4, /* %ebp */ 20 + 5 * 4, /* %esi */ 20 + 4 * 4, /* %edi */ 20 + 14 * 4, /* %eip */ 20 + 16 * 4, /* %eflags */ 20 + 15 * 4, /* %cs */ 20 + 18 * 4, /* %ss */ 20 + 3 * 4, /* %ds */ 20 + 2 * 4, /* %es */ 20 + 1 * 4, /* %fs */ 20 + 0 * 4 /* %gs */ }; static void i386fbsd4_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); /* Generic FreeBSD support. */ fbsd_init_abi (info, gdbarch); /* Inherit stuff from older releases. We assume that FreeBSD 4.0-RELEASE always uses ELF. */ i386fbsd_init_abi (info, gdbarch); /* FreeBSD 4.0 introduced a new `struct reg'. */ tdep->gregset_reg_offset = i386fbsd4_r_reg_offset; tdep->gregset_num_regs = ARRAY_SIZE (i386fbsd4_r_reg_offset); tdep->sizeof_gregset = 19 * 4; /* FreeBSD 4.0 introduced a new `struct sigcontext'. */ tdep->sc_reg_offset = i386fbsd4_sc_reg_offset; tdep->sc_num_regs = ARRAY_SIZE (i386fbsd4_sc_reg_offset); tdep->xsave_xcr0_offset = I386_FBSD_XSAVE_XCR0_OFFSET; /* Iterate over core file register note sections. */ set_gdbarch_iterate_over_regset_sections (gdbarch, i386fbsd_iterate_over_regset_sections); set_gdbarch_core_read_description (gdbarch, i386fbsd_core_read_description); set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); set_gdbarch_get_thread_local_address (gdbarch, i386fbsd_get_thread_local_address); } void _initialize_i386fbsd_tdep (); void _initialize_i386fbsd_tdep () { gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_FREEBSD, i386fbsd4_init_abi); }