xref: /netbsd-src/sys/kern/core_elf32.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: core_elf32.c,v 1.14 2004/09/17 14:11:24 skrll 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.14 2004/09/17 14:11:24 skrll Exp $");
44 
45 /* If not included by core_elf64.c, ELFSIZE won't be defined. */
46 #ifndef ELFSIZE
47 #define	ELFSIZE		32
48 #endif
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/proc.h>
53 #include <sys/vnode.h>
54 #include <sys/exec_elf.h>
55 #include <sys/ptrace.h>
56 
57 #include <machine/reg.h>
58 
59 #include <uvm/uvm_extern.h>
60 
61 struct countsegs_state {
62 	int	npsections;
63 };
64 
65 int	ELFNAMEEND(coredump_countsegs)(struct proc *, struct vnode *,
66 	    struct ucred *, struct uvm_coredump_state *);
67 
68 struct writesegs_state {
69 	off_t	offset;
70 	off_t	secoff;
71 };
72 
73 int	ELFNAMEEND(coredump_writeseghdrs)(struct proc *, struct vnode *,
74 	    struct ucred *, struct uvm_coredump_state *);
75 int	ELFNAMEEND(coredump_writesegs)(struct proc *, struct vnode *,
76 	    struct ucred *, struct uvm_coredump_state *);
77 
78 int	ELFNAMEEND(coredump_notes)(struct proc *, struct lwp *, struct vnode *,
79 	    struct ucred *, size_t *, off_t);
80 int	ELFNAMEEND(coredump_note)(struct proc *, struct lwp *, struct vnode *,
81 	    struct ucred *, size_t *, off_t);
82 
83 #define	ELFROUNDSIZE	4	/* XXX Should it be sizeof(Elf_Word)? */
84 #define	elfround(x)	roundup((x), ELFROUNDSIZE)
85 
86 int
87 ELFNAMEEND(coredump)(struct lwp *l, struct vnode *vp, struct ucred *cred)
88 {
89 	struct proc *p;
90 	Elf_Ehdr ehdr;
91 	Elf_Phdr phdr;
92 	struct countsegs_state cs;
93 	struct writesegs_state ws;
94 	off_t notestart, secstart;
95 	size_t notesize;
96 	int error;
97 
98 	p = l->l_proc;
99 	/*
100 	 * We have to make a total of 3 passes across the map:
101 	 *
102 	 *	1. Count the number of map entries (the number of
103 	 *	   PT_LOAD sections).
104 	 *
105 	 *	2. Write the P-section headers.
106 	 *
107 	 *	3. Write the P-sections.
108 	 */
109 
110 	/* Pass 1: count the entries. */
111 	cs.npsections = 0;
112 	error = uvm_coredump_walkmap(p, vp, cred,
113 	    ELFNAMEEND(coredump_countsegs), &cs);
114 	if (error)
115 		return (error);
116 
117 	/* Get the size of the notes. */
118 	error = ELFNAMEEND(coredump_notes)(p, l, vp, cred, &notesize, 0);
119 	if (error)
120 		return (error);
121 
122 	/* Count the PT_NOTE section. */
123 	cs.npsections++;
124 
125 	memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
126 #if ELFSIZE == 32
127 	ehdr.e_ident[EI_CLASS] = ELFCLASS32;
128 #elif ELFSIZE == 64
129 	ehdr.e_ident[EI_CLASS] = ELFCLASS64;
130 #endif
131 	ehdr.e_ident[EI_DATA] = ELFDEFNNAME(MACHDEP_ENDIANNESS);
132 	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
133 	/* XXX Should be the OSABI/ABI version of the executable. */
134 	ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV;
135 	ehdr.e_ident[EI_ABIVERSION] = 0;
136 
137 	ehdr.e_type = ET_CORE;
138 	/* XXX This should be the e_machine of the executable. */
139 	ehdr.e_machine = ELFDEFNNAME(MACHDEP_ID);
140 	ehdr.e_version = EV_CURRENT;
141 	ehdr.e_entry = 0;
142 	ehdr.e_phoff = sizeof(ehdr);
143 	ehdr.e_shoff = 0;
144 	ehdr.e_flags = 0;
145 	ehdr.e_ehsize = sizeof(ehdr);
146 	ehdr.e_phentsize = sizeof(Elf_Phdr);
147 	ehdr.e_phnum = cs.npsections;
148 	ehdr.e_shentsize = 0;
149 	ehdr.e_shnum = 0;
150 	ehdr.e_shstrndx = 0;
151 
152 	/* Write out the ELF header. */
153 	error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&ehdr,
154 	    (int)sizeof(ehdr), (off_t)0,
155 	    UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, NULL, NULL);
156 
157 	ws.offset = ehdr.e_phoff;
158 	notestart = ws.offset + (sizeof(phdr) * cs.npsections);
159 	secstart = round_page(notestart + notesize);
160 
161 	/* Pass 2: now write the P-section headers. */
162 	ws.secoff = secstart;
163 	error = uvm_coredump_walkmap(p, vp, cred,
164 	    ELFNAMEEND(coredump_writeseghdrs), &ws);
165 	if (error)
166 		return (error);
167 
168 	/* Write out the PT_NOTE header. */
169 	phdr.p_type = PT_NOTE;
170 	phdr.p_offset = notestart;
171 	phdr.p_vaddr = 0;
172 	phdr.p_paddr = 0;
173 	phdr.p_filesz = notesize;
174 	phdr.p_memsz = 0;
175 	phdr.p_flags = PF_R;
176 	phdr.p_align = ELFROUNDSIZE;
177 
178 	error = vn_rdwr(UIO_WRITE, vp,
179 	    (caddr_t)&phdr, sizeof(phdr),
180 	    ws.offset, UIO_SYSSPACE,
181 	    IO_NODELOCKED|IO_UNIT, cred, NULL, NULL);
182 	if (error)
183 		return (error);
184 
185 	ws.offset += sizeof(phdr);
186 
187 #ifdef DIAGNOSTIC
188 	if (ws.offset != notestart)
189 		panic("coredump: offset %lld != notestart %lld",
190 		    (long long) ws.offset, (long long) notestart);
191 #endif
192 
193 	/* Write out the notes. */
194 	error = ELFNAMEEND(coredump_notes)(p, l, vp, cred, &notesize, ws.offset);
195 
196 	ws.offset += notesize;
197 
198 #ifdef DIAGNOSTIC
199 	if (round_page(ws.offset) != secstart)
200 		panic("coredump: offset %lld != secstart %lld",
201 		    (long long) round_page(ws.offset), (long long) secstart);
202 #endif
203 
204 	/* Pass 3: finally, write the sections themselves. */
205 	ws.secoff = secstart;
206 	error = uvm_coredump_walkmap(p, vp, cred,
207 	    ELFNAMEEND(coredump_writesegs), &ws);
208 	if (error)
209 		return (error);
210 
211 	return (error);
212 }
213 
214 int
215 ELFNAMEEND(coredump_countsegs)(struct proc *p, struct vnode *vp,
216     struct ucred *cred, struct uvm_coredump_state *us)
217 {
218 	struct countsegs_state *cs = us->cookie;
219 
220 	cs->npsections++;
221 	return (0);
222 }
223 
224 int
225 ELFNAMEEND(coredump_writeseghdrs)(struct proc *p, struct vnode *vp,
226     struct ucred *cred, struct uvm_coredump_state *us)
227 {
228 	struct writesegs_state *ws = us->cookie;
229 	Elf_Phdr phdr;
230 	vsize_t size;
231 	int error;
232 
233 	size = us->end - us->start;
234 
235 	phdr.p_type = PT_LOAD;
236 	phdr.p_offset = ws->secoff;
237 	phdr.p_vaddr = us->start;
238 	phdr.p_paddr = 0;
239 	phdr.p_filesz = (us->flags & UVM_COREDUMP_NODUMP) ? 0 : size;
240 	phdr.p_memsz = size;
241 	phdr.p_flags = 0;
242 	if (us->prot & VM_PROT_READ)
243 		phdr.p_flags |= PF_R;
244 	if (us->prot & VM_PROT_WRITE)
245 		phdr.p_flags |= PF_W;
246 	if (us->prot & VM_PROT_EXECUTE)
247 		phdr.p_flags |= PF_X;
248 	phdr.p_align = PAGE_SIZE;
249 
250 	error = vn_rdwr(UIO_WRITE, vp,
251 	    (caddr_t)&phdr, sizeof(phdr),
252 	    ws->offset, UIO_SYSSPACE,
253 	    IO_NODELOCKED|IO_UNIT, cred, NULL, NULL);
254 	if (error)
255 		return (error);
256 
257 	ws->offset += sizeof(phdr);
258 	ws->secoff += phdr.p_filesz;
259 
260 	return (0);
261 }
262 
263 int
264 ELFNAMEEND(coredump_writesegs)(struct proc *p, struct vnode *vp,
265     struct ucred *cred, struct uvm_coredump_state *us)
266 {
267 	struct writesegs_state *ws = us->cookie;
268 	vsize_t size;
269 	int error;
270 
271 	if (us->flags & UVM_COREDUMP_NODUMP)
272 		return (0);
273 
274 	size = us->end - us->start;
275 
276 	error = vn_rdwr(UIO_WRITE, vp,
277 	    (caddr_t) us->start, size,
278 	    ws->secoff, UIO_USERSPACE,
279 	    IO_NODELOCKED|IO_UNIT, cred, NULL, p);
280 	if (error)
281 		return (error);
282 
283 	ws->secoff += size;
284 
285 	return (0);
286 }
287 
288 int
289 ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l, struct vnode *vp,
290     struct ucred *cred, size_t *sizep, off_t offset)
291 {
292 	struct netbsd_elfcore_procinfo cpi;
293 	Elf_Nhdr nhdr;
294 	size_t size, notesize;
295 	int error;
296 	struct lwp *l0;
297 
298 	size = 0;
299 
300 	/* First, write an elfcore_procinfo. */
301 	notesize = sizeof(nhdr) + elfround(sizeof(ELF_NOTE_NETBSD_CORE_NAME)) +
302 	    elfround(sizeof(cpi));
303 	if (offset) {
304 		cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION;
305 		cpi.cpi_cpisize = sizeof(cpi);
306 		cpi.cpi_signo = p->p_sigctx.ps_signo;
307 		cpi.cpi_sigcode = p->p_sigctx.ps_code;
308 		cpi.cpi_siglwp = p->p_sigctx.ps_lwp;
309 
310 		memcpy(&cpi.cpi_sigpend, &p->p_sigctx.ps_siglist,
311 		    sizeof(cpi.cpi_sigpend));
312 		memcpy(&cpi.cpi_sigmask, &p->p_sigctx.ps_sigmask,
313 		    sizeof(cpi.cpi_sigmask));
314 		memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore,
315 		    sizeof(cpi.cpi_sigignore));
316 		memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch,
317 		    sizeof(cpi.cpi_sigcatch));
318 
319 		cpi.cpi_pid = p->p_pid;
320 		cpi.cpi_ppid = p->p_pptr->p_pid;
321 		cpi.cpi_pgrp = p->p_pgid;
322 		cpi.cpi_sid = p->p_session->s_sid;
323 
324 		cpi.cpi_ruid = p->p_cred->p_ruid;
325 		cpi.cpi_euid = p->p_ucred->cr_uid;
326 		cpi.cpi_svuid = p->p_cred->p_svuid;
327 
328 		cpi.cpi_rgid = p->p_cred->p_rgid;
329 		cpi.cpi_egid = p->p_ucred->cr_gid;
330 		cpi.cpi_svgid = p->p_cred->p_svgid;
331 
332 		cpi.cpi_nlwps = p->p_nlwps;
333 		strlcpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name));
334 
335 		nhdr.n_namesz = sizeof(ELF_NOTE_NETBSD_CORE_NAME);
336 		nhdr.n_descsz = sizeof(cpi);
337 		nhdr.n_type = ELF_NOTE_NETBSD_CORE_PROCINFO;
338 
339 		error = ELFNAMEEND(coredump_writenote)(p, vp, cred, offset,
340 		    &nhdr, ELF_NOTE_NETBSD_CORE_NAME, &cpi);
341 		if (error)
342 			return (error);
343 
344 		offset += notesize;
345 	}
346 
347 	size += notesize;
348 
349 	/* XXX Add hook for machdep per-proc notes. */
350 
351 	/*
352 	 * Now write the register info for the thread that caused the
353 	 * coredump.
354 	 */
355 	error = ELFNAMEEND(coredump_note)(p, l, vp, cred, &notesize, offset);
356 	if (error)
357 		return (error);
358 	if (offset)
359 		offset += notesize;
360 	size += notesize;
361 
362 	/*
363 	 * Now, for each LWP, write the register info and any other
364 	 * per-LWP notes.
365 	 */
366 	LIST_FOREACH(l0, &p->p_lwps, l_sibling) {
367 		if (l0 == l)		/* we've taken care of this thread */
368 			continue;
369 		error = ELFNAMEEND(coredump_note)(p, l0, vp, cred,
370 		    &notesize, offset);
371 		if (error)
372 			return (error);
373 		if (offset)
374 			offset += notesize;
375 		size += notesize;
376 	}
377 
378 	*sizep = size;
379 	return (0);
380 }
381 
382 int
383 ELFNAMEEND(coredump_note)(struct proc *p, struct lwp *l, struct vnode *vp,
384     struct ucred *cred, size_t *sizep, off_t offset)
385 {
386 	Elf_Nhdr nhdr;
387 	int size, notesize, error;
388 	int namesize;
389 	char name[64];
390 	struct reg intreg;
391 #ifdef PT_GETFPREGS
392 	struct fpreg freg;
393 #endif
394 
395 	size = 0;
396 
397 	snprintf(name, sizeof(name), "%s@%d", ELF_NOTE_NETBSD_CORE_NAME,
398 	    l->l_lid);
399 	namesize = strlen(name) + 1;
400 
401 	notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(intreg));
402 	if (offset) {
403 		PHOLD(l);
404 		error = process_read_regs(l, &intreg);
405 		PRELE(l);
406 		if (error)
407 			return (error);
408 
409 		nhdr.n_namesz = namesize;
410 		nhdr.n_descsz = sizeof(intreg);
411 		nhdr.n_type = PT_GETREGS;
412 
413 		error = ELFNAMEEND(coredump_writenote)(p, vp, cred,
414 		    offset, &nhdr, name, &intreg);
415 		if (error)
416 			return (error);
417 
418 		offset += notesize;
419 	}
420 	size += notesize;
421 
422 #ifdef PT_GETFPREGS
423 	notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(freg));
424 	if (offset) {
425 		PHOLD(l);
426 		error = process_read_fpregs(l, &freg);
427 		PRELE(l);
428 		if (error)
429 			return (error);
430 
431 		nhdr.n_namesz = namesize;
432 		nhdr.n_descsz = sizeof(freg);
433 		nhdr.n_type = PT_GETFPREGS;
434 
435 		error = ELFNAMEEND(coredump_writenote)(p, vp, cred,
436 		    offset, &nhdr, name, &freg);
437 		if (error)
438 			return (error);
439 
440 		offset += notesize;
441 	}
442 	size += notesize;
443 #endif
444 	*sizep = size;
445 	/* XXX Add hook for machdep per-LWP notes. */
446 	return (0);
447 }
448 
449 int
450 ELFNAMEEND(coredump_writenote)(struct proc *p, struct vnode *vp,
451     struct ucred *cred, off_t offset, Elf_Nhdr *nhdr, const char *name,
452     void *data)
453 {
454 	int error;
455 
456 	error = vn_rdwr(UIO_WRITE, vp,
457 	    (caddr_t) nhdr, sizeof(*nhdr),
458 	    offset, UIO_SYSSPACE,
459 	    IO_NODELOCKED|IO_UNIT, cred, NULL, NULL);
460 	if (error)
461 		return (error);
462 
463 	offset += sizeof(*nhdr);
464 
465 	error = vn_rdwr(UIO_WRITE, vp,
466 	    (caddr_t)name, nhdr->n_namesz,
467 	    offset, UIO_SYSSPACE,
468 	    IO_NODELOCKED|IO_UNIT, cred, NULL, NULL);
469 	if (error)
470 		return (error);
471 
472 	offset += elfround(nhdr->n_namesz);
473 
474 	error = vn_rdwr(UIO_WRITE, vp,
475 	    data, nhdr->n_descsz,
476 	    offset, UIO_SYSSPACE,
477 	    IO_NODELOCKED|IO_UNIT, cred, NULL, NULL);
478 
479 	return (error);
480 }
481