1 /* $NetBSD: core_elf32.c,v 1.71 2026/02/15 21:51:08 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * core_elf32.c/core_elf64.c: Support for the Elf32/Elf64 core file format. 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.71 2026/02/15 21:51:08 christos Exp $"); 44 45 #ifdef _KERNEL_OPT 46 #include "opt_compat_netbsd32.h" 47 #endif 48 49 #ifndef ELFSIZE 50 #define ELFSIZE 32 51 #endif 52 53 #include <sys/param.h> 54 #include <sys/types.h> 55 56 #include <sys/compat_stub.h> 57 #include <sys/exec.h> 58 #include <sys/exec_elf.h> 59 #include <sys/kauth.h> 60 #include <sys/kmem.h> 61 #include <sys/proc.h> 62 #include <sys/ptrace.h> 63 #include <sys/sdt.h> 64 #include <sys/systm.h> 65 #include <sys/vnode.h> 66 67 #include <machine/reg.h> 68 69 #include <uvm/uvm_extern.h> 70 71 #ifdef COMPAT_NETBSD32 72 #include <compat/netbsd32/netbsd32.h> 73 #endif 74 75 struct writesegs_state { 76 Elf_Phdr *psections; 77 proc_t *p; 78 off_t secoff; 79 size_t npsections; 80 }; 81 82 /* 83 * We need to know how big the 'notes' are before we write the main header. 84 * To avoid problems with double-processing we save the data. 85 */ 86 struct note_buf { 87 struct note_buf *nb_next; 88 unsigned char nb_data[4096 - sizeof (void *)]; 89 }; 90 91 struct note_state { 92 struct note_buf *ns_first; 93 struct note_buf *ns_last; 94 unsigned int ns_count; /* Of full buffers */ 95 unsigned int ns_offset; /* Write point in last buffer */ 96 }; 97 98 static int ELFNAMEEND(coredump_getseghdrs)(struct uvm_coredump_state *); 99 100 static int ELFNAMEEND(coredump_notes)(struct lwp *, struct note_state *); 101 static int ELFNAMEEND(coredump_note)(struct lwp *, struct note_state *); 102 103 /* The 'note' section names and data are always 4-byte aligned. */ 104 #define ELFROUNDSIZE sizeof(Elf_Word) 105 106 #define elf_read_lwpstatus CONCAT(process_read_lwpstatus, ELFSIZE) 107 #define elf_lwpstatus CONCAT(process_lwpstatus, ELFSIZE) 108 109 #define elf_process_read_regs CONCAT(process_read_regs, ELFSIZE) 110 #define elf_process_read_fpregs CONCAT(process_read_fpregs, ELFSIZE) 111 #define elf_reg CONCAT(process_reg, ELFSIZE) 112 #define elf_fpreg CONCAT(process_fpreg, ELFSIZE) 113 114 int 115 ELFNAMEEND(real_coredump)(struct lwp *l, struct coredump_iostate *cookie) 116 { 117 Elf_Ehdr ehdr; 118 Elf_Shdr shdr; 119 Elf_Phdr *psections; 120 size_t psectionssize; 121 int npsections; 122 struct writesegs_state ws; 123 off_t notestart; 124 size_t notesize; 125 int error, i; 126 off_t offset __diagused; 127 128 struct note_state ns; 129 struct note_buf *nb; 130 131 psections = NULL; 132 133 /* Get all of the notes (mostly all the registers). */ 134 ns.ns_first = kmem_alloc(sizeof *ns.ns_first, KM_SLEEP); 135 ns.ns_last = ns.ns_first; 136 ns.ns_count = 0; 137 ns.ns_offset = 0; 138 error = ELFNAMEEND(coredump_notes)(l, &ns); 139 ns.ns_last->nb_next = NULL; 140 if (error) 141 goto out; 142 notesize = ns.ns_count * sizeof nb->nb_data + ns.ns_offset; 143 144 /* 145 * We have to make a total of 3 passes across the map: 146 * 147 * 1. Count the number of map entries (the number of 148 * PT_LOAD sections in the dump). 149 * 150 * 2. Write the P-section headers. 151 * 152 * 3. Write the P-sections. 153 */ 154 155 /* Pass 1: count the entries. */ 156 MODULE_HOOK_CALL(uvm_coredump_count_segs_hook, 157 (l->l_proc), 0, npsections); 158 /* Allow for the PT_NOTE section. */ 159 npsections++; 160 161 /* Build the main elf header */ 162 memset(&ehdr.e_ident[EI_PAD], 0, sizeof(ehdr.e_ident) - EI_PAD); 163 memcpy(ehdr.e_ident, ELFMAG, SELFMAG); 164 #if ELFSIZE == 32 165 ehdr.e_ident[EI_CLASS] = ELFCLASS32; 166 #elif ELFSIZE == 64 167 ehdr.e_ident[EI_CLASS] = ELFCLASS64; 168 #endif 169 ehdr.e_ident[EI_DATA] = ELFDEFNNAME(MACHDEP_ENDIANNESS); 170 ehdr.e_ident[EI_VERSION] = EV_CURRENT; 171 /* 172 * NetBSD sets generic SYSV OSABI and ABI version 0 173 * Native ELF files are distinguishable with NetBSD specific notes 174 */ 175 ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV; 176 ehdr.e_ident[EI_ABIVERSION] = 0; 177 178 ehdr.e_type = ET_CORE; 179 /* XXX This should be the e_machine of the executable. */ 180 ehdr.e_machine = ELFDEFNNAME(MACHDEP_ID); 181 ehdr.e_version = EV_CURRENT; 182 ehdr.e_entry = 0; 183 ehdr.e_flags = 0; 184 ehdr.e_ehsize = sizeof(ehdr); 185 ehdr.e_phentsize = sizeof(Elf_Phdr); 186 if (npsections < PN_XNUM) { 187 ehdr.e_phnum = npsections; 188 ehdr.e_shentsize = 0; 189 ehdr.e_shnum = 0; 190 ehdr.e_shoff = 0; 191 ehdr.e_phoff = sizeof(ehdr); 192 } else { 193 ehdr.e_phnum = PN_XNUM; 194 ehdr.e_shentsize = sizeof(Elf_Shdr); 195 ehdr.e_shnum = 1; 196 ehdr.e_shoff = sizeof(ehdr); 197 ehdr.e_phoff = sizeof(ehdr) + sizeof(shdr); 198 } 199 ehdr.e_shstrndx = 0; 200 201 #ifdef ELF_MD_COREDUMP_SETUP 202 ELF_MD_COREDUMP_SETUP(l, &ehdr); 203 #endif 204 205 /* Write out the ELF header. */ 206 MODULE_HOOK_CALL(coredump_write_hook, (cookie, UIO_SYSSPACE, &ehdr, 207 sizeof(ehdr)), ENOSYS, error); 208 if (error) 209 goto out; 210 211 /* Write out sections, if needed */ 212 if (npsections >= PN_XNUM) { 213 memset(&shdr, 0, sizeof(shdr)); 214 shdr.sh_type = SHT_NULL; 215 shdr.sh_info = npsections; 216 MODULE_HOOK_CALL(coredump_write_hook, (cookie, UIO_SYSSPACE, 217 &shdr, sizeof(shdr)), ENOSYS, error); 218 if (error) 219 goto out; 220 } 221 222 psectionssize = npsections * sizeof(*psections); 223 notestart = ehdr.e_phoff + psectionssize; 224 225 psections = kmem_zalloc(psectionssize, KM_SLEEP); 226 227 /* Pass 2: now find the P-section headers. */ 228 ws.secoff = notestart + notesize; 229 ws.psections = psections; 230 ws.npsections = npsections - 1; 231 ws.p = l->l_proc; 232 MODULE_HOOK_CALL(uvm_coredump_walkmap_hook, 233 (l->l_proc, ELFNAMEEND(coredump_getseghdrs), &ws), 234 SET_ERROR(ENOSYS), error); 235 if (error) 236 goto out; 237 if (ws.npsections != 0) { 238 /* A section went away */ 239 error = SET_ERROR(ENOMEM); 240 goto out; 241 } 242 243 /* Add the PT_NOTE header after the P-section headers. */ 244 ws.psections->p_type = PT_NOTE; 245 ws.psections->p_offset = notestart; 246 ws.psections->p_vaddr = 0; 247 ws.psections->p_paddr = 0; 248 ws.psections->p_filesz = notesize; 249 ws.psections->p_memsz = 0; 250 ws.psections->p_flags = PF_R; 251 ws.psections->p_align = ELFROUNDSIZE; 252 253 /* Write the P-section headers followed by the PT_NOTE header */ 254 MODULE_HOOK_CALL(coredump_write_hook, (cookie, UIO_SYSSPACE, psections, 255 psectionssize), SET_ERROR(ENOSYS), error); 256 if (error) 257 goto out; 258 259 #ifdef DIAGNOSTIC 260 MODULE_HOOK_CALL(coredump_offset_hook, (cookie), 0, offset); 261 if (offset != notestart) 262 panic("coredump: offset %lld != notestart %lld", 263 (long long) offset, 264 (long long) notestart); 265 #endif 266 267 /* Write out the notes. */ 268 for (nb = ns.ns_first; nb != NULL; nb = nb->nb_next) { 269 MODULE_HOOK_CALL(coredump_write_hook, (cookie, UIO_SYSSPACE, 270 nb->nb_data, 271 nb->nb_next == NULL ? ns.ns_offset : sizeof nb->nb_data), 272 SET_ERROR(ENOSYS), error); 273 if (error) 274 goto out; 275 } 276 277 /* Finally, write the sections themselves. */ 278 for (i = 0; i < npsections - 1; i++) { 279 if (psections[i].p_filesz == 0) 280 continue; 281 282 #ifdef DIAGNOSTIC 283 MODULE_HOOK_CALL(coredump_offset_hook, (cookie), 0, offset); 284 if (offset != psections[i].p_offset) 285 panic("coredump: offset %lld != p_offset[%d] %lld", 286 (long long) offset, i, 287 (long long) psections[i].p_filesz); 288 #endif 289 290 MODULE_HOOK_CALL(coredump_write_hook, (cookie, UIO_USERSPACE, 291 (void *)(vaddr_t)psections[i].p_vaddr, 292 psections[i].p_filesz), SET_ERROR(ENOSYS), error); 293 if (error) 294 goto out; 295 } 296 297 out: 298 if (psections) 299 kmem_free(psections, psectionssize); 300 while ((nb = ns.ns_first) != NULL) { 301 ns.ns_first = nb->nb_next; 302 kmem_free(nb, sizeof *nb); 303 } 304 return (error); 305 } 306 307 static int 308 ELFNAMEEND(coredump_getseghdrs)(struct uvm_coredump_state *us) 309 { 310 struct writesegs_state *ws = us->cookie; 311 Elf_Phdr phdr; 312 vsize_t size, realsize; 313 vaddr_t end; 314 int error; 315 316 /* Don't overrun if there are more sections */ 317 if (ws->npsections == 0) 318 return SET_ERROR(ENOMEM); 319 ws->npsections--; 320 321 size = us->end - us->start; 322 realsize = us->realend - us->start; 323 end = us->realend; 324 325 /* Don't bother writing out trailing zeros */ 326 while (realsize > 0) { 327 long buf[1024 / sizeof(long)]; 328 size_t slen = realsize > sizeof(buf) ? sizeof(buf) : realsize; 329 const long *ep; 330 int i; 331 332 end -= slen; 333 if ((error = copyin_proc(ws->p, (void *)end, buf, slen)) != 0) { 334 /* 335 * In case of any errors of scanning the segments reset 336 * their content to a default value with zeros. This is 337 * achieved with shortening the p_filesz parameter. 338 * 339 * This allows to emit core(5) files for a process 340 * regardless of its state of mappings, such as mapping 341 * pages after EOF in a file. 342 */ 343 realsize -= slen; 344 continue; 345 } 346 347 ep = (const long *) &buf[slen / sizeof(buf[0])]; 348 for (i = 0, ep--; buf <= ep; ep--, i++) { 349 if (*ep) 350 break; 351 } 352 realsize -= i * sizeof(buf[0]); 353 if (i * sizeof(buf[0]) < slen) 354 break; 355 } 356 357 phdr.p_type = PT_LOAD; 358 phdr.p_offset = ws->secoff; 359 phdr.p_vaddr = us->start; 360 phdr.p_paddr = 0; 361 phdr.p_filesz = realsize; 362 phdr.p_memsz = size; 363 phdr.p_flags = 0; 364 if (us->prot & VM_PROT_READ) 365 phdr.p_flags |= PF_R; 366 if (us->prot & VM_PROT_WRITE) 367 phdr.p_flags |= PF_W; 368 if (us->prot & VM_PROT_EXECUTE) 369 phdr.p_flags |= PF_X; 370 phdr.p_align = PAGE_SIZE; 371 372 ws->secoff += phdr.p_filesz; 373 *ws->psections++ = phdr; 374 375 return (0); 376 } 377 378 static void 379 coredump_note_procinfo(struct lwp *l, struct note_state *ns) 380 { 381 struct proc *p; 382 struct netbsd_elfcore_procinfo cpi; 383 384 p = l->l_proc; 385 386 /* First, write an elfcore_procinfo. */ 387 cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION; 388 cpi.cpi_cpisize = sizeof(cpi); 389 cpi.cpi_signo = p->p_sigctx.ps_info._signo; 390 cpi.cpi_sigcode = p->p_sigctx.ps_info._code; 391 cpi.cpi_siglwp = p->p_sigctx.ps_lwp; 392 393 /* 394 * per-LWP pending signals are stored in PT_LWPSTATUS@nnn. 395 */ 396 memcpy(&cpi.cpi_sigpend, &p->p_sigpend.sp_set, sizeof(cpi.cpi_sigpend)); 397 398 /* 399 * Signal mask is stored on a per-LWP basis in PT_LWPSTATUS@nnn. 400 * For compatibility purposes, cpi_sigmask is present, but zeroed. 401 */ 402 memset(&cpi.cpi_sigmask, 0, sizeof(cpi.cpi_sigmask)); 403 404 memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore, 405 sizeof(cpi.cpi_sigignore)); 406 memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch, 407 sizeof(cpi.cpi_sigcatch)); 408 409 cpi.cpi_pid = p->p_pid; 410 mutex_enter(&proc_lock); 411 cpi.cpi_ppid = p->p_pptr->p_pid; 412 cpi.cpi_pgrp = p->p_pgid; 413 cpi.cpi_sid = p->p_session->s_sid; 414 mutex_exit(&proc_lock); 415 416 cpi.cpi_ruid = kauth_cred_getuid(l->l_cred); 417 cpi.cpi_euid = kauth_cred_geteuid(l->l_cred); 418 cpi.cpi_svuid = kauth_cred_getsvuid(l->l_cred); 419 420 cpi.cpi_rgid = kauth_cred_getgid(l->l_cred); 421 cpi.cpi_egid = kauth_cred_getegid(l->l_cred); 422 cpi.cpi_svgid = kauth_cred_getsvgid(l->l_cred); 423 424 cpi.cpi_nlwps = p->p_nlwps; 425 (void)strncpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name)); 426 cpi.cpi_name[sizeof(cpi.cpi_name) - 1] = '\0'; 427 428 ELFNAMEEND(coredump_savenote)(ns, ELF_NOTE_NETBSD_CORE_PROCINFO, 429 ELF_NOTE_NETBSD_CORE_NAME, &cpi, sizeof(cpi)); 430 } 431 432 static int 433 coredump_note_auxv(struct lwp *l, struct note_state *ns) 434 { 435 int error; 436 size_t len; 437 void *kauxv; 438 439 if ((error = proc_getauxv(l->l_proc, &kauxv, &len)) != 0) 440 return error; 441 442 ELFNAMEEND(coredump_savenote)(ns, ELF_NOTE_NETBSD_CORE_AUXV, 443 ELF_NOTE_NETBSD_CORE_NAME, kauxv, len); 444 445 kmem_free(kauxv, len); 446 return 0; 447 } 448 449 static int 450 ELFNAMEEND(coredump_notes)(struct lwp *l, struct note_state *ns) 451 { 452 int error; 453 struct lwp *l0; 454 struct proc *p = l->l_proc; 455 456 coredump_note_procinfo(l, ns); 457 error = coredump_note_auxv(l, ns); 458 if (error) 459 return error; 460 461 /* XXX Add hook for machdep per-proc notes. */ 462 463 /* 464 * Now write the register info for the thread that caused the 465 * coredump. 466 */ 467 error = ELFNAMEEND(coredump_note)(l, ns); 468 if (error) 469 return error; 470 471 /* 472 * Now, for each LWP, write the register info and any other 473 * per-LWP notes. 474 * Lock in case this is a gcore requested dump. 475 */ 476 mutex_enter(p->p_lock); 477 LIST_FOREACH(l0, &p->p_lwps, l_sibling) { 478 if (l0 == l) /* we've taken care of this thread */ 479 continue; 480 error = ELFNAMEEND(coredump_note)(l0, ns); 481 if (error) 482 break; 483 } 484 mutex_exit(p->p_lock); 485 486 return error; 487 } 488 489 struct elf_coredump_note_data { 490 char name[64]; 491 elf_lwpstatus els; 492 elf_reg intreg; 493 #ifdef PT_GETFPREGS 494 elf_fpreg freg; 495 #endif 496 }; 497 498 static int 499 ELFNAMEEND(coredump_note)(struct lwp *l, struct note_state *ns) 500 { 501 struct elf_coredump_note_data *d; 502 #ifdef PT_GETFPREGS 503 size_t freglen; 504 #endif 505 int error; 506 507 d = kmem_alloc(sizeof(*d), KM_SLEEP); 508 509 snprintf(d->name, sizeof(d->name), "%s@%d", 510 ELF_NOTE_NETBSD_CORE_NAME, l->l_lid); 511 512 elf_read_lwpstatus(l, &d->els); 513 514 ELFNAMEEND(coredump_savenote)(ns, PT_LWPSTATUS, d->name, &d->els, 515 sizeof(d->els)); 516 517 error = elf_process_read_regs(l, &d->intreg); 518 if (error) 519 goto out; 520 521 ELFNAMEEND(coredump_savenote)(ns, 522 #if ELFSIZE == 32 && defined(PT32_GETREGS) 523 PT32_GETREGS, 524 #else 525 PT_GETREGS, 526 #endif 527 d->name, &d->intreg, sizeof(d->intreg)); 528 529 #ifdef PT_GETFPREGS 530 freglen = sizeof(d->freg); 531 error = elf_process_read_fpregs(l, &d->freg, &freglen); 532 if (error) 533 goto out; 534 535 ELFNAMEEND(coredump_savenote)(ns, 536 # if ELFSIZE == 32 && defined(PT32_GETFPREGS) 537 PT32_GETFPREGS, 538 # else 539 PT_GETFPREGS, 540 # endif 541 d->name, &d->freg, freglen); 542 #endif 543 544 #ifdef COREDUMP_MACHDEP_LWP_NOTES 545 COREDUMP_MACHDEP_LWP_NOTES(l, ns, d->name); 546 #endif 547 548 out: 549 kmem_free(d, sizeof(*d)); 550 return (error); 551 } 552 553 static void 554 save_note_bytes(struct note_state *ns, const void *data, size_t len) 555 { 556 struct note_buf *nb = ns->ns_last; 557 size_t copylen; 558 unsigned char *wp; 559 560 /* 561 * Just copy the data into a buffer list. 562 * All but the last buffer is full. 563 */ 564 for (;;) { 565 copylen = uimin(len, sizeof(nb->nb_data) - ns->ns_offset); 566 wp = nb->nb_data + ns->ns_offset; 567 memcpy(wp, data, copylen); 568 if (copylen == len) 569 break; 570 nb->nb_next = kmem_alloc(sizeof(*nb->nb_next), KM_SLEEP); 571 nb = nb->nb_next; 572 ns->ns_last = nb; 573 ns->ns_count++; 574 ns->ns_offset = 0; 575 len -= copylen; 576 data = (const unsigned char *)data + copylen; 577 } 578 579 while ((copylen & (ELFROUNDSIZE - 1)) && 580 wp + copylen < nb->nb_data + sizeof(nb->nb_data)) 581 wp[copylen++] = 0; 582 583 ns->ns_offset += copylen; 584 } 585 586 void 587 ELFNAMEEND(coredump_savenote)(struct note_state *ns, unsigned int type, 588 const char *name, void *data, size_t data_len) 589 { 590 Elf_Nhdr nhdr; 591 592 nhdr.n_namesz = strlen(name) + 1; 593 nhdr.n_descsz = data_len; 594 nhdr.n_type = type; 595 596 save_note_bytes(ns, &nhdr, sizeof (nhdr)); 597 save_note_bytes(ns, name, nhdr.n_namesz); 598 save_note_bytes(ns, data, data_len); 599 } 600