1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2021 Edward Tomasz Napierala <trasz@FreeBSD.org> 5 * Copyright (c) 2018 Chuck Tuffli 6 * Copyright (c) 2017 Dell EMC 7 * Copyright (c) 2000 David O'Brien 8 * Copyright (c) 1995-1996 Søren Schmidt 9 * Copyright (c) 1996 Peter Wemm 10 * All rights reserved. 11 * 12 * This software was developed by the University of Cambridge Computer 13 * Laboratory as part of the CHERI for Hypervisors and Operating Systems 14 * (CHaOS) project, funded by EPSRC grant EP/V000292/1. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer 21 * in this position and unchanged. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. The name of the author may not be used to endorse or promote products 26 * derived from this software without specific prior written permission 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 29 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 30 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 31 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 32 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 33 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 37 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/cdefs.h> 41 __FBSDID("$FreeBSD$"); 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/exec.h> 46 #include <sys/imgact.h> 47 #include <sys/imgact_elf.h> 48 #include <sys/kernel.h> 49 #include <sys/proc.h> 50 #include <sys/procfs.h> 51 #include <sys/ptrace.h> 52 #include <sys/sbuf.h> 53 #include <sys/sysent.h> 54 #include <sys/user.h> 55 #include <sys/vnode.h> 56 57 #include <machine/elf.h> 58 59 #if __ELF_WORD_SIZE == 32 60 #define linux_pt_regset linux_pt_regset32 61 #define bsd_to_linux_regset bsd_to_linux_regset32 62 #include <machine/../linux32/linux.h> 63 #else 64 #include <machine/../linux/linux.h> 65 #endif 66 #include <compat/linux/linux_elf.h> 67 #include <compat/linux/linux_emul.h> 68 #include <compat/linux/linux_misc.h> 69 70 /* This adds "linux32_" and "linux64_" prefixes. */ 71 #define __linuxN(x) __CONCAT(__CONCAT(__CONCAT(linux,__ELF_WORD_SIZE),_),x) 72 73 #define LINUX_NT_AUXV 6 74 75 static void __linuxN(note_fpregset)(void *, struct sbuf *, size_t *); 76 static void __linuxN(note_prpsinfo)(void *, struct sbuf *, size_t *); 77 static void __linuxN(note_prstatus)(void *, struct sbuf *, size_t *); 78 static void __linuxN(note_threadmd)(void *, struct sbuf *, size_t *); 79 static void __linuxN(note_nt_auxv)(void *, struct sbuf *, size_t *); 80 81 void 82 __linuxN(prepare_notes)(struct thread *td, struct note_info_list *list, 83 size_t *sizep) 84 { 85 struct proc *p; 86 struct thread *thr; 87 size_t size; 88 89 p = td->td_proc; 90 size = 0; 91 92 /* 93 * To have the debugger select the right thread (LWP) as the initial 94 * thread, we dump the state of the thread passed to us in td first. 95 * This is the thread that causes the core dump and thus likely to 96 * be the right thread one wants to have selected in the debugger. 97 */ 98 thr = td; 99 while (thr != NULL) { 100 size += __elfN(register_note)(td, list, 101 NT_PRSTATUS, __linuxN(note_prstatus), thr); 102 size += __elfN(register_note)(td, list, 103 NT_PRPSINFO, __linuxN(note_prpsinfo), p); 104 size += __elfN(register_note)(td, list, 105 LINUX_NT_AUXV, __linuxN(note_nt_auxv), p); 106 size += __elfN(register_note)(td, list, 107 NT_FPREGSET, __linuxN(note_fpregset), thr); 108 size += __elfN(register_note)(td, list, 109 -1, __linuxN(note_threadmd), thr); 110 111 thr = thr == td ? TAILQ_FIRST(&p->p_threads) : 112 TAILQ_NEXT(thr, td_plist); 113 if (thr == td) 114 thr = TAILQ_NEXT(thr, td_plist); 115 } 116 117 *sizep = size; 118 } 119 120 typedef struct linux_elf_prstatus linux_elf_prstatus_t; 121 #if __ELF_WORD_SIZE == 32 122 typedef struct prpsinfo32 linux_elf_prpsinfo_t; 123 typedef struct fpreg32 linux_elf_prfpregset_t; 124 #else 125 typedef prpsinfo_t linux_elf_prpsinfo_t; 126 typedef prfpregset_t linux_elf_prfpregset_t; 127 #endif 128 129 static void 130 __linuxN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) 131 { 132 struct sbuf sbarg; 133 size_t len; 134 char *cp, *end; 135 struct proc *p; 136 linux_elf_prpsinfo_t *psinfo; 137 int error; 138 139 p = arg; 140 if (sb != NULL) { 141 KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); 142 psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); 143 psinfo->pr_version = PRPSINFO_VERSION; 144 psinfo->pr_psinfosz = sizeof(linux_elf_prpsinfo_t); 145 strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); 146 PROC_LOCK(p); 147 if (p->p_args != NULL) { 148 len = sizeof(psinfo->pr_psargs) - 1; 149 if (len > p->p_args->ar_length) 150 len = p->p_args->ar_length; 151 memcpy(psinfo->pr_psargs, p->p_args->ar_args, len); 152 PROC_UNLOCK(p); 153 error = 0; 154 } else { 155 _PHOLD(p); 156 PROC_UNLOCK(p); 157 sbuf_new(&sbarg, psinfo->pr_psargs, 158 sizeof(psinfo->pr_psargs), SBUF_FIXEDLEN); 159 error = proc_getargv(curthread, p, &sbarg); 160 PRELE(p); 161 if (sbuf_finish(&sbarg) == 0) 162 len = sbuf_len(&sbarg) - 1; 163 else 164 len = sizeof(psinfo->pr_psargs) - 1; 165 sbuf_delete(&sbarg); 166 } 167 if (error || len == 0) 168 strlcpy(psinfo->pr_psargs, p->p_comm, 169 sizeof(psinfo->pr_psargs)); 170 else { 171 KASSERT(len < sizeof(psinfo->pr_psargs), 172 ("len is too long: %zu vs %zu", len, 173 sizeof(psinfo->pr_psargs))); 174 cp = psinfo->pr_psargs; 175 end = cp + len - 1; 176 for (;;) { 177 cp = memchr(cp, '\0', end - cp); 178 if (cp == NULL) 179 break; 180 *cp = ' '; 181 } 182 } 183 psinfo->pr_pid = p->p_pid; 184 sbuf_bcat(sb, psinfo, sizeof(*psinfo)); 185 free(psinfo, M_TEMP); 186 } 187 *sizep = sizeof(*psinfo); 188 } 189 190 static void 191 __linuxN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) 192 { 193 struct thread *td; 194 linux_elf_prstatus_t *status; 195 #if __ELF_WORD_SIZE == 32 196 struct reg32 pr_reg; 197 #else 198 struct reg pr_reg; 199 #endif 200 201 td = arg; 202 if (sb != NULL) { 203 KASSERT(*sizep == sizeof(*status), ("invalid size")); 204 status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); 205 206 /* 207 * XXX: Some fields missing. 208 */ 209 status->pr_cursig = td->td_proc->p_sig; 210 status->pr_pid = td->td_tid; 211 212 #if __ELF_WORD_SIZE == 32 213 fill_regs32(td, &pr_reg); 214 #else 215 fill_regs(td, &pr_reg); 216 #endif 217 bsd_to_linux_regset(&pr_reg, &status->pr_reg); 218 sbuf_bcat(sb, status, sizeof(*status)); 219 free(status, M_TEMP); 220 } 221 *sizep = sizeof(*status); 222 } 223 224 static void 225 __linuxN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) 226 { 227 struct thread *td; 228 linux_elf_prfpregset_t *fpregset; 229 230 td = arg; 231 if (sb != NULL) { 232 KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); 233 fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); 234 #if __ELF_WORD_SIZE == 32 235 fill_fpregs32(td, fpregset); 236 #else 237 fill_fpregs(td, fpregset); 238 #endif 239 sbuf_bcat(sb, fpregset, sizeof(*fpregset)); 240 free(fpregset, M_TEMP); 241 } 242 *sizep = sizeof(*fpregset); 243 } 244 245 /* 246 * Allow for MD specific notes, as well as any MD 247 * specific preparations for writing MI notes. 248 */ 249 static void 250 __linuxN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) 251 { 252 struct thread *td; 253 void *buf; 254 size_t size; 255 256 td = arg; 257 size = *sizep; 258 if (size != 0 && sb != NULL) 259 buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); 260 else 261 buf = NULL; 262 size = 0; 263 __elfN(dump_thread)(td, buf, &size); 264 KASSERT(sb == NULL || *sizep == size, ("invalid size")); 265 if (size != 0 && sb != NULL) 266 sbuf_bcat(sb, buf, size); 267 free(buf, M_TEMP); 268 *sizep = size; 269 } 270 271 static void 272 __linuxN(note_nt_auxv)(void *arg, struct sbuf *sb, size_t *sizep) 273 { 274 struct proc *p; 275 size_t size; 276 277 p = arg; 278 if (sb == NULL) { 279 size = 0; 280 sb = sbuf_new(NULL, NULL, LINUX_AT_COUNT * sizeof(Elf_Auxinfo), 281 SBUF_FIXEDLEN); 282 sbuf_set_drain(sb, sbuf_count_drain, &size); 283 PHOLD(p); 284 proc_getauxv(curthread, p, sb); 285 PRELE(p); 286 sbuf_finish(sb); 287 sbuf_delete(sb); 288 *sizep = size; 289 } else { 290 PHOLD(p); 291 proc_getauxv(curthread, p, sb); 292 PRELE(p); 293 } 294 } 295