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/reg.h> 53 #include <sys/sbuf.h> 54 #include <sys/sysent.h> 55 #include <sys/user.h> 56 #include <sys/vnode.h> 57 58 #include <machine/elf.h> 59 60 #if __ELF_WORD_SIZE == 32 61 #define linux_pt_regset linux_pt_regset32 62 #define bsd_to_linux_regset bsd_to_linux_regset32 63 #include <machine/../linux32/linux.h> 64 #else 65 #include <machine/../linux/linux.h> 66 #endif 67 #include <compat/linux/linux_elf.h> 68 #include <compat/linux/linux_emul.h> 69 #include <compat/linux/linux_misc.h> 70 71 /* This adds "linux32_" and "linux64_" prefixes. */ 72 #define __linuxN(x) __CONCAT(__CONCAT(__CONCAT(linux,__ELF_WORD_SIZE),_),x) 73 74 #define LINUX_NT_AUXV 6 75 76 static void __linuxN(note_fpregset)(void *, struct sbuf *, size_t *); 77 static void __linuxN(note_prpsinfo)(void *, struct sbuf *, size_t *); 78 static void __linuxN(note_prstatus)(void *, struct sbuf *, size_t *); 79 static void __linuxN(note_threadmd)(void *, struct sbuf *, size_t *); 80 static void __linuxN(note_nt_auxv)(void *, struct sbuf *, size_t *); 81 82 void 83 __linuxN(prepare_notes)(struct thread *td, struct note_info_list *list, 84 size_t *sizep) 85 { 86 struct proc *p; 87 struct thread *thr; 88 size_t size; 89 90 p = td->td_proc; 91 size = 0; 92 93 /* 94 * To have the debugger select the right thread (LWP) as the initial 95 * thread, we dump the state of the thread passed to us in td first. 96 * This is the thread that causes the core dump and thus likely to 97 * be the right thread one wants to have selected in the debugger. 98 */ 99 thr = td; 100 while (thr != NULL) { 101 size += __elfN(register_note)(td, list, 102 NT_PRSTATUS, __linuxN(note_prstatus), thr); 103 size += __elfN(register_note)(td, list, 104 NT_PRPSINFO, __linuxN(note_prpsinfo), p); 105 size += __elfN(register_note)(td, list, 106 LINUX_NT_AUXV, __linuxN(note_nt_auxv), p); 107 size += __elfN(register_note)(td, list, 108 NT_FPREGSET, __linuxN(note_fpregset), thr); 109 size += __elfN(register_note)(td, list, 110 -1, __linuxN(note_threadmd), thr); 111 112 thr = thr == td ? TAILQ_FIRST(&p->p_threads) : 113 TAILQ_NEXT(thr, td_plist); 114 if (thr == td) 115 thr = TAILQ_NEXT(thr, td_plist); 116 } 117 118 *sizep = size; 119 } 120 121 typedef struct linux_elf_prstatus linux_elf_prstatus_t; 122 #if __ELF_WORD_SIZE == 32 123 typedef struct prpsinfo32 linux_elf_prpsinfo_t; 124 typedef struct fpreg32 linux_elf_prfpregset_t; 125 #else 126 typedef prpsinfo_t linux_elf_prpsinfo_t; 127 typedef prfpregset_t linux_elf_prfpregset_t; 128 #endif 129 130 static void 131 __linuxN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) 132 { 133 struct sbuf sbarg; 134 size_t len; 135 char *cp, *end; 136 struct proc *p; 137 linux_elf_prpsinfo_t *psinfo; 138 int error; 139 140 p = arg; 141 if (sb != NULL) { 142 KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); 143 psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); 144 psinfo->pr_version = PRPSINFO_VERSION; 145 psinfo->pr_psinfosz = sizeof(linux_elf_prpsinfo_t); 146 strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); 147 PROC_LOCK(p); 148 if (p->p_args != NULL) { 149 len = sizeof(psinfo->pr_psargs) - 1; 150 if (len > p->p_args->ar_length) 151 len = p->p_args->ar_length; 152 memcpy(psinfo->pr_psargs, p->p_args->ar_args, len); 153 PROC_UNLOCK(p); 154 error = 0; 155 } else { 156 _PHOLD(p); 157 PROC_UNLOCK(p); 158 sbuf_new(&sbarg, psinfo->pr_psargs, 159 sizeof(psinfo->pr_psargs), SBUF_FIXEDLEN); 160 error = proc_getargv(curthread, p, &sbarg); 161 PRELE(p); 162 if (sbuf_finish(&sbarg) == 0) 163 len = sbuf_len(&sbarg) - 1; 164 else 165 len = sizeof(psinfo->pr_psargs) - 1; 166 sbuf_delete(&sbarg); 167 } 168 if (error || len == 0) 169 strlcpy(psinfo->pr_psargs, p->p_comm, 170 sizeof(psinfo->pr_psargs)); 171 else { 172 KASSERT(len < sizeof(psinfo->pr_psargs), 173 ("len is too long: %zu vs %zu", len, 174 sizeof(psinfo->pr_psargs))); 175 cp = psinfo->pr_psargs; 176 end = cp + len - 1; 177 for (;;) { 178 cp = memchr(cp, '\0', end - cp); 179 if (cp == NULL) 180 break; 181 *cp = ' '; 182 } 183 } 184 psinfo->pr_pid = p->p_pid; 185 sbuf_bcat(sb, psinfo, sizeof(*psinfo)); 186 free(psinfo, M_TEMP); 187 } 188 *sizep = sizeof(*psinfo); 189 } 190 191 static void 192 __linuxN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) 193 { 194 struct thread *td; 195 linux_elf_prstatus_t *status; 196 #if __ELF_WORD_SIZE == 32 197 struct reg32 pr_reg; 198 #else 199 struct reg pr_reg; 200 #endif 201 202 td = arg; 203 if (sb != NULL) { 204 KASSERT(*sizep == sizeof(*status), ("invalid size")); 205 status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); 206 207 /* 208 * XXX: Some fields missing. 209 */ 210 status->pr_cursig = td->td_proc->p_sig; 211 status->pr_pid = td->td_tid; 212 213 #if __ELF_WORD_SIZE == 32 214 fill_regs32(td, &pr_reg); 215 #else 216 fill_regs(td, &pr_reg); 217 #endif 218 bsd_to_linux_regset(&pr_reg, &status->pr_reg); 219 sbuf_bcat(sb, status, sizeof(*status)); 220 free(status, M_TEMP); 221 } 222 *sizep = sizeof(*status); 223 } 224 225 static void 226 __linuxN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) 227 { 228 struct thread *td; 229 linux_elf_prfpregset_t *fpregset; 230 231 td = arg; 232 if (sb != NULL) { 233 KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); 234 fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); 235 #if __ELF_WORD_SIZE == 32 236 fill_fpregs32(td, fpregset); 237 #else 238 fill_fpregs(td, fpregset); 239 #endif 240 sbuf_bcat(sb, fpregset, sizeof(*fpregset)); 241 free(fpregset, M_TEMP); 242 } 243 *sizep = sizeof(*fpregset); 244 } 245 246 /* 247 * Allow for MD specific notes, as well as any MD 248 * specific preparations for writing MI notes. 249 */ 250 static void 251 __linuxN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) 252 { 253 struct thread *td; 254 void *buf; 255 size_t size; 256 257 td = arg; 258 size = *sizep; 259 if (size != 0 && sb != NULL) 260 buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); 261 else 262 buf = NULL; 263 size = 0; 264 __elfN(dump_thread)(td, buf, &size); 265 KASSERT(sb == NULL || *sizep == size, ("invalid size")); 266 if (size != 0 && sb != NULL) 267 sbuf_bcat(sb, buf, size); 268 free(buf, M_TEMP); 269 *sizep = size; 270 } 271 272 static void 273 __linuxN(note_nt_auxv)(void *arg, struct sbuf *sb, size_t *sizep) 274 { 275 struct proc *p; 276 size_t size; 277 278 p = arg; 279 if (sb == NULL) { 280 size = 0; 281 sb = sbuf_new(NULL, NULL, LINUX_AT_COUNT * sizeof(Elf_Auxinfo), 282 SBUF_FIXEDLEN); 283 sbuf_set_drain(sb, sbuf_count_drain, &size); 284 PHOLD(p); 285 proc_getauxv(curthread, p, sb); 286 PRELE(p); 287 sbuf_finish(sb); 288 sbuf_delete(sb); 289 *sizep = size; 290 } else { 291 PHOLD(p); 292 proc_getauxv(curthread, p, sb); 293 PRELE(p); 294 } 295 } 296