| /* Target-dependent code for FreeBSD, architecture-independent. |
| |
| Copyright (C) 2002-2024 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 <http://www.gnu.org/licenses/>. */ |
| |
| #include "auxv.h" |
| #include "exceptions.h" |
| #include "extract-store-integer.h" |
| #include "gdbcore.h" |
| #include "inferior.h" |
| #include "objfiles.h" |
| #include "regcache.h" |
| #include "regset.h" |
| #include "gdbthread.h" |
| #include "objfiles.h" |
| #include "xml-syscall.h" |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| |
| #include "elf-bfd.h" |
| #include "fbsd-tdep.h" |
| #include "gcore-elf.h" |
| |
| /* This enum is derived from FreeBSD's <sys/signal.h>. */ |
| |
| enum |
| { |
| FREEBSD_SIGHUP = 1, |
| FREEBSD_SIGINT = 2, |
| FREEBSD_SIGQUIT = 3, |
| FREEBSD_SIGILL = 4, |
| FREEBSD_SIGTRAP = 5, |
| FREEBSD_SIGABRT = 6, |
| FREEBSD_SIGEMT = 7, |
| FREEBSD_SIGFPE = 8, |
| FREEBSD_SIGKILL = 9, |
| FREEBSD_SIGBUS = 10, |
| FREEBSD_SIGSEGV = 11, |
| FREEBSD_SIGSYS = 12, |
| FREEBSD_SIGPIPE = 13, |
| FREEBSD_SIGALRM = 14, |
| FREEBSD_SIGTERM = 15, |
| FREEBSD_SIGURG = 16, |
| FREEBSD_SIGSTOP = 17, |
| FREEBSD_SIGTSTP = 18, |
| FREEBSD_SIGCONT = 19, |
| FREEBSD_SIGCHLD = 20, |
| FREEBSD_SIGTTIN = 21, |
| FREEBSD_SIGTTOU = 22, |
| FREEBSD_SIGIO = 23, |
| FREEBSD_SIGXCPU = 24, |
| FREEBSD_SIGXFSZ = 25, |
| FREEBSD_SIGVTALRM = 26, |
| FREEBSD_SIGPROF = 27, |
| FREEBSD_SIGWINCH = 28, |
| FREEBSD_SIGINFO = 29, |
| FREEBSD_SIGUSR1 = 30, |
| FREEBSD_SIGUSR2 = 31, |
| FREEBSD_SIGTHR = 32, |
| FREEBSD_SIGLIBRT = 33, |
| FREEBSD_SIGRTMIN = 65, |
| FREEBSD_SIGRTMAX = 126, |
| }; |
| |
| /* Constants for values of si_code as defined in FreeBSD's |
| <sys/signal.h>. */ |
| |
| #define FBSD_SI_USER 0x10001 |
| #define FBSD_SI_QUEUE 0x10002 |
| #define FBSD_SI_TIMER 0x10003 |
| #define FBSD_SI_ASYNCIO 0x10004 |
| #define FBSD_SI_MESGQ 0x10005 |
| #define FBSD_SI_KERNEL 0x10006 |
| #define FBSD_SI_LWP 0x10007 |
| |
| #define FBSD_ILL_ILLOPC 1 |
| #define FBSD_ILL_ILLOPN 2 |
| #define FBSD_ILL_ILLADR 3 |
| #define FBSD_ILL_ILLTRP 4 |
| #define FBSD_ILL_PRVOPC 5 |
| #define FBSD_ILL_PRVREG 6 |
| #define FBSD_ILL_COPROC 7 |
| #define FBSD_ILL_BADSTK 8 |
| |
| #define FBSD_BUS_ADRALN 1 |
| #define FBSD_BUS_ADRERR 2 |
| #define FBSD_BUS_OBJERR 3 |
| #define FBSD_BUS_OOMERR 100 |
| |
| #define FBSD_SEGV_MAPERR 1 |
| #define FBSD_SEGV_ACCERR 2 |
| #define FBSD_SEGV_PKUERR 100 |
| |
| #define FBSD_FPE_INTOVF 1 |
| #define FBSD_FPE_INTDIV 2 |
| #define FBSD_FPE_FLTDIV 3 |
| #define FBSD_FPE_FLTOVF 4 |
| #define FBSD_FPE_FLTUND 5 |
| #define FBSD_FPE_FLTRES 6 |
| #define FBSD_FPE_FLTINV 7 |
| #define FBSD_FPE_FLTSUB 8 |
| |
| #define FBSD_TRAP_BRKPT 1 |
| #define FBSD_TRAP_TRACE 2 |
| #define FBSD_TRAP_DTRACE 3 |
| #define FBSD_TRAP_CAP 4 |
| |
| #define FBSD_CLD_EXITED 1 |
| #define FBSD_CLD_KILLED 2 |
| #define FBSD_CLD_DUMPED 3 |
| #define FBSD_CLD_TRAPPED 4 |
| #define FBSD_CLD_STOPPED 5 |
| #define FBSD_CLD_CONTINUED 6 |
| |
| #define FBSD_POLL_IN 1 |
| #define FBSD_POLL_OUT 2 |
| #define FBSD_POLL_MSG 3 |
| #define FBSD_POLL_ERR 4 |
| #define FBSD_POLL_PRI 5 |
| #define FBSD_POLL_HUP 6 |
| |
| /* FreeBSD kernels 12.0 and later include a copy of the |
| 'ptrace_lwpinfo' structure returned by the PT_LWPINFO ptrace |
| operation in an ELF core note (NT_FREEBSD_PTLWPINFO) for each LWP. |
| The constants below define the offset of field members and flags in |
| this structure used by methods in this file. Note that the |
| 'ptrace_lwpinfo' struct in the note is preceded by a 4 byte integer |
| containing the size of the structure. */ |
| |
| #define LWPINFO_OFFSET 0x4 |
| |
| /* Offsets in ptrace_lwpinfo. */ |
| #define LWPINFO_PL_FLAGS 0x8 |
| #define LWPINFO64_PL_SIGINFO 0x30 |
| #define LWPINFO32_PL_SIGINFO 0x2c |
| |
| /* Flags in pl_flags. */ |
| #define PL_FLAG_SI 0x20 /* siginfo is valid */ |
| |
| /* Sizes of siginfo_t. */ |
| #define SIZE64_SIGINFO_T 80 |
| #define SIZE32_SIGINFO_T 64 |
| |
| /* Offsets in data structure used in NT_FREEBSD_PROCSTAT_VMMAP core |
| dump notes. See <sys/user.h> for the definition of struct |
| kinfo_vmentry. This data structure should have the same layout on |
| all architectures. |
| |
| Note that FreeBSD 7.0 used an older version of this structure |
| (struct kinfo_vmentry), but the NT_FREEBSD_PROCSTAT_VMMAP core |
| dump note wasn't introduced until FreeBSD 9.2. As a result, the |
| core dump note has always used the 7.1 and later structure |
| format. */ |
| |
| #define KVE_STRUCTSIZE 0x0 |
| #define KVE_START 0x8 |
| #define KVE_END 0x10 |
| #define KVE_OFFSET 0x18 |
| #define KVE_FLAGS 0x2c |
| #define KVE_PROTECTION 0x38 |
| #define KVE_PATH 0x88 |
| |
| /* Flags in the 'kve_protection' field in struct kinfo_vmentry. These |
| match the KVME_PROT_* constants in <sys/user.h>. */ |
| |
| #define KINFO_VME_PROT_READ 0x00000001 |
| #define KINFO_VME_PROT_WRITE 0x00000002 |
| #define KINFO_VME_PROT_EXEC 0x00000004 |
| |
| /* Flags in the 'kve_flags' field in struct kinfo_vmentry. These |
| match the KVME_FLAG_* constants in <sys/user.h>. */ |
| |
| #define KINFO_VME_FLAG_COW 0x00000001 |
| #define KINFO_VME_FLAG_NEEDS_COPY 0x00000002 |
| #define KINFO_VME_FLAG_NOCOREDUMP 0x00000004 |
| #define KINFO_VME_FLAG_SUPER 0x00000008 |
| #define KINFO_VME_FLAG_GROWS_UP 0x00000010 |
| #define KINFO_VME_FLAG_GROWS_DOWN 0x00000020 |
| |
| /* Offsets in data structure used in NT_FREEBSD_PROCSTAT_FILES core |
| dump notes. See <sys/user.h> for the definition of struct |
| kinfo_file. This data structure should have the same layout on all |
| architectures. |
| |
| Note that FreeBSD 7.0 used an older version of this structure |
| (struct kinfo_ofile), but the NT_FREEBSD_PROCSTAT_FILES core dump |
| note wasn't introduced until FreeBSD 9.2. As a result, the core |
| dump note has always used the 7.1 and later structure format. */ |
| |
| #define KF_STRUCTSIZE 0x0 |
| #define KF_TYPE 0x4 |
| #define KF_FD 0x8 |
| #define KF_FLAGS 0x10 |
| #define KF_OFFSET 0x18 |
| #define KF_VNODE_TYPE 0x20 |
| #define KF_SOCK_DOMAIN 0x24 |
| #define KF_SOCK_TYPE 0x28 |
| #define KF_SOCK_PROTOCOL 0x2c |
| #define KF_SA_LOCAL 0x30 |
| #define KF_SA_PEER 0xb0 |
| #define KF_PATH 0x170 |
| |
| /* Constants for the 'kf_type' field in struct kinfo_file. These |
| match the KF_TYPE_* constants in <sys/user.h>. */ |
| |
| #define KINFO_FILE_TYPE_VNODE 1 |
| #define KINFO_FILE_TYPE_SOCKET 2 |
| #define KINFO_FILE_TYPE_PIPE 3 |
| #define KINFO_FILE_TYPE_FIFO 4 |
| #define KINFO_FILE_TYPE_KQUEUE 5 |
| #define KINFO_FILE_TYPE_CRYPTO 6 |
| #define KINFO_FILE_TYPE_MQUEUE 7 |
| #define KINFO_FILE_TYPE_SHM 8 |
| #define KINFO_FILE_TYPE_SEM 9 |
| #define KINFO_FILE_TYPE_PTS 10 |
| #define KINFO_FILE_TYPE_PROCDESC 11 |
| |
| /* Special values for the 'kf_fd' field in struct kinfo_file. These |
| match the KF_FD_TYPE_* constants in <sys/user.h>. */ |
| |
| #define KINFO_FILE_FD_TYPE_CWD -1 |
| #define KINFO_FILE_FD_TYPE_ROOT -2 |
| #define KINFO_FILE_FD_TYPE_JAIL -3 |
| #define KINFO_FILE_FD_TYPE_TRACE -4 |
| #define KINFO_FILE_FD_TYPE_TEXT -5 |
| #define KINFO_FILE_FD_TYPE_CTTY -6 |
| |
| /* Flags in the 'kf_flags' field in struct kinfo_file. These match |
| the KF_FLAG_* constants in <sys/user.h>. */ |
| |
| #define KINFO_FILE_FLAG_READ 0x00000001 |
| #define KINFO_FILE_FLAG_WRITE 0x00000002 |
| #define KINFO_FILE_FLAG_APPEND 0x00000004 |
| #define KINFO_FILE_FLAG_ASYNC 0x00000008 |
| #define KINFO_FILE_FLAG_FSYNC 0x00000010 |
| #define KINFO_FILE_FLAG_NONBLOCK 0x00000020 |
| #define KINFO_FILE_FLAG_DIRECT 0x00000040 |
| #define KINFO_FILE_FLAG_HASLOCK 0x00000080 |
| #define KINFO_FILE_FLAG_EXEC 0x00004000 |
| |
| /* Constants for the 'kf_vnode_type' field in struct kinfo_file. |
| These match the KF_VTYPE_* constants in <sys/user.h>. */ |
| |
| #define KINFO_FILE_VTYPE_VREG 1 |
| #define KINFO_FILE_VTYPE_VDIR 2 |
| #define KINFO_FILE_VTYPE_VCHR 4 |
| #define KINFO_FILE_VTYPE_VLNK 5 |
| #define KINFO_FILE_VTYPE_VSOCK 6 |
| #define KINFO_FILE_VTYPE_VFIFO 7 |
| |
| /* Constants for socket address families. These match AF_* constants |
| in <sys/socket.h>. */ |
| |
| #define FBSD_AF_UNIX 1 |
| #define FBSD_AF_INET 2 |
| #define FBSD_AF_INET6 28 |
| |
| /* Constants for socket types. These match SOCK_* constants in |
| <sys/socket.h>. */ |
| |
| #define FBSD_SOCK_STREAM 1 |
| #define FBSD_SOCK_DGRAM 2 |
| #define FBSD_SOCK_SEQPACKET 5 |
| |
| /* Constants for IP protocols. These match IPPROTO_* constants in |
| <netinet/in.h>. */ |
| |
| #define FBSD_IPPROTO_ICMP 1 |
| #define FBSD_IPPROTO_TCP 6 |
| #define FBSD_IPPROTO_UDP 17 |
| #define FBSD_IPPROTO_SCTP 132 |
| |
| /* Socket address structures. These have the same layout on all |
| FreeBSD architectures. In addition, multibyte fields such as IP |
| addresses are always stored in network byte order. */ |
| |
| struct fbsd_sockaddr_in |
| { |
| uint8_t sin_len; |
| uint8_t sin_family; |
| uint8_t sin_port[2]; |
| uint8_t sin_addr[4]; |
| char sin_zero[8]; |
| }; |
| |
| struct fbsd_sockaddr_in6 |
| { |
| uint8_t sin6_len; |
| uint8_t sin6_family; |
| uint8_t sin6_port[2]; |
| uint32_t sin6_flowinfo; |
| uint8_t sin6_addr[16]; |
| uint32_t sin6_scope_id; |
| }; |
| |
| struct fbsd_sockaddr_un |
| { |
| uint8_t sun_len; |
| uint8_t sun_family; |
| char sun_path[104]; |
| }; |
| |
| /* Number of 32-bit words in a signal set. This matches _SIG_WORDS in |
| <sys/_sigset.h> and is the same value on all architectures. */ |
| |
| #define SIG_WORDS 4 |
| |
| /* Offsets in data structure used in NT_FREEBSD_PROCSTAT_PROC core |
| dump notes. See <sys/user.h> for the definition of struct |
| kinfo_proc. This data structure has different layouts on different |
| architectures mostly due to ILP32 vs LP64. However, FreeBSD/i386 |
| uses a 32-bit time_t while all other architectures use a 64-bit |
| time_t. |
| |
| The core dump note actually contains one kinfo_proc structure for |
| each thread, but all of the process-wide data can be obtained from |
| the first structure. One result of this note's format is that some |
| of the process-wide status available in the native target method |
| from the kern.proc.pid.<pid> sysctl such as ki_stat and ki_siglist |
| is not available from a core dump. Instead, the per-thread data |
| structures contain the value of these fields for individual |
| threads. */ |
| |
| struct kinfo_proc_layout |
| { |
| /* Offsets of struct kinfo_proc members. */ |
| int ki_layout; |
| int ki_pid; |
| int ki_ppid; |
| int ki_pgid; |
| int ki_tpgid; |
| int ki_sid; |
| int ki_tdev_freebsd11; |
| int ki_sigignore; |
| int ki_sigcatch; |
| int ki_uid; |
| int ki_ruid; |
| int ki_svuid; |
| int ki_rgid; |
| int ki_svgid; |
| int ki_ngroups; |
| int ki_groups; |
| int ki_size; |
| int ki_rssize; |
| int ki_tsize; |
| int ki_dsize; |
| int ki_ssize; |
| int ki_start; |
| int ki_nice; |
| int ki_comm; |
| int ki_tdev; |
| int ki_rusage; |
| int ki_rusage_ch; |
| |
| /* Offsets of struct rusage members. */ |
| int ru_utime; |
| int ru_stime; |
| int ru_maxrss; |
| int ru_minflt; |
| int ru_majflt; |
| }; |
| |
| const struct kinfo_proc_layout kinfo_proc_layout_32 = |
| { |
| .ki_layout = 0x4, |
| .ki_pid = 0x28, |
| .ki_ppid = 0x2c, |
| .ki_pgid = 0x30, |
| .ki_tpgid = 0x34, |
| .ki_sid = 0x38, |
| .ki_tdev_freebsd11 = 0x44, |
| .ki_sigignore = 0x68, |
| .ki_sigcatch = 0x78, |
| .ki_uid = 0x88, |
| .ki_ruid = 0x8c, |
| .ki_svuid = 0x90, |
| .ki_rgid = 0x94, |
| .ki_svgid = 0x98, |
| .ki_ngroups = 0x9c, |
| .ki_groups = 0xa0, |
| .ki_size = 0xe0, |
| .ki_rssize = 0xe4, |
| .ki_tsize = 0xec, |
| .ki_dsize = 0xf0, |
| .ki_ssize = 0xf4, |
| .ki_start = 0x118, |
| .ki_nice = 0x145, |
| .ki_comm = 0x17f, |
| .ki_tdev = 0x1f0, |
| .ki_rusage = 0x220, |
| .ki_rusage_ch = 0x278, |
| |
| .ru_utime = 0x0, |
| .ru_stime = 0x10, |
| .ru_maxrss = 0x20, |
| .ru_minflt = 0x30, |
| .ru_majflt = 0x34, |
| }; |
| |
| const struct kinfo_proc_layout kinfo_proc_layout_i386 = |
| { |
| .ki_layout = 0x4, |
| .ki_pid = 0x28, |
| .ki_ppid = 0x2c, |
| .ki_pgid = 0x30, |
| .ki_tpgid = 0x34, |
| .ki_sid = 0x38, |
| .ki_tdev_freebsd11 = 0x44, |
| .ki_sigignore = 0x68, |
| .ki_sigcatch = 0x78, |
| .ki_uid = 0x88, |
| .ki_ruid = 0x8c, |
| .ki_svuid = 0x90, |
| .ki_rgid = 0x94, |
| .ki_svgid = 0x98, |
| .ki_ngroups = 0x9c, |
| .ki_groups = 0xa0, |
| .ki_size = 0xe0, |
| .ki_rssize = 0xe4, |
| .ki_tsize = 0xec, |
| .ki_dsize = 0xf0, |
| .ki_ssize = 0xf4, |
| .ki_start = 0x118, |
| .ki_nice = 0x135, |
| .ki_comm = 0x16f, |
| .ki_tdev = 0x1e0, |
| .ki_rusage = 0x210, |
| .ki_rusage_ch = 0x258, |
| |
| .ru_utime = 0x0, |
| .ru_stime = 0x8, |
| .ru_maxrss = 0x10, |
| .ru_minflt = 0x20, |
| .ru_majflt = 0x24, |
| }; |
| |
| const struct kinfo_proc_layout kinfo_proc_layout_64 = |
| { |
| .ki_layout = 0x4, |
| .ki_pid = 0x48, |
| .ki_ppid = 0x4c, |
| .ki_pgid = 0x50, |
| .ki_tpgid = 0x54, |
| .ki_sid = 0x58, |
| .ki_tdev_freebsd11 = 0x64, |
| .ki_sigignore = 0x88, |
| .ki_sigcatch = 0x98, |
| .ki_uid = 0xa8, |
| .ki_ruid = 0xac, |
| .ki_svuid = 0xb0, |
| .ki_rgid = 0xb4, |
| .ki_svgid = 0xb8, |
| .ki_ngroups = 0xbc, |
| .ki_groups = 0xc0, |
| .ki_size = 0x100, |
| .ki_rssize = 0x108, |
| .ki_tsize = 0x118, |
| .ki_dsize = 0x120, |
| .ki_ssize = 0x128, |
| .ki_start = 0x150, |
| .ki_nice = 0x185, |
| .ki_comm = 0x1bf, |
| .ki_tdev = 0x230, |
| .ki_rusage = 0x260, |
| .ki_rusage_ch = 0x2f0, |
| |
| .ru_utime = 0x0, |
| .ru_stime = 0x10, |
| .ru_maxrss = 0x20, |
| .ru_minflt = 0x40, |
| .ru_majflt = 0x48, |
| }; |
| |
| struct fbsd_gdbarch_data |
| { |
| struct type *siginfo_type = nullptr; |
| }; |
| |
| static const registry<gdbarch>::key<fbsd_gdbarch_data> |
| fbsd_gdbarch_data_handle; |
| |
| static struct fbsd_gdbarch_data * |
| get_fbsd_gdbarch_data (struct gdbarch *gdbarch) |
| { |
| struct fbsd_gdbarch_data *result = fbsd_gdbarch_data_handle.get (gdbarch); |
| if (result == nullptr) |
| result = fbsd_gdbarch_data_handle.emplace (gdbarch); |
| return result; |
| } |
| |
| struct fbsd_pspace_data |
| { |
| /* Offsets in the runtime linker's 'Obj_Entry' structure. */ |
| LONGEST off_linkmap = 0; |
| LONGEST off_tlsindex = 0; |
| bool rtld_offsets_valid = false; |
| |
| /* vDSO mapping range. */ |
| struct mem_range vdso_range {}; |
| |
| /* Zero if the range hasn't been searched for, > 0 if a range was |
| found, or < 0 if a range was not found. */ |
| int vdso_range_p = 0; |
| }; |
| |
| /* Per-program-space data for FreeBSD architectures. */ |
| static const registry<program_space>::key<fbsd_pspace_data> |
| fbsd_pspace_data_handle; |
| |
| static struct fbsd_pspace_data * |
| get_fbsd_pspace_data (struct program_space *pspace) |
| { |
| struct fbsd_pspace_data *data; |
| |
| data = fbsd_pspace_data_handle.get (pspace); |
| if (data == NULL) |
| data = fbsd_pspace_data_handle.emplace (pspace); |
| |
| return data; |
| } |
| |
| /* This is how we want PTIDs from core files to be printed. */ |
| |
| static std::string |
| fbsd_core_pid_to_str (struct gdbarch *gdbarch, ptid_t ptid) |
| { |
| if (ptid.lwp () != 0) |
| return string_printf ("LWP %ld", ptid.lwp ()); |
| |
| return normal_pid_to_str (ptid); |
| } |
| |
| /* Extract the name assigned to a thread from a core. Returns the |
| string in a static buffer. */ |
| |
| static const char * |
| fbsd_core_thread_name (struct gdbarch *gdbarch, struct thread_info *thr) |
| { |
| static char buf[80]; |
| struct bfd_section *section; |
| bfd_size_type size; |
| |
| if (thr->ptid.lwp () != 0) |
| { |
| /* FreeBSD includes a NT_FREEBSD_THRMISC note for each thread |
| whose contents are defined by a "struct thrmisc" declared in |
| <sys/procfs.h> on FreeBSD. The per-thread name is stored as |
| a null-terminated string as the first member of the |
| structure. Rather than define the full structure here, just |
| extract the null-terminated name from the start of the |
| note. */ |
| thread_section_name section_name (".thrmisc", thr->ptid); |
| bfd *cbfd = current_program_space->core_bfd (); |
| |
| section = bfd_get_section_by_name (cbfd, section_name.c_str ()); |
| if (section != NULL && bfd_section_size (section) > 0) |
| { |
| /* Truncate the name if it is longer than "buf". */ |
| size = bfd_section_size (section); |
| if (size > sizeof buf - 1) |
| size = sizeof buf - 1; |
| if (bfd_get_section_contents (cbfd, section, buf, (file_ptr) 0, size) |
| && buf[0] != '\0') |
| { |
| buf[size] = '\0'; |
| |
| /* Note that each thread will report the process command |
| as its thread name instead of an empty name if a name |
| has not been set explicitly. Return a NULL name in |
| that case. */ |
| if (strcmp (buf, elf_tdata (cbfd)->core->program) != 0) |
| return buf; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* Implement the "core_xfer_siginfo" gdbarch method. */ |
| |
| static LONGEST |
| fbsd_core_xfer_siginfo (struct gdbarch *gdbarch, gdb_byte *readbuf, |
| ULONGEST offset, ULONGEST len) |
| { |
| size_t siginfo_size; |
| |
| if (gdbarch_long_bit (gdbarch) == 32) |
| siginfo_size = SIZE32_SIGINFO_T; |
| else |
| siginfo_size = SIZE64_SIGINFO_T; |
| if (offset > siginfo_size) |
| return -1; |
| |
| thread_section_name section_name (".note.freebsdcore.lwpinfo", inferior_ptid); |
| bfd *cbfd = current_program_space->core_bfd (); |
| asection *section = bfd_get_section_by_name (cbfd, section_name.c_str ()); |
| if (section == NULL) |
| return -1; |
| |
| gdb_byte buf[4]; |
| if (!bfd_get_section_contents (cbfd, section, buf, |
| LWPINFO_OFFSET + LWPINFO_PL_FLAGS, 4)) |
| return -1; |
| |
| int pl_flags = extract_signed_integer (buf, gdbarch_byte_order (gdbarch)); |
| if (!(pl_flags & PL_FLAG_SI)) |
| return -1; |
| |
| if (offset + len > siginfo_size) |
| len = siginfo_size - offset; |
| |
| ULONGEST siginfo_offset; |
| if (gdbarch_long_bit (gdbarch) == 32) |
| siginfo_offset = LWPINFO_OFFSET + LWPINFO32_PL_SIGINFO; |
| else |
| siginfo_offset = LWPINFO_OFFSET + LWPINFO64_PL_SIGINFO; |
| |
| if (!bfd_get_section_contents (cbfd, section, readbuf, |
| siginfo_offset + offset, len)) |
| return -1; |
| |
| return len; |
| } |
| |
| static int |
| find_signalled_thread (struct thread_info *info, void *data) |
| { |
| if (info->stop_signal () != GDB_SIGNAL_0 |
| && info->ptid.pid () == inferior_ptid.pid ()) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Return a byte_vector containing the contents of a core dump note |
| for the target object of type OBJECT. If STRUCTSIZE is non-zero, |
| the data is prefixed with a 32-bit integer size to match the format |
| used in FreeBSD NT_PROCSTAT_* notes. */ |
| |
| static std::optional<gdb::byte_vector> |
| fbsd_make_note_desc (enum target_object object, uint32_t structsize) |
| { |
| std::optional<gdb::byte_vector> buf = |
| target_read_alloc (current_inferior ()->top_target (), object, NULL); |
| if (!buf || buf->empty ()) |
| return {}; |
| |
| if (structsize == 0) |
| return buf; |
| |
| gdb::byte_vector desc (sizeof (structsize) + buf->size ()); |
| memcpy (desc.data (), &structsize, sizeof (structsize)); |
| std::copy (buf->begin (), buf->end (), desc.data () + sizeof (structsize)); |
| return desc; |
| } |
| |
| /* Create appropriate note sections for a corefile, returning them in |
| allocated memory. */ |
| |
| static gdb::unique_xmalloc_ptr<char> |
| fbsd_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size) |
| { |
| gdb::unique_xmalloc_ptr<char> note_data; |
| Elf_Internal_Ehdr *i_ehdrp; |
| struct thread_info *curr_thr, *signalled_thr; |
| |
| /* Put a "FreeBSD" label in the ELF header. */ |
| i_ehdrp = elf_elfheader (obfd); |
| i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; |
| |
| gdb_assert (gdbarch_iterate_over_regset_sections_p (gdbarch)); |
| |
| if (current_program_space->exec_filename () != nullptr) |
| { |
| const char *fname = lbasename (current_program_space->exec_filename ()); |
| std::string psargs = fname; |
| |
| const std::string &infargs = current_inferior ()->args (); |
| if (!infargs.empty ()) |
| psargs += ' ' + infargs; |
| |
| note_data.reset (elfcore_write_prpsinfo (obfd, note_data.release (), |
| note_size, fname, |
| psargs.c_str ())); |
| } |
| |
| /* Thread register information. */ |
| try |
| { |
| update_thread_list (); |
| } |
| catch (const gdb_exception_error &e) |
| { |
| exception_print (gdb_stderr, e); |
| } |
| |
| /* Like the kernel, prefer dumping the signalled thread first. |
| "First thread" is what tools use to infer the signalled thread. |
| In case there's more than one signalled thread, prefer the |
| current thread, if it is signalled. */ |
| curr_thr = inferior_thread (); |
| if (curr_thr->stop_signal () != GDB_SIGNAL_0) |
| signalled_thr = curr_thr; |
| else |
| { |
| signalled_thr = iterate_over_threads (find_signalled_thread, NULL); |
| if (signalled_thr == NULL) |
| signalled_thr = curr_thr; |
| } |
| |
| enum gdb_signal stop_signal = signalled_thr->stop_signal (); |
| gcore_elf_build_thread_register_notes (gdbarch, signalled_thr, stop_signal, |
| obfd, ¬e_data, note_size); |
| for (thread_info *thr : current_inferior ()->non_exited_threads ()) |
| { |
| if (thr == signalled_thr) |
| continue; |
| |
| gcore_elf_build_thread_register_notes (gdbarch, thr, stop_signal, |
| obfd, ¬e_data, note_size); |
| } |
| |
| /* Auxiliary vector. */ |
| uint32_t structsize = gdbarch_ptr_bit (gdbarch) / 4; /* Elf_Auxinfo */ |
| std::optional<gdb::byte_vector> note_desc = |
| fbsd_make_note_desc (TARGET_OBJECT_AUXV, structsize); |
| if (note_desc && !note_desc->empty ()) |
| { |
| note_data.reset (elfcore_write_note (obfd, note_data.release (), |
| note_size, "FreeBSD", |
| NT_FREEBSD_PROCSTAT_AUXV, |
| note_desc->data (), |
| note_desc->size ())); |
| if (!note_data) |
| return NULL; |
| } |
| |
| /* Virtual memory mappings. */ |
| note_desc = fbsd_make_note_desc (TARGET_OBJECT_FREEBSD_VMMAP, 0); |
| if (note_desc && !note_desc->empty ()) |
| { |
| note_data.reset (elfcore_write_note (obfd, note_data.release (), |
| note_size, "FreeBSD", |
| NT_FREEBSD_PROCSTAT_VMMAP, |
| note_desc->data (), |
| note_desc->size ())); |
| if (!note_data) |
| return NULL; |
| } |
| |
| note_desc = fbsd_make_note_desc (TARGET_OBJECT_FREEBSD_PS_STRINGS, 0); |
| if (note_desc && !note_desc->empty ()) |
| { |
| note_data.reset (elfcore_write_note (obfd, note_data.release (), |
| note_size, "FreeBSD", |
| NT_FREEBSD_PROCSTAT_PSSTRINGS, |
| note_desc->data (), |
| note_desc->size ())); |
| if (!note_data) |
| return NULL; |
| } |
| |
| /* Include the target description when possible. Some architectures |
| allow for per-thread gdbarch so we should really be emitting a tdesc |
| per-thread, however, we don't currently support reading in a |
| per-thread tdesc, so just emit the tdesc for the signalled thread. */ |
| gdbarch = target_thread_architecture (signalled_thr->ptid); |
| gcore_elf_make_tdesc_note (gdbarch, obfd, ¬e_data, note_size); |
| |
| return note_data; |
| } |
| |
| /* Helper function to generate the file descriptor description for a |
| single open file in 'info proc files'. */ |
| |
| static const char * |
| fbsd_file_fd (int kf_fd) |
| { |
| switch (kf_fd) |
| { |
| case KINFO_FILE_FD_TYPE_CWD: |
| return "cwd"; |
| case KINFO_FILE_FD_TYPE_ROOT: |
| return "root"; |
| case KINFO_FILE_FD_TYPE_JAIL: |
| return "jail"; |
| case KINFO_FILE_FD_TYPE_TRACE: |
| return "trace"; |
| case KINFO_FILE_FD_TYPE_TEXT: |
| return "text"; |
| case KINFO_FILE_FD_TYPE_CTTY: |
| return "ctty"; |
| default: |
| return int_string (kf_fd, 10, 1, 0, 0); |
| } |
| } |
| |
| /* Helper function to generate the file type for a single open file in |
| 'info proc files'. */ |
| |
| static const char * |
| fbsd_file_type (int kf_type, int kf_vnode_type) |
| { |
| switch (kf_type) |
| { |
| case KINFO_FILE_TYPE_VNODE: |
| switch (kf_vnode_type) |
| { |
| case KINFO_FILE_VTYPE_VREG: |
| return "file"; |
| case KINFO_FILE_VTYPE_VDIR: |
| return "dir"; |
| case KINFO_FILE_VTYPE_VCHR: |
| return "chr"; |
| case KINFO_FILE_VTYPE_VLNK: |
| return "link"; |
| case KINFO_FILE_VTYPE_VSOCK: |
| return "socket"; |
| case KINFO_FILE_VTYPE_VFIFO: |
| return "fifo"; |
| default: |
| { |
| char *str = get_print_cell (); |
| |
| xsnprintf (str, PRINT_CELL_SIZE, "vn:%d", kf_vnode_type); |
| return str; |
| } |
| } |
| case KINFO_FILE_TYPE_SOCKET: |
| return "socket"; |
| case KINFO_FILE_TYPE_PIPE: |
| return "pipe"; |
| case KINFO_FILE_TYPE_FIFO: |
| return "fifo"; |
| case KINFO_FILE_TYPE_KQUEUE: |
| return "kqueue"; |
| case KINFO_FILE_TYPE_CRYPTO: |
| return "crypto"; |
| case KINFO_FILE_TYPE_MQUEUE: |
| return "mqueue"; |
| case KINFO_FILE_TYPE_SHM: |
| return "shm"; |
| case KINFO_FILE_TYPE_SEM: |
| return "sem"; |
| case KINFO_FILE_TYPE_PTS: |
| return "pts"; |
| case KINFO_FILE_TYPE_PROCDESC: |
| return "proc"; |
| default: |
| return int_string (kf_type, 10, 1, 0, 0); |
| } |
| } |
| |
| /* Helper function to generate the file flags for a single open file in |
| 'info proc files'. */ |
| |
| static const char * |
| fbsd_file_flags (int kf_flags) |
| { |
| static char file_flags[10]; |
| |
| file_flags[0] = (kf_flags & KINFO_FILE_FLAG_READ) ? 'r' : '-'; |
| file_flags[1] = (kf_flags & KINFO_FILE_FLAG_WRITE) ? 'w' : '-'; |
| file_flags[2] = (kf_flags & KINFO_FILE_FLAG_EXEC) ? 'x' : '-'; |
| file_flags[3] = (kf_flags & KINFO_FILE_FLAG_APPEND) ? 'a' : '-'; |
| file_flags[4] = (kf_flags & KINFO_FILE_FLAG_ASYNC) ? 's' : '-'; |
| file_flags[5] = (kf_flags & KINFO_FILE_FLAG_FSYNC) ? 'f' : '-'; |
| file_flags[6] = (kf_flags & KINFO_FILE_FLAG_NONBLOCK) ? 'n' : '-'; |
| file_flags[7] = (kf_flags & KINFO_FILE_FLAG_DIRECT) ? 'd' : '-'; |
| file_flags[8] = (kf_flags & KINFO_FILE_FLAG_HASLOCK) ? 'l' : '-'; |
| file_flags[9] = '\0'; |
| |
| return file_flags; |
| } |
| |
| /* Helper function to generate the name of an IP protocol. */ |
| |
| static const char * |
| fbsd_ipproto (int protocol) |
| { |
| switch (protocol) |
| { |
| case FBSD_IPPROTO_ICMP: |
| return "icmp"; |
| case FBSD_IPPROTO_TCP: |
| return "tcp"; |
| case FBSD_IPPROTO_UDP: |
| return "udp"; |
| case FBSD_IPPROTO_SCTP: |
| return "sctp"; |
| default: |
| { |
| char *str = get_print_cell (); |
| |
| xsnprintf (str, PRINT_CELL_SIZE, "ip<%d>", protocol); |
| return str; |
| } |
| } |
| } |
| |
| /* Helper function to print out an IPv4 socket address. */ |
| |
| static void |
| fbsd_print_sockaddr_in (const void *sockaddr) |
| { |
| const struct fbsd_sockaddr_in *sin = |
| reinterpret_cast<const struct fbsd_sockaddr_in *> (sockaddr); |
| char buf[INET_ADDRSTRLEN]; |
| |
| if (inet_ntop (AF_INET, sin->sin_addr, buf, sizeof buf) == nullptr) |
| error (_("Failed to format IPv4 address")); |
| gdb_printf ("%s:%u", buf, |
| (sin->sin_port[0] << 8) | sin->sin_port[1]); |
| } |
| |
| /* Helper function to print out an IPv6 socket address. */ |
| |
| static void |
| fbsd_print_sockaddr_in6 (const void *sockaddr) |
| { |
| const struct fbsd_sockaddr_in6 *sin6 = |
| reinterpret_cast<const struct fbsd_sockaddr_in6 *> (sockaddr); |
| char buf[INET6_ADDRSTRLEN]; |
| |
| if (inet_ntop (AF_INET6, sin6->sin6_addr, buf, sizeof buf) == nullptr) |
| error (_("Failed to format IPv6 address")); |
| gdb_printf ("%s.%u", buf, |
| (sin6->sin6_port[0] << 8) | sin6->sin6_port[1]); |
| } |
| |
| /* See fbsd-tdep.h. */ |
| |
| void |
| fbsd_info_proc_files_header () |
| { |
| gdb_printf (_("Open files:\n\n")); |
| gdb_printf (" %6s %6s %10s %9s %s\n", |
| "FD", "Type", "Offset", "Flags ", "Name"); |
| } |
| |
| /* See fbsd-tdep.h. */ |
| |
| void |
| fbsd_info_proc_files_entry (int kf_type, int kf_fd, int kf_flags, |
| LONGEST kf_offset, int kf_vnode_type, |
| int kf_sock_domain, int kf_sock_type, |
| int kf_sock_protocol, const void *kf_sa_local, |
| const void *kf_sa_peer, const void *kf_path) |
| { |
| gdb_printf (" %6s %6s %10s %8s ", |
| fbsd_file_fd (kf_fd), |
| fbsd_file_type (kf_type, kf_vnode_type), |
| kf_offset > -1 ? hex_string (kf_offset) : "-", |
| fbsd_file_flags (kf_flags)); |
| if (kf_type == KINFO_FILE_TYPE_SOCKET) |
| { |
| switch (kf_sock_domain) |
| { |
| case FBSD_AF_UNIX: |
| { |
| switch (kf_sock_type) |
| { |
| case FBSD_SOCK_STREAM: |
| gdb_printf ("unix stream:"); |
| break; |
| case FBSD_SOCK_DGRAM: |
| gdb_printf ("unix dgram:"); |
| break; |
| case FBSD_SOCK_SEQPACKET: |
| gdb_printf ("unix seqpacket:"); |
| break; |
| default: |
| gdb_printf ("unix <%d>:", kf_sock_type); |
| break; |
| } |
| |
| /* For local sockets, print out the first non-nul path |
| rather than both paths. */ |
| const struct fbsd_sockaddr_un *saddr_un |
| = reinterpret_cast<const struct fbsd_sockaddr_un *> (kf_sa_local); |
| if (saddr_un->sun_path[0] == 0) |
| saddr_un = reinterpret_cast<const struct fbsd_sockaddr_un *> |
| (kf_sa_peer); |
| gdb_printf ("%s", saddr_un->sun_path); |
| break; |
| } |
| case FBSD_AF_INET: |
| gdb_printf ("%s4 ", fbsd_ipproto (kf_sock_protocol)); |
| fbsd_print_sockaddr_in (kf_sa_local); |
| gdb_printf (" -> "); |
| fbsd_print_sockaddr_in (kf_sa_peer); |
| break; |
| case FBSD_AF_INET6: |
| gdb_printf ("%s6 ", fbsd_ipproto (kf_sock_protocol)); |
| fbsd_print_sockaddr_in6 (kf_sa_local); |
| gdb_printf (" -> "); |
| fbsd_print_sockaddr_in6 (kf_sa_peer); |
| break; |
| } |
| } |
| else |
| gdb_printf ("%s", reinterpret_cast<const char *> (kf_path)); |
| gdb_printf ("\n"); |
| } |
| |
| /* Implement "info proc files" for a corefile. */ |
| |
| static void |
| fbsd_core_info_proc_files (struct gdbarch *gdbarch) |
| { |
| bfd *cbfd = current_program_space->core_bfd (); |
| asection *section = bfd_get_section_by_name (cbfd, ".note.freebsdcore.files"); |
| if (section == NULL) |
| { |
| warning (_("unable to find open files in core file")); |
| return; |
| } |
| |
| size_t note_size = bfd_section_size (section); |
| if (note_size < 4) |
| error (_("malformed core note - too short for header")); |
| |
| gdb::def_vector<unsigned char> contents (note_size); |
| if (!bfd_get_section_contents (cbfd, section, contents.data (), |
| 0, note_size)) |
| error (_("could not get core note contents")); |
| |
| unsigned char *descdata = contents.data (); |
| unsigned char *descend = descdata + note_size; |
| |
| /* Skip over the structure size. */ |
| descdata += 4; |
| |
| fbsd_info_proc_files_header (); |
| |
| while (descdata + KF_PATH < descend) |
| { |
| ULONGEST structsize = bfd_get_32 (cbfd, descdata + KF_STRUCTSIZE); |
| if (structsize < KF_PATH) |
| error (_("malformed core note - file structure too small")); |
| |
| LONGEST type = bfd_get_signed_32 (cbfd, descdata + KF_TYPE); |
| LONGEST fd = bfd_get_signed_32 (cbfd, descdata + KF_FD); |
| LONGEST flags = bfd_get_signed_32 (cbfd, descdata + KF_FLAGS); |
| LONGEST offset = bfd_get_signed_64 (cbfd, descdata + KF_OFFSET); |
| LONGEST vnode_type = bfd_get_signed_32 (cbfd, descdata + KF_VNODE_TYPE); |
| LONGEST sock_domain = bfd_get_signed_32 (cbfd, descdata + KF_SOCK_DOMAIN); |
| LONGEST sock_type = bfd_get_signed_32 (cbfd, descdata + KF_SOCK_TYPE); |
| LONGEST sock_protocol = bfd_get_signed_32 (cbfd, |
| descdata + KF_SOCK_PROTOCOL); |
| fbsd_info_proc_files_entry (type, fd, flags, offset, vnode_type, |
| sock_domain, sock_type, sock_protocol, |
| descdata + KF_SA_LOCAL, descdata + KF_SA_PEER, |
| descdata + KF_PATH); |
| |
| descdata += structsize; |
| } |
| } |
| |
| /* Helper function to generate mappings flags for a single VM map |
| entry in 'info proc mappings'. */ |
| |
| static const char * |
| fbsd_vm_map_entry_flags (int kve_flags, int kve_protection) |
| { |
| static char vm_flags[9]; |
| |
| vm_flags[0] = (kve_protection & KINFO_VME_PROT_READ) ? 'r' : '-'; |
| vm_flags[1] = (kve_protection & KINFO_VME_PROT_WRITE) ? 'w' : '-'; |
| vm_flags[2] = (kve_protection & KINFO_VME_PROT_EXEC) ? 'x' : '-'; |
| vm_flags[3] = ' '; |
| vm_flags[4] = (kve_flags & KINFO_VME_FLAG_COW) ? 'C' : '-'; |
| vm_flags[5] = (kve_flags & KINFO_VME_FLAG_NEEDS_COPY) ? 'N' : '-'; |
| vm_flags[6] = (kve_flags & KINFO_VME_FLAG_SUPER) ? 'S' : '-'; |
| vm_flags[7] = (kve_flags & KINFO_VME_FLAG_GROWS_UP) ? 'U' |
| : (kve_flags & KINFO_VME_FLAG_GROWS_DOWN) ? 'D' : '-'; |
| vm_flags[8] = '\0'; |
| |
| return vm_flags; |
| } |
| |
| /* See fbsd-tdep.h. */ |
| |
| void |
| fbsd_info_proc_mappings_header (int addr_bit) |
| { |
| gdb_printf (_("Mapped address spaces:\n\n")); |
| if (addr_bit == 64) |
| { |
| gdb_printf (" %18s %18s %10s %10s %9s %s\n", |
| "Start Addr", |
| " End Addr", |
| " Size", " Offset", "Flags ", "File"); |
| } |
| else |
| { |
| gdb_printf ("\t%10s %10s %10s %10s %9s %s\n", |
| "Start Addr", |
| " End Addr", |
| " Size", " Offset", "Flags ", "File"); |
| } |
| } |
| |
| /* See fbsd-tdep.h. */ |
| |
| void |
| fbsd_info_proc_mappings_entry (int addr_bit, ULONGEST kve_start, |
| ULONGEST kve_end, ULONGEST kve_offset, |
| int kve_flags, int kve_protection, |
| const void *kve_path) |
| { |
| if (addr_bit == 64) |
| { |
| gdb_printf (" %18s %18s %10s %10s %9s %s\n", |
| hex_string (kve_start), |
| hex_string (kve_end), |
| hex_string (kve_end - kve_start), |
| hex_string (kve_offset), |
| fbsd_vm_map_entry_flags (kve_flags, kve_protection), |
| reinterpret_cast<const char *> (kve_path)); |
| } |
| else |
| { |
| gdb_printf ("\t%10s %10s %10s %10s %9s %s\n", |
| hex_string (kve_start), |
| hex_string (kve_end), |
| hex_string (kve_end - kve_start), |
| hex_string (kve_offset), |
| fbsd_vm_map_entry_flags (kve_flags, kve_protection), |
| reinterpret_cast<const char *> (kve_path)); |
| } |
| } |
| |
| /* Implement "info proc mappings" for a corefile. */ |
| |
| static void |
| fbsd_core_info_proc_mappings (struct gdbarch *gdbarch) |
| { |
| asection *section; |
| unsigned char *descdata, *descend; |
| size_t note_size; |
| bfd *cbfd = current_program_space->core_bfd (); |
| |
| section = bfd_get_section_by_name (cbfd, ".note.freebsdcore.vmmap"); |
| if (section == NULL) |
| { |
| warning (_("unable to find mappings in core file")); |
| return; |
| } |
| |
| note_size = bfd_section_size (section); |
| if (note_size < 4) |
| error (_("malformed core note - too short for header")); |
| |
| gdb::def_vector<unsigned char> contents (note_size); |
| if (!bfd_get_section_contents (cbfd, section, contents.data (), 0, note_size)) |
| error (_("could not get core note contents")); |
| |
| descdata = contents.data (); |
| descend = descdata + note_size; |
| |
| /* Skip over the structure size. */ |
| descdata += 4; |
| |
| fbsd_info_proc_mappings_header (gdbarch_addr_bit (gdbarch)); |
| while (descdata + KVE_PATH < descend) |
| { |
| ULONGEST structsize = bfd_get_32 (cbfd, descdata + KVE_STRUCTSIZE); |
| if (structsize < KVE_PATH) |
| error (_("malformed core note - vmmap entry too small")); |
| |
| ULONGEST start = bfd_get_64 (cbfd, descdata + KVE_START); |
| ULONGEST end = bfd_get_64 (cbfd, descdata + KVE_END); |
| ULONGEST offset = bfd_get_64 (cbfd, descdata + KVE_OFFSET); |
| LONGEST flags = bfd_get_signed_32 (cbfd, descdata + KVE_FLAGS); |
| LONGEST prot = bfd_get_signed_32 (cbfd, descdata + KVE_PROTECTION); |
| fbsd_info_proc_mappings_entry (gdbarch_addr_bit (gdbarch), start, end, |
| offset, flags, prot, descdata + KVE_PATH); |
| |
| descdata += structsize; |
| } |
| } |
| |
| /* Fetch the pathname of a vnode for a single file descriptor from the |
| file table core note. */ |
| |
| static gdb::unique_xmalloc_ptr<char> |
| fbsd_core_vnode_path (struct gdbarch *gdbarch, int fd) |
| { |
| asection *section; |
| unsigned char *descdata, *descend; |
| size_t note_size; |
| bfd *cbfd = current_program_space->core_bfd (); |
| |
| section = bfd_get_section_by_name (cbfd, ".note.freebsdcore.files"); |
| if (section == NULL) |
| return nullptr; |
| |
| note_size = bfd_section_size (section); |
| if (note_size < 4) |
| error (_("malformed core note - too short for header")); |
| |
| gdb::def_vector<unsigned char> contents (note_size); |
| if (!bfd_get_section_contents (cbfd, section, contents.data (), 0, note_size)) |
| error (_("could not get core note contents")); |
| |
| descdata = contents.data (); |
| descend = descdata + note_size; |
| |
| /* Skip over the structure size. */ |
| descdata += 4; |
| |
| while (descdata + KF_PATH < descend) |
| { |
| ULONGEST structsize; |
| |
| structsize = bfd_get_32 (cbfd, descdata + KF_STRUCTSIZE); |
| if (structsize < KF_PATH) |
| error (_("malformed core note - file structure too small")); |
| |
| if (bfd_get_32 (cbfd, descdata + KF_TYPE) == KINFO_FILE_TYPE_VNODE |
| && bfd_get_signed_32 (cbfd, descdata + KF_FD) == fd) |
| { |
| char *path = (char *) descdata + KF_PATH; |
| return make_unique_xstrdup (path); |
| } |
| |
| descdata += structsize; |
| } |
| return nullptr; |
| } |
| |
| /* Helper function to read a struct timeval. */ |
| |
| static void |
| fbsd_core_fetch_timeval (struct gdbarch *gdbarch, unsigned char *data, |
| LONGEST &sec, ULONGEST &usec) |
| { |
| bfd *cbfd = current_program_space->core_bfd (); |
| |
| if (gdbarch_addr_bit (gdbarch) == 64) |
| { |
| sec = bfd_get_signed_64 (cbfd, data); |
| usec = bfd_get_64 (cbfd, data + 8); |
| } |
| else if (bfd_get_arch (cbfd) == bfd_arch_i386) |
| { |
| sec = bfd_get_signed_32 (cbfd, data); |
| usec = bfd_get_32 (cbfd, data + 4); |
| } |
| else |
| { |
| sec = bfd_get_signed_64 (cbfd, data); |
| usec = bfd_get_32 (cbfd, data + 8); |
| } |
| } |
| |
| /* Print out the contents of a signal set. */ |
| |
| static void |
| fbsd_print_sigset (const char *descr, unsigned char *sigset) |
| { |
| bfd *cbfd = current_program_space->core_bfd (); |
| gdb_printf ("%s: ", descr); |
| for (int i = 0; i < SIG_WORDS; i++) |
| gdb_printf ("%08x ", |
| (unsigned int) bfd_get_32 (cbfd, sigset + i * 4)); |
| gdb_printf ("\n"); |
| } |
| |
| /* Implement "info proc status" for a corefile. */ |
| |
| static void |
| fbsd_core_info_proc_status (struct gdbarch *gdbarch) |
| { |
| const struct kinfo_proc_layout *kp; |
| asection *section; |
| unsigned char *descdata; |
| int addr_bit, long_bit; |
| size_t note_size; |
| ULONGEST value; |
| LONGEST sec; |
| bfd *cbfd = current_program_space->core_bfd (); |
| |
| section = bfd_get_section_by_name (cbfd, ".note.freebsdcore.proc"); |
| if (section == NULL) |
| { |
| warning (_("unable to find process info in core file")); |
| return; |
| } |
| |
| addr_bit = gdbarch_addr_bit (gdbarch); |
| if (addr_bit == 64) |
| kp = &kinfo_proc_layout_64; |
| else if (bfd_get_arch (cbfd) == bfd_arch_i386) |
| kp = &kinfo_proc_layout_i386; |
| else |
| kp = &kinfo_proc_layout_32; |
| long_bit = gdbarch_long_bit (gdbarch); |
| |
| /* |
| * Ensure that the note is large enough for all of the fields fetched |
| * by this function. In particular, the note must contain the 32-bit |
| * structure size, then it must be long enough to access the last |
| * field used (ki_rusage_ch.ru_majflt) which is the size of a long. |
| */ |
| note_size = bfd_section_size (section); |
| if (note_size < (4 + kp->ki_rusage_ch + kp->ru_majflt |
| + long_bit / TARGET_CHAR_BIT)) |
| error (_("malformed core note - too short")); |
| |
| gdb::def_vector<unsigned char> contents (note_size); |
| if (!bfd_get_section_contents (cbfd, section, contents.data (), 0, note_size)) |
| error (_("could not get core note contents")); |
| |
| descdata = contents.data (); |
| |
| /* Skip over the structure size. */ |
| descdata += 4; |
| |
| /* Verify 'ki_layout' is 0. */ |
| if (bfd_get_32 (cbfd, descdata + kp->ki_layout) != 0) |
| { |
| warning (_("unsupported process information in core file")); |
| return; |
| } |
| |
| gdb_printf ("Name: %.19s\n", descdata + kp->ki_comm); |
| gdb_printf ("Process ID: %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_pid))); |
| gdb_printf ("Parent process: %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_ppid))); |
| gdb_printf ("Process group: %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_pgid))); |
| gdb_printf ("Session id: %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_sid))); |
| |
| /* FreeBSD 12.0 and later store a 64-bit dev_t at 'ki_tdev'. Older |
| kernels store a 32-bit dev_t at 'ki_tdev_freebsd11'. In older |
| kernels the 64-bit 'ki_tdev' field is in a reserved section of |
| the structure that is cleared to zero. Assume that a zero value |
| in ki_tdev indicates a core dump from an older kernel and use the |
| value in 'ki_tdev_freebsd11' instead. */ |
| value = bfd_get_64 (cbfd, descdata + kp->ki_tdev); |
| if (value == 0) |
| value = bfd_get_32 (cbfd, descdata + kp->ki_tdev_freebsd11); |
| gdb_printf ("TTY: %s\n", pulongest (value)); |
| gdb_printf ("TTY owner process group: %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_tpgid))); |
| gdb_printf ("User IDs (real, effective, saved): %s %s %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_ruid)), |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_uid)), |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_svuid))); |
| gdb_printf ("Group IDs (real, effective, saved): %s %s %s\n", |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_rgid)), |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_groups)), |
| pulongest (bfd_get_32 (cbfd, descdata + kp->ki_svgid))); |
| gdb_printf ("Groups: "); |
| uint16_t ngroups = bfd_get_16 (cbfd, descdata + kp->ki_ngroups); |
| for (int i = 0; i < ngroups; i++) |
| gdb_printf ("%s ", |
| pulongest (bfd_get_32 (cbfd, |
| descdata + kp->ki_groups + i * 4))); |
| gdb_printf ("\n"); |
| value = bfd_get (long_bit, cbfd, |
| descdata + kp->ki_rusage + kp->ru_minflt); |
| gdb_printf ("Minor faults (no memory page): %s\n", pulongest (value)); |
| value = bfd_get (long_bit, cbfd, |
| descdata + kp->ki_rusage_ch + kp->ru_minflt); |
| gdb_printf ("Minor faults, children: %s\n", pulongest (value)); |
| value = bfd_get (long_bit, cbfd, |
| descdata + kp->ki_rusage + kp->ru_majflt); |
| gdb_printf ("Major faults (memory page faults): %s\n", |
| pulongest (value)); |
| value = bfd_get (long_bit, cbfd, |
| descdata + kp->ki_rusage_ch + kp->ru_majflt); |
| gdb_printf ("Major faults, children: %s\n", pulongest (value)); |
| fbsd_core_fetch_timeval (gdbarch, |
| descdata + kp->ki_rusage + kp->ru_utime, |
| sec, value); |
| gdb_printf ("utime: %s.%06d\n", plongest (sec), (int) value); |
| fbsd_core_fetch_timeval (gdbarch, |
| descdata + kp->ki_rusage + kp->ru_stime, |
| sec, value); |
| gdb_printf ("stime: %s.%06d\n", plongest (sec), (int) value); |
| fbsd_core_fetch_timeval (gdbarch, |
| descdata + kp->ki_rusage_ch + kp->ru_utime, |
| sec, value); |
| gdb_printf ("utime, children: %s.%06d\n", plongest (sec), (int) value); |
| fbsd_core_fetch_timeval (gdbarch, |
| descdata + kp->ki_rusage_ch + kp->ru_stime, |
| sec, value); |
| gdb_printf ("stime, children: %s.%06d\n", plongest (sec), (int) value); |
| gdb_printf ("'nice' value: %d\n", |
| (int) bfd_get_signed_8 (core_bfd, descdata + kp->ki_nice)); |
| fbsd_core_fetch_timeval (gdbarch, descdata + kp->ki_start, sec, value); |
| gdb_printf ("Start time: %s.%06d\n", plongest (sec), (int) value); |
| gdb_printf ("Virtual memory size: %s kB\n", |
| pulongest (bfd_get (addr_bit, cbfd, |
| descdata + kp->ki_size) / 1024)); |
| gdb_printf ("Data size: %s pages\n", |
| pulongest (bfd_get (addr_bit, cbfd, |
| descdata + kp->ki_dsize))); |
| gdb_printf ("Stack size: %s pages\n", |
| pulongest (bfd_get (addr_bit, cbfd, |
| descdata + kp->ki_ssize))); |
| gdb_printf ("Text size: %s pages\n", |
| pulongest (bfd_get (addr_bit, cbfd, |
| descdata + kp->ki_tsize))); |
| gdb_printf ("Resident set size: %s pages\n", |
| pulongest (bfd_get (addr_bit, cbfd, |
| descdata + kp->ki_rssize))); |
| gdb_printf ("Maximum RSS: %s pages\n", |
| pulongest (bfd_get (long_bit, cbfd, |
| descdata + kp->ki_rusage |
| + kp->ru_maxrss))); |
| fbsd_print_sigset ("Ignored Signals", descdata + kp->ki_sigignore); |
| fbsd_print_sigset ("Caught Signals", descdata + kp->ki_sigcatch); |
| } |
| |
| /* Implement the "core_info_proc" gdbarch method. */ |
| |
| static void |
| fbsd_core_info_proc (struct gdbarch *gdbarch, const char *args, |
| enum info_proc_what what) |
| { |
| bool do_cmdline = false; |
| bool do_cwd = false; |
| bool do_exe = false; |
| bool do_files = false; |
| bool do_mappings = false; |
| bool do_status = false; |
| int pid; |
| |
| switch (what) |
| { |
| case IP_MINIMAL: |
| do_cmdline = true; |
| do_cwd = true; |
| do_exe = true; |
| break; |
| case IP_MAPPINGS: |
| do_mappings = true; |
| break; |
| case IP_STATUS: |
| case IP_STAT: |
| do_status = true; |
| break; |
| case IP_CMDLINE: |
| do_cmdline = true; |
| break; |
| case IP_EXE: |
| do_exe = true; |
| break; |
| case IP_CWD: |
| do_cwd = true; |
| break; |
| case IP_FILES: |
| do_files = true; |
| break; |
| case IP_ALL: |
| do_cmdline = true; |
| do_cwd = true; |
| do_exe = true; |
| do_files = true; |
| do_mappings = true; |
| do_status = true; |
| break; |
| default: |
| return; |
| } |
| |
| bfd *cbfd = current_program_space->core_bfd (); |
| pid = bfd_core_file_pid (cbfd); |
| if (pid != 0) |
| gdb_printf (_("process %d\n"), pid); |
| |
| if (do_cmdline) |
| { |
| const char *cmdline; |
| |
| cmdline = bfd_core_file_failing_command (cbfd); |
| if (cmdline) |
| gdb_printf ("cmdline = '%s'\n", cmdline); |
| else |
| warning (_("Command line unavailable")); |
| } |
| if (do_cwd) |
| { |
| gdb::unique_xmalloc_ptr<char> cwd = |
| fbsd_core_vnode_path (gdbarch, KINFO_FILE_FD_TYPE_CWD); |
| if (cwd) |
| gdb_printf ("cwd = '%s'\n", cwd.get ()); |
| else |
| warning (_("unable to read current working directory")); |
| } |
| if (do_exe) |
| { |
| gdb::unique_xmalloc_ptr<char> exe = |
| fbsd_core_vnode_path (gdbarch, KINFO_FILE_FD_TYPE_TEXT); |
| if (exe) |
| gdb_printf ("exe = '%s'\n", exe.get ()); |
| else |
| warning (_("unable to read executable path name")); |
| } |
| if (do_files) |
| fbsd_core_info_proc_files (gdbarch); |
| if (do_mappings) |
| fbsd_core_info_proc_mappings (gdbarch); |
| if (do_status) |
| fbsd_core_info_proc_status (gdbarch); |
| } |
| |
| /* Print descriptions of FreeBSD-specific AUXV entries to FILE. */ |
| |
| static void |
| fbsd_print_auxv_entry (struct gdbarch *gdbarch, struct ui_file *file, |
| CORE_ADDR type, CORE_ADDR val) |
| { |
| const char *name = "???"; |
| const char *description = ""; |
| enum auxv_format format = AUXV_FORMAT_HEX; |
| |
| switch (type) |
| { |
| case AT_NULL: |
| case AT_IGNORE: |
| case AT_EXECFD: |
| case AT_PHDR: |
| case AT_PHENT: |
| case AT_PHNUM: |
| case AT_PAGESZ: |
| case AT_BASE: |
| case AT_FLAGS: |
| case AT_ENTRY: |
| case AT_NOTELF: |
| case AT_UID: |
| case AT_EUID: |
| case AT_GID: |
| case AT_EGID: |
| default_print_auxv_entry (gdbarch, file, type, val); |
| return; |
| #define _TAGNAME(tag) #tag |
| #define TAGNAME(tag) _TAGNAME(AT_##tag) |
| #define TAG(tag, text, kind) \ |
| case AT_FREEBSD_##tag: name = TAGNAME(tag); description = text; format = kind; break |
| TAG (EXECPATH, _("Executable path"), AUXV_FORMAT_STR); |
| TAG (CANARY, _("Canary for SSP"), AUXV_FORMAT_HEX); |
| TAG (CANARYLEN, ("Length of the SSP canary"), AUXV_FORMAT_DEC); |
| TAG (OSRELDATE, _("OSRELDATE"), AUXV_FORMAT_DEC); |
| TAG (NCPUS, _("Number of CPUs"), AUXV_FORMAT_DEC); |
| TAG (PAGESIZES, _("Pagesizes"), AUXV_FORMAT_HEX); |
| TAG (PAGESIZESLEN, _("Number of pagesizes"), AUXV_FORMAT_DEC); |
| TAG (TIMEKEEP, _("Pointer to timehands"), AUXV_FORMAT_HEX); |
| TAG (STACKPROT, _("Initial stack protection"), AUXV_FORMAT_HEX); |
| TAG (EHDRFLAGS, _("ELF header e_flags"), AUXV_FORMAT_HEX); |
| TAG (HWCAP, _("Machine-dependent CPU capability hints"), AUXV_FORMAT_HEX); |
| TAG (HWCAP2, _("Extension of AT_HWCAP"), AUXV_FORMAT_HEX); |
| TAG (BSDFLAGS, _("ELF BSD flags"), AUXV_FORMAT_HEX); |
| TAG (ARGC, _("Argument count"), AUXV_FORMAT_DEC); |
| TAG (ARGV, _("Argument vector"), AUXV_FORMAT_HEX); |
| TAG (ENVC, _("Environment count"), AUXV_FORMAT_DEC); |
| TAG (ENVV, _("Environment vector"), AUXV_FORMAT_HEX); |
| TAG (PS_STRINGS, _("Pointer to ps_strings"), AUXV_FORMAT_HEX); |
| TAG (FXRNG, _("Pointer to root RNG seed version"), AUXV_FORMAT_HEX); |
| TAG (KPRELOAD, _("Base address of vDSO"), AUXV_FORMAT_HEX); |
| TAG (USRSTACKBASE, _("Top of user stack"), AUXV_FORMAT_HEX); |
| TAG (USRSTACKLIM, _("Grow limit of user stack"), AUXV_FORMAT_HEX); |
| } |
| |
| fprint_auxv_entry (file, name, description, format, type, val); |
| } |
| |
| /* Implement the "get_siginfo_type" gdbarch method. */ |
| |
| static struct type * |
| fbsd_get_siginfo_type (struct gdbarch *gdbarch) |
| { |
| struct fbsd_gdbarch_data *fbsd_gdbarch_data; |
| struct type *int_type, *int32_type, *uint32_type, *long_type, *void_ptr_type; |
| struct type *uid_type, *pid_type; |
| struct type *sigval_type, *reason_type; |
| struct type *siginfo_type; |
| struct type *type; |
| |
| fbsd_gdbarch_data = get_fbsd_gdbarch_data (gdbarch); |
| if (fbsd_gdbarch_data->siginfo_type != NULL) |
| return fbsd_gdbarch_data->siginfo_type; |
| |
| type_allocator alloc (gdbarch); |
| int_type = init_integer_type (alloc, gdbarch_int_bit (gdbarch), |
| 0, "int"); |
| int32_type = init_integer_type (alloc, 32, 0, "int32_t"); |
| uint32_type = init_integer_type (alloc, 32, 1, "uint32_t"); |
| long_type = init_integer_type (alloc, gdbarch_long_bit (gdbarch), |
| 0, "long"); |
| void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); |
| |
| /* union sigval */ |
| sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); |
| sigval_type->set_name (xstrdup ("sigval")); |
| append_composite_type_field (sigval_type, "sival_int", int_type); |
| append_composite_type_field (sigval_type, "sival_ptr", void_ptr_type); |
| |
| /* __pid_t */ |
| pid_type = alloc.new_type (TYPE_CODE_TYPEDEF, |
| int32_type->length () * TARGET_CHAR_BIT, |
| "__pid_t"); |
| pid_type->set_target_type (int32_type); |
| pid_type->set_target_is_stub (true); |
| |
| /* __uid_t */ |
| uid_type = alloc.new_type (TYPE_CODE_TYPEDEF, |
| uint32_type->length () * TARGET_CHAR_BIT, |
| "__uid_t"); |
| uid_type->set_target_type (uint32_type); |
| pid_type->set_target_is_stub (true); |
| |
| /* _reason */ |
| reason_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); |
| |
| /* _fault */ |
| type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| append_composite_type_field (type, "si_trapno", int_type); |
| append_composite_type_field (reason_type, "_fault", type); |
| |
| /* _timer */ |
| type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| append_composite_type_field (type, "si_timerid", int_type); |
| append_composite_type_field (type, "si_overrun", int_type); |
| append_composite_type_field (reason_type, "_timer", type); |
| |
| /* _mesgq */ |
| type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| append_composite_type_field (type, "si_mqd", int_type); |
| append_composite_type_field (reason_type, "_mesgq", type); |
| |
| /* _poll */ |
| type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| append_composite_type_field (type, "si_band", long_type); |
| append_composite_type_field (reason_type, "_poll", type); |
| |
| /* __spare__ */ |
| type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| append_composite_type_field (type, "__spare1__", long_type); |
| append_composite_type_field (type, "__spare2__", |
| init_vector_type (int_type, 7)); |
| append_composite_type_field (reason_type, "__spare__", type); |
| |
| /* struct siginfo */ |
| siginfo_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| siginfo_type->set_name (xstrdup ("siginfo")); |
| append_composite_type_field (siginfo_type, "si_signo", int_type); |
| append_composite_type_field (siginfo_type, "si_errno", int_type); |
| append_composite_type_field (siginfo_type, "si_code", int_type); |
| append_composite_type_field (siginfo_type, "si_pid", pid_type); |
| append_composite_type_field (siginfo_type, "si_uid", uid_type); |
| append_composite_type_field (siginfo_type, "si_status", int_type); |
| append_composite_type_field (siginfo_type, "si_addr", void_ptr_type); |
| append_composite_type_field (siginfo_type, "si_value", sigval_type); |
| append_composite_type_field (siginfo_type, "_reason", reason_type); |
| |
| fbsd_gdbarch_data->siginfo_type = siginfo_type; |
| |
| return siginfo_type; |
| } |
| |
| /* Implement the "gdb_signal_from_target" gdbarch method. */ |
| |
| static enum gdb_signal |
| fbsd_gdb_signal_from_target (struct gdbarch *gdbarch, int signal) |
| { |
| switch (signal) |
| { |
| case 0: |
| return GDB_SIGNAL_0; |
| |
| case FREEBSD_SIGHUP: |
| return GDB_SIGNAL_HUP; |
| |
| case FREEBSD_SIGINT: |
| return GDB_SIGNAL_INT; |
| |
| case FREEBSD_SIGQUIT: |
| return GDB_SIGNAL_QUIT; |
| |
| case FREEBSD_SIGILL: |
| return GDB_SIGNAL_ILL; |
| |
| case FREEBSD_SIGTRAP: |
| return GDB_SIGNAL_TRAP; |
| |
| case FREEBSD_SIGABRT: |
| return GDB_SIGNAL_ABRT; |
| |
| case FREEBSD_SIGEMT: |
| return GDB_SIGNAL_EMT; |
| |
| case FREEBSD_SIGFPE: |
| return GDB_SIGNAL_FPE; |
| |
| case FREEBSD_SIGKILL: |
| return GDB_SIGNAL_KILL; |
| |
| case FREEBSD_SIGBUS: |
| return GDB_SIGNAL_BUS; |
| |
| case FREEBSD_SIGSEGV: |
| return GDB_SIGNAL_SEGV; |
| |
| case FREEBSD_SIGSYS: |
| return GDB_SIGNAL_SYS; |
| |
| case FREEBSD_SIGPIPE: |
| return GDB_SIGNAL_PIPE; |
| |
| case FREEBSD_SIGALRM: |
| return GDB_SIGNAL_ALRM; |
| |
| case FREEBSD_SIGTERM: |
| return GDB_SIGNAL_TERM; |
| |
| case FREEBSD_SIGURG: |
| return GDB_SIGNAL_URG; |
| |
| case FREEBSD_SIGSTOP: |
| return GDB_SIGNAL_STOP; |
| |
| case FREEBSD_SIGTSTP: |
| return GDB_SIGNAL_TSTP; |
| |
| case FREEBSD_SIGCONT: |
| return GDB_SIGNAL_CONT; |
| |
| case FREEBSD_SIGCHLD: |
| return GDB_SIGNAL_CHLD; |
| |
| case FREEBSD_SIGTTIN: |
| return GDB_SIGNAL_TTIN; |
| |
| case FREEBSD_SIGTTOU: |
| return GDB_SIGNAL_TTOU; |
| |
| case FREEBSD_SIGIO: |
| return GDB_SIGNAL_IO; |
| |
| case FREEBSD_SIGXCPU: |
| return GDB_SIGNAL_XCPU; |
| |
| case FREEBSD_SIGXFSZ: |
| return GDB_SIGNAL_XFSZ; |
| |
| case FREEBSD_SIGVTALRM: |
| return GDB_SIGNAL_VTALRM; |
| |
| case FREEBSD_SIGPROF: |
| return GDB_SIGNAL_PROF; |
| |
| case FREEBSD_SIGWINCH: |
| return GDB_SIGNAL_WINCH; |
| |
| case FREEBSD_SIGINFO: |
| return GDB_SIGNAL_INFO; |
| |
| case FREEBSD_SIGUSR1: |
| return GDB_SIGNAL_USR1; |
| |
| case FREEBSD_SIGUSR2: |
| return GDB_SIGNAL_USR2; |
| |
| /* SIGTHR is the same as SIGLWP on FreeBSD. */ |
| case FREEBSD_SIGTHR: |
| return GDB_SIGNAL_LWP; |
| |
| case FREEBSD_SIGLIBRT: |
| return GDB_SIGNAL_LIBRT; |
| } |
| |
| if (signal >= FREEBSD_SIGRTMIN && signal <= FREEBSD_SIGRTMAX) |
| { |
| int offset = signal - FREEBSD_SIGRTMIN; |
| |
| return (enum gdb_signal) ((int) GDB_SIGNAL_REALTIME_65 + offset); |
| } |
| |
| return GDB_SIGNAL_UNKNOWN; |
| } |
| |
| /* Implement the "gdb_signal_to_target" gdbarch method. */ |
| |
| static int |
| fbsd_gdb_signal_to_target (struct gdbarch *gdbarch, |
| enum gdb_signal signal) |
| { |
| switch (signal) |
| { |
| case GDB_SIGNAL_0: |
| return 0; |
| |
| case GDB_SIGNAL_HUP: |
| return FREEBSD_SIGHUP; |
| |
| case GDB_SIGNAL_INT: |
| return FREEBSD_SIGINT; |
| |
| case GDB_SIGNAL_QUIT: |
| return FREEBSD_SIGQUIT; |
| |
| case GDB_SIGNAL_ILL: |
| return FREEBSD_SIGILL; |
| |
| case GDB_SIGNAL_TRAP: |
| return FREEBSD_SIGTRAP; |
| |
| case GDB_SIGNAL_ABRT: |
| return FREEBSD_SIGABRT; |
| |
| case GDB_SIGNAL_EMT: |
| return FREEBSD_SIGEMT; |
| |
| case GDB_SIGNAL_FPE: |
| return FREEBSD_SIGFPE; |
| |
| case GDB_SIGNAL_KILL: |
| return FREEBSD_SIGKILL; |
| |
| case GDB_SIGNAL_BUS: |
| return FREEBSD_SIGBUS; |
| |
| case GDB_SIGNAL_SEGV: |
| return FREEBSD_SIGSEGV; |
| |
| case GDB_SIGNAL_SYS: |
| return FREEBSD_SIGSYS; |
| |
| case GDB_SIGNAL_PIPE: |
| return FREEBSD_SIGPIPE; |
| |
| case GDB_SIGNAL_ALRM: |
| return FREEBSD_SIGALRM; |
| |
| case GDB_SIGNAL_TERM: |
| return FREEBSD_SIGTERM; |
| |
| case GDB_SIGNAL_URG: |
| return FREEBSD_SIGURG; |
| |
| case GDB_SIGNAL_STOP: |
| return FREEBSD_SIGSTOP; |
| |
| case GDB_SIGNAL_TSTP: |
| return FREEBSD_SIGTSTP; |
| |
| case GDB_SIGNAL_CONT: |
| return FREEBSD_SIGCONT; |
| |
| case GDB_SIGNAL_CHLD: |
| return FREEBSD_SIGCHLD; |
| |
| case GDB_SIGNAL_TTIN: |
| return FREEBSD_SIGTTIN; |
| |
| case GDB_SIGNAL_TTOU: |
| return FREEBSD_SIGTTOU; |
| |
| case GDB_SIGNAL_IO: |
| return FREEBSD_SIGIO; |
| |
| case GDB_SIGNAL_XCPU: |
| return FREEBSD_SIGXCPU; |
| |
| case GDB_SIGNAL_XFSZ: |
| return FREEBSD_SIGXFSZ; |
| |
| case GDB_SIGNAL_VTALRM: |
| return FREEBSD_SIGVTALRM; |
| |
| case GDB_SIGNAL_PROF: |
| return FREEBSD_SIGPROF; |
| |
| case GDB_SIGNAL_WINCH: |
| return FREEBSD_SIGWINCH; |
| |
| case GDB_SIGNAL_INFO: |
| return FREEBSD_SIGINFO; |
| |
| case GDB_SIGNAL_USR1: |
| return FREEBSD_SIGUSR1; |
| |
| case GDB_SIGNAL_USR2: |
| return FREEBSD_SIGUSR2; |
| |
| case GDB_SIGNAL_LWP: |
| return FREEBSD_SIGTHR; |
| |
| case GDB_SIGNAL_LIBRT: |
| return FREEBSD_SIGLIBRT; |
| } |
| |
| if (signal >= GDB_SIGNAL_REALTIME_65 |
| && signal <= GDB_SIGNAL_REALTIME_126) |
| { |
| int offset = signal - GDB_SIGNAL_REALTIME_65; |
| |
| return FREEBSD_SIGRTMIN + offset; |
| } |
| |
| return -1; |
| } |
| |
| /* Implement the "get_syscall_number" gdbarch method. */ |
| |
| static LONGEST |
| fbsd_get_syscall_number (struct gdbarch *gdbarch, thread_info *thread) |
| { |
| |
| /* FreeBSD doesn't use gdbarch_get_syscall_number since FreeBSD |
| native targets fetch the system call number from the |
| 'pl_syscall_code' member of struct ptrace_lwpinfo in fbsd_wait. |
| However, system call catching requires this function to be |
| set. */ |
| |
| internal_error (_("fbsd_get_sycall_number called")); |
| } |
| |
| /* Read an integer symbol value from the current target. */ |
| |
| static LONGEST |
| fbsd_read_integer_by_name (struct gdbarch *gdbarch, const char *name) |
| { |
| bound_minimal_symbol ms |
| = lookup_minimal_symbol (current_program_space, name); |
| if (ms.minsym == NULL) |
| error (_("Unable to resolve symbol '%s'"), name); |
| |
| gdb_byte buf[4]; |
| if (target_read_memory (ms.value_address (), buf, sizeof buf) != 0) |
| error (_("Unable to read value of '%s'"), name); |
| |
| return extract_signed_integer (buf, gdbarch_byte_order (gdbarch)); |
| } |
| |
| /* Lookup offsets of fields in the runtime linker's 'Obj_Entry' |
| structure needed to determine the TLS index of an object file. */ |
| |
| static void |
| fbsd_fetch_rtld_offsets (struct gdbarch *gdbarch, struct fbsd_pspace_data *data) |
| { |
| try |
| { |
| /* Fetch offsets from debug symbols in rtld. */ |
| struct symbol *obj_entry_sym |
| = lookup_symbol_in_language ("Struct_Obj_Entry", nullptr, |
| SEARCH_STRUCT_DOMAIN, |
| language_c, nullptr).symbol; |
| if (obj_entry_sym == NULL) |
| error (_("Unable to find Struct_Obj_Entry symbol")); |
| data->off_linkmap = lookup_struct_elt (obj_entry_sym->type (), |
| "linkmap", 0).offset / 8; |
| data->off_tlsindex = lookup_struct_elt (obj_entry_sym->type (), |
| "tlsindex", 0).offset / 8; |
| data->rtld_offsets_valid = true; |
| return; |
| } |
| catch (const gdb_exception_error &e) |
| { |
| data->off_linkmap = -1; |
| } |
| |
| try |
| { |
| /* Fetch offsets from global variables in libthr. Note that |
| this does not work for single-threaded processes that are not |
| linked against libthr. */ |
| data->off_linkmap = fbsd_read_integer_by_name (gdbarch, |
| "_thread_off_linkmap"); |
| data->off_tlsindex = fbsd_read_integer_by_name (gdbarch, |
| "_thread_off_tlsindex"); |
| data->rtld_offsets_valid = true; |
| return; |
| } |
| catch (const gdb_exception_error &e) |
| { |
| data->off_linkmap = -1; |
| } |
| } |
| |
| /* Helper function to read the TLS index of an object file associated |
| with a link map entry at LM_ADDR. */ |
| |
| static LONGEST |
| fbsd_get_tls_index (struct gdbarch *gdbarch, CORE_ADDR lm_addr) |
| { |
| struct fbsd_pspace_data *data = get_fbsd_pspace_data (current_program_space); |
| |
| if (!data->rtld_offsets_valid) |
| fbsd_fetch_rtld_offsets (gdbarch, data); |
| |
| if (data->off_linkmap == -1) |
| throw_error (TLS_GENERIC_ERROR, |
| _("Cannot fetch runtime linker structure offsets")); |
| |
| /* Simulate container_of to convert from LM_ADDR to the Obj_Entry |
| pointer and then compute the offset of the tlsindex member. */ |
| CORE_ADDR tlsindex_addr = lm_addr - data->off_linkmap + data->off_tlsindex; |
| |
| gdb_byte buf[4]; |
| if (target_read_memory (tlsindex_addr, buf, sizeof buf) != 0) |
| throw_error (TLS_GENERIC_ERROR, |
| _("Cannot find thread-local variables on this target")); |
| |
| return extract_signed_integer (buf, gdbarch_byte_order (gdbarch)); |
| } |
| |
| /* See fbsd-tdep.h. */ |
| |
| CORE_ADDR |
| fbsd_get_thread_local_address (struct gdbarch *gdbarch, CORE_ADDR dtv_addr, |
| CORE_ADDR lm_addr, CORE_ADDR offset) |
| { |
| LONGEST tls_index = fbsd_get_tls_index (gdbarch, lm_addr); |
| |
| gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); |
| if (target_read_memory (dtv_addr, buf.data (), buf.size ()) != 0) |
| throw_error (TLS_GENERIC_ERROR, |
| _("Cannot find thread-local variables on this target")); |
| |
| const struct builtin_type *builtin = builtin_type (gdbarch); |
| CORE_ADDR addr |
| = gdbarch_pointer_to_address (gdbarch, builtin->builtin_data_ptr, |
| buf.data ()); |
| |
| addr += (tls_index + 1) * builtin->builtin_data_ptr->length (); |
| if (target_read_memory (addr, buf.data (), buf.size ()) != 0) |
| throw_error (TLS_GENERIC_ERROR, |
| _("Cannot find thread-local variables on this target")); |
| |
| addr = gdbarch_pointer_to_address (gdbarch, builtin->builtin_data_ptr, |
| buf.data ()); |
| return addr + offset; |
| } |
| |
| /* See fbsd-tdep.h. */ |
| |
| CORE_ADDR |
| fbsd_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| bound_minimal_symbol msym |
| = lookup_minimal_symbol (current_program_space, "_rtld_bind"); |
| if (msym.minsym != nullptr && msym.value_address () == pc) |
| return frame_unwind_caller_pc (get_current_frame ()); |
| |
| return 0; |
| } |
| |
| /* Return description of signal code or nullptr. */ |
| |
| static const char * |
| fbsd_signal_cause (enum gdb_signal siggnal, int code) |
| { |
| /* Signal-independent causes. */ |
| switch (code) |
| { |
| case FBSD_SI_USER: |
| return _("Sent by kill()"); |
| case FBSD_SI_QUEUE: |
| return _("Sent by sigqueue()"); |
| case FBSD_SI_TIMER: |
| return _("Timer expired"); |
| case FBSD_SI_ASYNCIO: |
| return _("Asynchronous I/O request completed"); |
| case FBSD_SI_MESGQ: |
| return _("Message arrived on empty message queue"); |
| case FBSD_SI_KERNEL: |
| return _("Sent by kernel"); |
| case FBSD_SI_LWP: |
| return _("Sent by thr_kill()"); |
| } |
| |
| switch (siggnal) |
| { |
| case GDB_SIGNAL_ILL: |
| switch (code) |
| { |
| case FBSD_ILL_ILLOPC: |
| return _("Illegal opcode"); |
| case FBSD_ILL_ILLOPN: |
| return _("Illegal operand"); |
| case FBSD_ILL_ILLADR: |
| return _("Illegal addressing mode"); |
| case FBSD_ILL_ILLTRP: |
| return _("Illegal trap"); |
| case FBSD_ILL_PRVOPC: |
| return _("Privileged opcode"); |
| case FBSD_ILL_PRVREG: |
| return _("Privileged register"); |
| case FBSD_ILL_COPROC: |
| return _("Coprocessor error"); |
| case FBSD_ILL_BADSTK: |
| return _("Internal stack error"); |
| } |
| break; |
| case GDB_SIGNAL_BUS: |
| switch (code) |
| { |
| case FBSD_BUS_ADRALN: |
| return _("Invalid address alignment"); |
| case FBSD_BUS_ADRERR: |
| return _("Address not present"); |
| case FBSD_BUS_OBJERR: |
| return _("Object-specific hardware error"); |
| case FBSD_BUS_OOMERR: |
| return _("Out of memory"); |
| } |
| break; |
| case GDB_SIGNAL_SEGV: |
| switch (code) |
| { |
| case FBSD_SEGV_MAPERR: |
| return _("Address not mapped to object"); |
| case FBSD_SEGV_ACCERR: |
| return _("Invalid permissions for mapped object"); |
| case FBSD_SEGV_PKUERR: |
| return _("PKU violation"); |
| } |
| break; |
| case GDB_SIGNAL_FPE: |
| switch (code) |
| { |
| case FBSD_FPE_INTOVF: |
| return _("Integer overflow"); |
| case FBSD_FPE_INTDIV: |
| return _("Integer divide by zero"); |
| case FBSD_FPE_FLTDIV: |
| return _("Floating point divide by zero"); |
| case FBSD_FPE_FLTOVF: |
| return _("Floating point overflow"); |
| case FBSD_FPE_FLTUND: |
| return _("Floating point underflow"); |
| case FBSD_FPE_FLTRES: |
| return _("Floating point inexact result"); |
| case FBSD_FPE_FLTINV: |
| return _("Invalid floating point operation"); |
| case FBSD_FPE_FLTSUB: |
| return _("Subscript out of range"); |
| } |
| break; |
| case GDB_SIGNAL_TRAP: |
| switch (code) |
| { |
| case FBSD_TRAP_BRKPT: |
| return _("Breakpoint"); |
| case FBSD_TRAP_TRACE: |
| return _("Trace trap"); |
| case FBSD_TRAP_DTRACE: |
| return _("DTrace-induced trap"); |
| case FBSD_TRAP_CAP: |
| return _("Capability violation"); |
| } |
| break; |
| case GDB_SIGNAL_CHLD: |
| switch (code) |
| { |
| case FBSD_CLD_EXITED: |
| return _("Child has exited"); |
| case FBSD_CLD_KILLED: |
| return _("Child has terminated abnormally"); |
| case FBSD_CLD_DUMPED: |
| return _("Child has dumped core"); |
| case FBSD_CLD_TRAPPED: |
| return _("Traced child has trapped"); |
| case FBSD_CLD_STOPPED: |
| return _("Child has stopped"); |
| case FBSD_CLD_CONTINUED: |
| return _("Stopped child has continued"); |
| } |
| break; |
| case GDB_SIGNAL_POLL: |
| switch (code) |
| { |
| case FBSD_POLL_IN: |
| return _("Data input available"); |
| case FBSD_POLL_OUT: |
| return _("Output buffers available"); |
| case FBSD_POLL_MSG: |
| return _("Input message available"); |
| case FBSD_POLL_ERR: |
| return _("I/O error"); |
| case FBSD_POLL_PRI: |
| return _("High priority input available"); |
| case FBSD_POLL_HUP: |
| return _("Device disconnected"); |
| } |
| break; |
| } |
| |
| return nullptr; |
| } |
| |
| /* Report additional details for a signal stop. */ |
| |
| static void |
| fbsd_report_signal_info (struct gdbarch *gdbarch, struct ui_out *uiout, |
| enum gdb_signal siggnal) |
| { |
| LONGEST code, mqd, pid, status, timerid, uid; |
| |
| try |
| { |
| code = parse_and_eval_long ("$_siginfo.si_code"); |
| pid = parse_and_eval_long ("$_siginfo.si_pid"); |
| uid = parse_and_eval_long ("$_siginfo.si_uid"); |
| status = parse_and_eval_long ("$_siginfo.si_status"); |
| timerid = parse_and_eval_long ("$_siginfo._reason._timer.si_timerid"); |
| mqd = parse_and_eval_long ("$_siginfo._reason._mesgq.si_mqd"); |
| } |
| catch (const gdb_exception_error &e) |
| { |
| return; |
| } |
| |
| const char *meaning = fbsd_signal_cause (siggnal, code); |
| if (meaning == nullptr) |
| return; |
| |
| uiout->text (".\n"); |
| uiout->field_string ("sigcode-meaning", meaning); |
| |
| switch (code) |
| { |
| case FBSD_SI_USER: |
| case FBSD_SI_QUEUE: |
| case FBSD_SI_LWP: |
| uiout->text (" from pid "); |
| uiout->field_string ("sending-pid", plongest (pid)); |
| uiout->text (" and user "); |
| uiout->field_string ("sending-uid", plongest (uid)); |
| return; |
| case FBSD_SI_TIMER: |
| uiout->text (": timerid "); |
| uiout->field_string ("timerid", plongest (timerid)); |
| return; |
| case FBSD_SI_MESGQ: |
| uiout->text (": message queue "); |
| uiout->field_string ("message-queue", plongest (mqd)); |
| return; |
| case FBSD_SI_ASYNCIO: |
| return; |
| } |
| |
| if (siggnal == GDB_SIGNAL_CHLD) |
| { |
| uiout->text (": pid "); |
| uiout->field_string ("child-pid", plongest (pid)); |
| uiout->text (", uid "); |
| uiout->field_string ("child-uid", plongest (uid)); |
| if (code == FBSD_CLD_EXITED) |
| { |
| uiout->text (", exit status "); |
| uiout->field_string ("exit-status", plongest (status)); |
| } |
| else |
| { |
| uiout->text (", signal "); |
| uiout->field_string ("signal", plongest (status)); |
| } |
| } |
| } |
| |
| /* Search a list of struct kinfo_vmmap entries in the ENTRIES buffer |
| of LEN bytes to find the length of the entry starting at ADDR. |
| Returns the length of the entry or zero if no entry was found. */ |
| |
| static ULONGEST |
| fbsd_vmmap_length (struct gdbarch *gdbarch, unsigned char *entries, size_t len, |
| CORE_ADDR addr) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| unsigned char *descdata = entries; |
| unsigned char *descend = descdata + len; |
| |
| /* Skip over the structure size. */ |
| descdata += 4; |
| |
| while (descdata + KVE_PATH < descend) |
| { |
| ULONGEST structsize = extract_unsigned_integer (descdata |
| + KVE_STRUCTSIZE, 4, |
| byte_order); |
| if (structsize < KVE_PATH) |
| return false; |
| |
| ULONGEST start = extract_unsigned_integer (descdata + KVE_START, 8, |
| byte_order); |
| ULONGEST end = extract_unsigned_integer (descdata + KVE_END, 8, |
| byte_order); |
| if (start == addr) |
| return end - start; |
| |
| descdata += structsize; |
| } |
| return 0; |
| } |
| |
| /* Helper for fbsd_vsyscall_range that does the real work of finding |
| the vDSO's address range. */ |
| |
| static bool |
| fbsd_vdso_range (struct gdbarch *gdbarch, struct mem_range *range) |
| { |
| if (target_auxv_search (AT_FREEBSD_KPRELOAD, &range->start) <= 0) |
| return false; |
| |
| if (!target_has_execution ()) |
| { |
| /* Search for the ending address in the NT_PROCSTAT_VMMAP note. */ |
| bfd *cbfd = current_program_space->core_bfd (); |
| asection *section = bfd_get_section_by_name (cbfd, |
| ".note.freebsdcore.vmmap"); |
| if (section == nullptr) |
| return false; |
| |
| size_t note_size = bfd_section_size (section); |
| if (note_size < 4) |
| return false; |
| |
| gdb::def_vector<unsigned char> contents (note_size); |
| if (!bfd_get_section_contents (cbfd, section, contents.data (), |
| 0, note_size)) |
| return false; |
| |
| range->length = fbsd_vmmap_length (gdbarch, contents.data (), note_size, |
| range->start); |
| } |
| else |
| { |
| /* Fetch the list of address space entries from the running target. */ |
| std::optional<gdb::byte_vector> buf = |
| target_read_alloc (current_inferior ()->top_target (), |
| TARGET_OBJECT_FREEBSD_VMMAP, nullptr); |
| if (!buf || buf->empty ()) |
| return false; |
| |
| range->length = fbsd_vmmap_length (gdbarch, buf->data (), buf->size (), |
| range->start); |
| } |
| return range->length != 0; |
| } |
| |
| /* Return the address range of the vDSO for the current inferior. */ |
| |
| static int |
| fbsd_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) |
| { |
| struct fbsd_pspace_data *data = get_fbsd_pspace_data (current_program_space); |
| |
| if (data->vdso_range_p == 0) |
| { |
| if (fbsd_vdso_range (gdbarch, &data->vdso_range)) |
| data->vdso_range_p = 1; |
| else |
| data->vdso_range_p = -1; |
| } |
| |
| if (data->vdso_range_p < 0) |
| return 0; |
| |
| *range = data->vdso_range; |
| return 1; |
| } |
| |
| /* To be called from GDB_OSABI_FREEBSD handlers. */ |
| |
| void |
| fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) |
| { |
| set_gdbarch_core_pid_to_str (gdbarch, fbsd_core_pid_to_str); |
| set_gdbarch_core_thread_name (gdbarch, fbsd_core_thread_name); |
| set_gdbarch_core_xfer_siginfo (gdbarch, fbsd_core_xfer_siginfo); |
| set_gdbarch_make_corefile_notes (gdbarch, fbsd_make_corefile_notes); |
| set_gdbarch_core_info_proc (gdbarch, fbsd_core_info_proc); |
| set_gdbarch_print_auxv_entry (gdbarch, fbsd_print_auxv_entry); |
| set_gdbarch_get_siginfo_type (gdbarch, fbsd_get_siginfo_type); |
| set_gdbarch_gdb_signal_from_target (gdbarch, fbsd_gdb_signal_from_target); |
| set_gdbarch_gdb_signal_to_target (gdbarch, fbsd_gdb_signal_to_target); |
| set_gdbarch_report_signal_info (gdbarch, fbsd_report_signal_info); |
| set_gdbarch_skip_solib_resolver (gdbarch, fbsd_skip_solib_resolver); |
| set_gdbarch_vsyscall_range (gdbarch, fbsd_vsyscall_range); |
| |
| /* `catch syscall' */ |
| set_xml_syscall_file_name (gdbarch, "syscalls/freebsd.xml"); |
| set_gdbarch_get_syscall_number (gdbarch, fbsd_get_syscall_number); |
| } |