/* $NetBSD: core_elf32.c,v 1.67 2021/01/02 02:13:42 rin Exp $ */
/*
* Copyright (c) 2001 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* core_elf32.c/core_elf64.c: Support for the Elf32/Elf64 core file format.
*/
/*
* We need to know how big the 'notes' are before we write the main header.
* To avoid problems with double-processing we save the data.
*/
struct note_buf {
struct note_buf *nb_next;
unsigned char nb_data[4096 - sizeof (void *)];
};
struct note_state {
struct note_buf *ns_first;
struct note_buf *ns_last;
unsigned int ns_count; /* Of full buffers */
unsigned int ns_offset; /* Write point in last buffer */
};
static int ELFNAMEEND(coredump_getseghdrs)(struct uvm_coredump_state *);
static int ELFNAMEEND(coredump_notes)(struct lwp *, struct note_state *);
static int ELFNAMEEND(coredump_note)(struct lwp *, struct note_state *);
/* The 'note' section names and data are always 4-byte aligned. */
#define ELFROUNDSIZE 4 /* XXX Should it be sizeof(Elf_Word)? */
/* Get all of the notes (mostly all the registers). */
ns.ns_first = kmem_alloc(sizeof *ns.ns_first, KM_SLEEP);
ns.ns_last = ns.ns_first;
ns.ns_count = 0;
ns.ns_offset = 0;
error = ELFNAMEEND(coredump_notes)(l, &ns);
ns.ns_last->nb_next = NULL;
if (error)
goto out;
notesize = ns.ns_count * sizeof nb->nb_data + ns.ns_offset;
/*
* We have to make a total of 3 passes across the map:
*
* 1. Count the number of map entries (the number of
* PT_LOAD sections in the dump).
*
* 2. Write the P-section headers.
*
* 3. Write the P-sections.
*/
/* Pass 1: count the entries. */
MODULE_HOOK_CALL(uvm_coredump_count_segs_hook,
(l->l_proc), 0, npsections);
/* Allow for the PT_NOTE section. */
npsections++;
/* Build the main elf header */
memset(&ehdr.e_ident[EI_PAD], 0, sizeof(ehdr.e_ident) - EI_PAD);
memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
#if ELFSIZE == 32
ehdr.e_ident[EI_CLASS] = ELFCLASS32;
#elif ELFSIZE == 64
ehdr.e_ident[EI_CLASS] = ELFCLASS64;
#endif
ehdr.e_ident[EI_DATA] = ELFDEFNNAME(MACHDEP_ENDIANNESS);
ehdr.e_ident[EI_VERSION] = EV_CURRENT;
/*
* NetBSD sets generic SYSV OSABI and ABI version 0
* Native ELF files are distinguishable with NetBSD specific notes
*/
ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV;
ehdr.e_ident[EI_ABIVERSION] = 0;
/* Write the P-section headers followed by the PT_NOTE header */
MODULE_HOOK_CALL(coredump_write_hook, (cookie, UIO_SYSSPACE, psections,
psectionssize), ENOSYS, error);
if (error)
goto out;
/* Don't bother writing out trailing zeros */
while (realsize > 0) {
long buf[1024 / sizeof(long)];
size_t slen = realsize > sizeof(buf) ? sizeof(buf) : realsize;
const long *ep;
int i;
end -= slen;
if ((error = copyin_proc(ws->p, (void *)end, buf, slen)) != 0) {
/*
* In case of any errors of scanning the segments reset
* their content to a default value with zeros. This is
* achieved with shortening the p_filesz parameter.
*
* This allows to emit core(5) files for a process
* regardless of its state of mappings, such as mapping
* pages after EOF in a file.
*/
realsize -= slen;
continue;
}
ep = (const long *) &buf[slen / sizeof(buf[0])];
for (i = 0, ep--; buf <= ep; ep--, i++) {
if (*ep)
break;
}
realsize -= i * sizeof(buf[0]);
if (i * sizeof(buf[0]) < slen)
break;
}
/*
* per-LWP pending signals are stored in PT_LWPSTATUS@nnn.
*/
memcpy(&cpi.cpi_sigpend, &p->p_sigpend.sp_set, sizeof(cpi.cpi_sigpend));
/*
* Signal mask is stored on a per-LWP basis in PT_LWPSTATUS@nnn.
* For compatibility purposes, cpi_sigmask is present, but zeroed.
*/
memset(&cpi.cpi_sigmask, 0, sizeof(cpi.cpi_sigmask));
static int
ELFNAMEEND(coredump_notes)(struct lwp *l, struct note_state *ns)
{
int error;
struct lwp *l0;
struct proc *p = l->l_proc;
coredump_note_procinfo(l, ns);
error = coredump_note_auxv(l, ns);
if (error)
return error;
/* XXX Add hook for machdep per-proc notes. */
/*
* Now write the register info for the thread that caused the
* coredump.
*/
error = ELFNAMEEND(coredump_note)(l, ns);
if (error)
return error;
/*
* Now, for each LWP, write the register info and any other
* per-LWP notes.
* Lock in case this is a gcore requested dump.
*/
mutex_enter(p->p_lock);
LIST_FOREACH(l0, &p->p_lwps, l_sibling) {
if (l0 == l) /* we've taken care of this thread */
continue;
error = ELFNAMEEND(coredump_note)(l0, ns);
if (error)
break;
}
mutex_exit(p->p_lock);
/*
* Just copy the data into a buffer list.
* All but the last buffer is full.
*/
for (;;) {
copylen = uimin(len, sizeof(nb->nb_data) - ns->ns_offset);
wp = nb->nb_data + ns->ns_offset;
memcpy(wp, data, copylen);
if (copylen == len)
break;
nb->nb_next = kmem_alloc(sizeof(*nb->nb_next), KM_SLEEP);
nb = nb->nb_next;
ns->ns_last = nb;
ns->ns_count++;
ns->ns_offset = 0;
len -= copylen;
data = (const unsigned char *)data + copylen;
}