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