1 | /* $NetBSD: core_elf32.c,v 1.49 2016/09/29 20:40:53 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.49 2016/09/29 20:40:53 christos Exp $" ); |
44 | |
45 | #ifdef _KERNEL_OPT |
46 | #include "opt_coredump.h" |
47 | #include "opt_compat_netbsd32.h" |
48 | #endif |
49 | |
50 | #ifndef ELFSIZE |
51 | #define ELFSIZE 32 |
52 | #endif |
53 | |
54 | #include <sys/param.h> |
55 | #include <sys/systm.h> |
56 | #include <sys/proc.h> |
57 | #include <sys/vnode.h> |
58 | #include <sys/exec.h> |
59 | #include <sys/exec_elf.h> |
60 | #include <sys/ptrace.h> |
61 | #include <sys/kmem.h> |
62 | #include <sys/kauth.h> |
63 | |
64 | #include <machine/reg.h> |
65 | |
66 | #include <uvm/uvm_extern.h> |
67 | |
68 | #ifdef COREDUMP |
69 | |
70 | struct writesegs_state { |
71 | Elf_Phdr *psections; |
72 | proc_t *p; |
73 | off_t secoff; |
74 | size_t npsections; |
75 | }; |
76 | |
77 | /* |
78 | * We need to know how big the 'notes' are before we write the main header. |
79 | * To avoid problems with double-processing we save the data. |
80 | */ |
81 | struct note_buf { |
82 | struct note_buf *nb_next; |
83 | unsigned char nb_data[4096 - sizeof (void *)]; |
84 | }; |
85 | |
86 | struct note_state { |
87 | struct note_buf *ns_first; |
88 | struct note_buf *ns_last; |
89 | unsigned int ns_count; /* Of full buffers */ |
90 | unsigned int ns_offset; /* Write point in last buffer */ |
91 | }; |
92 | |
93 | static int ELFNAMEEND(coredump_getseghdrs)(struct uvm_coredump_state *); |
94 | |
95 | static int ELFNAMEEND(coredump_notes)(struct lwp *, struct note_state *); |
96 | static int ELFNAMEEND(coredump_note)(struct lwp *, struct note_state *); |
97 | |
98 | /* The 'note' section names and data are always 4-byte aligned. */ |
99 | #define ELFROUNDSIZE 4 /* XXX Should it be sizeof(Elf_Word)? */ |
100 | |
101 | #define elf_process_read_regs CONCAT(process_read_regs, ELFSIZE) |
102 | #define elf_process_read_fpregs CONCAT(process_read_fpregs, ELFSIZE) |
103 | #define elf_reg CONCAT(process_reg, ELFSIZE) |
104 | #define elf_fpreg CONCAT(process_fpreg, ELFSIZE) |
105 | |
106 | int |
107 | ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) |
108 | { |
109 | Elf_Ehdr ehdr; |
110 | Elf_Shdr shdr; |
111 | Elf_Phdr *psections; |
112 | size_t psectionssize; |
113 | int npsections; |
114 | struct writesegs_state ws; |
115 | off_t notestart; |
116 | size_t notesize; |
117 | int error, i; |
118 | |
119 | struct note_state ns; |
120 | struct note_buf *nb; |
121 | |
122 | psections = NULL; |
123 | |
124 | /* Get all of the notes (mostly all the registers). */ |
125 | ns.ns_first = kmem_alloc(sizeof *ns.ns_first, KM_SLEEP); |
126 | ns.ns_last = ns.ns_first; |
127 | ns.ns_count = 0; |
128 | ns.ns_offset = 0; |
129 | error = ELFNAMEEND(coredump_notes)(l, &ns); |
130 | ns.ns_last->nb_next = NULL; |
131 | if (error) |
132 | goto out; |
133 | notesize = ns.ns_count * sizeof nb->nb_data + ns.ns_offset; |
134 | |
135 | /* |
136 | * We have to make a total of 3 passes across the map: |
137 | * |
138 | * 1. Count the number of map entries (the number of |
139 | * PT_LOAD sections in the dump). |
140 | * |
141 | * 2. Write the P-section headers. |
142 | * |
143 | * 3. Write the P-sections. |
144 | */ |
145 | |
146 | /* Pass 1: count the entries. */ |
147 | npsections = uvm_coredump_count_segs(l->l_proc); |
148 | /* Allow for the PT_NOTE section. */ |
149 | npsections++; |
150 | |
151 | /* Build the main elf header */ |
152 | memset(&ehdr.e_ident[EI_PAD], 0, sizeof(ehdr.e_ident) - EI_PAD); |
153 | memcpy(ehdr.e_ident, ELFMAG, SELFMAG); |
154 | #if ELFSIZE == 32 |
155 | ehdr.e_ident[EI_CLASS] = ELFCLASS32; |
156 | #elif ELFSIZE == 64 |
157 | ehdr.e_ident[EI_CLASS] = ELFCLASS64; |
158 | #endif |
159 | ehdr.e_ident[EI_DATA] = ELFDEFNNAME(MACHDEP_ENDIANNESS); |
160 | ehdr.e_ident[EI_VERSION] = EV_CURRENT; |
161 | /* XXX Should be the OSABI/ABI version of the executable. */ |
162 | ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV; |
163 | ehdr.e_ident[EI_ABIVERSION] = 0; |
164 | |
165 | ehdr.e_type = ET_CORE; |
166 | /* XXX This should be the e_machine of the executable. */ |
167 | ehdr.e_machine = ELFDEFNNAME(MACHDEP_ID); |
168 | ehdr.e_version = EV_CURRENT; |
169 | ehdr.e_entry = 0; |
170 | ehdr.e_flags = 0; |
171 | ehdr.e_ehsize = sizeof(ehdr); |
172 | ehdr.e_phentsize = sizeof(Elf_Phdr); |
173 | if (npsections < PN_XNUM) { |
174 | ehdr.e_phnum = npsections; |
175 | ehdr.e_shentsize = 0; |
176 | ehdr.e_shnum = 0; |
177 | ehdr.e_shoff = 0; |
178 | ehdr.e_phoff = sizeof(ehdr); |
179 | } else { |
180 | ehdr.e_phnum = PN_XNUM; |
181 | ehdr.e_shentsize = sizeof(Elf_Shdr); |
182 | ehdr.e_shnum = 1; |
183 | ehdr.e_shoff = sizeof(ehdr); |
184 | ehdr.e_phoff = sizeof(ehdr) + sizeof(shdr); |
185 | } |
186 | ehdr.e_shstrndx = 0; |
187 | |
188 | #ifdef ELF_MD_COREDUMP_SETUP |
189 | ELF_MD_COREDUMP_SETUP(l, &ehdr); |
190 | #endif |
191 | |
192 | /* Write out the ELF header. */ |
193 | error = coredump_write(cookie, UIO_SYSSPACE, &ehdr, sizeof(ehdr)); |
194 | if (error) |
195 | goto out; |
196 | |
197 | /* Write out sections, if needed */ |
198 | if (npsections >= PN_XNUM) { |
199 | memset(&shdr, 0, sizeof(shdr)); |
200 | shdr.sh_type = SHT_NULL; |
201 | shdr.sh_info = npsections; |
202 | error = coredump_write(cookie, UIO_SYSSPACE, &shdr, |
203 | sizeof(shdr)); |
204 | if (error) |
205 | goto out; |
206 | } |
207 | |
208 | psectionssize = npsections * sizeof(*psections); |
209 | notestart = ehdr.e_phoff + psectionssize; |
210 | |
211 | psections = kmem_zalloc(psectionssize, KM_SLEEP); |
212 | |
213 | /* Pass 2: now find the P-section headers. */ |
214 | ws.secoff = notestart + notesize; |
215 | ws.psections = psections; |
216 | ws.npsections = npsections - 1; |
217 | ws.p = l->l_proc; |
218 | error = uvm_coredump_walkmap(l->l_proc, ELFNAMEEND(coredump_getseghdrs), |
219 | &ws); |
220 | if (error) |
221 | goto out; |
222 | if (ws.npsections != 0) { |
223 | /* A section went away */ |
224 | error = ENOMEM; |
225 | goto out; |
226 | } |
227 | |
228 | /* Add the PT_NOTE header after the P-section headers. */ |
229 | ws.psections->p_type = PT_NOTE; |
230 | ws.psections->p_offset = notestart; |
231 | ws.psections->p_vaddr = 0; |
232 | ws.psections->p_paddr = 0; |
233 | ws.psections->p_filesz = notesize; |
234 | ws.psections->p_memsz = 0; |
235 | ws.psections->p_flags = PF_R; |
236 | ws.psections->p_align = ELFROUNDSIZE; |
237 | |
238 | /* Write the P-section headers followed by the PT_NOTE header */ |
239 | error = coredump_write(cookie, UIO_SYSSPACE, psections, psectionssize); |
240 | if (error) |
241 | goto out; |
242 | |
243 | #ifdef DIAGNOSTIC |
244 | if (coredump_offset(cookie) != notestart) |
245 | panic("coredump: offset %lld != notestart %lld" , |
246 | (long long) coredump_offset(cookie), |
247 | (long long) notestart); |
248 | #endif |
249 | |
250 | /* Write out the notes. */ |
251 | for (nb = ns.ns_first; nb != NULL; nb = nb->nb_next) { |
252 | error = coredump_write(cookie, UIO_SYSSPACE, nb->nb_data, |
253 | nb->nb_next == NULL ? ns.ns_offset : sizeof nb->nb_data); |
254 | if (error) |
255 | goto out; |
256 | } |
257 | |
258 | /* Finally, write the sections themselves. */ |
259 | for (i = 0; i < npsections - 1; i++) { |
260 | if (psections[i].p_filesz == 0) |
261 | continue; |
262 | |
263 | #ifdef DIAGNOSTIC |
264 | if (coredump_offset(cookie) != psections[i].p_offset) |
265 | panic("coredump: offset %lld != p_offset[%d] %lld" , |
266 | (long long) coredump_offset(cookie), i, |
267 | (long long) psections[i].p_filesz); |
268 | #endif |
269 | |
270 | error = coredump_write(cookie, UIO_USERSPACE, |
271 | (void *)(vaddr_t)psections[i].p_vaddr, |
272 | psections[i].p_filesz); |
273 | if (error) |
274 | goto out; |
275 | } |
276 | |
277 | out: |
278 | if (psections) |
279 | kmem_free(psections, psectionssize); |
280 | while ((nb = ns.ns_first) != NULL) { |
281 | ns.ns_first = nb->nb_next; |
282 | kmem_free(nb, sizeof *nb); |
283 | } |
284 | return (error); |
285 | } |
286 | |
287 | static int |
288 | ELFNAMEEND(coredump_getseghdrs)(struct uvm_coredump_state *us) |
289 | { |
290 | struct writesegs_state *ws = us->cookie; |
291 | Elf_Phdr phdr; |
292 | vsize_t size, realsize; |
293 | vaddr_t end; |
294 | int error; |
295 | |
296 | /* Don't overrun if there are more sections */ |
297 | if (ws->npsections == 0) |
298 | return ENOMEM; |
299 | ws->npsections--; |
300 | |
301 | size = us->end - us->start; |
302 | realsize = us->realend - us->start; |
303 | end = us->realend; |
304 | |
305 | /* Don't bother writing out trailing zeros */ |
306 | while (realsize > 0) { |
307 | long buf[1024 / sizeof(long)]; |
308 | size_t slen = realsize > sizeof(buf) ? sizeof(buf) : realsize; |
309 | const long *ep; |
310 | int i; |
311 | |
312 | end -= slen; |
313 | if ((error = copyin_proc(ws->p, (void *)end, buf, slen)) != 0) |
314 | return error; |
315 | |
316 | ep = (const long *) &buf[slen / sizeof(buf[0])]; |
317 | for (i = 0, ep--; buf <= ep; ep--, i++) { |
318 | if (*ep) |
319 | break; |
320 | } |
321 | realsize -= i * sizeof(buf[0]); |
322 | if (i * sizeof(buf[0]) < slen) |
323 | break; |
324 | } |
325 | |
326 | phdr.p_type = PT_LOAD; |
327 | phdr.p_offset = ws->secoff; |
328 | phdr.p_vaddr = us->start; |
329 | phdr.p_paddr = 0; |
330 | phdr.p_filesz = realsize; |
331 | phdr.p_memsz = size; |
332 | phdr.p_flags = 0; |
333 | if (us->prot & VM_PROT_READ) |
334 | phdr.p_flags |= PF_R; |
335 | if (us->prot & VM_PROT_WRITE) |
336 | phdr.p_flags |= PF_W; |
337 | if (us->prot & VM_PROT_EXECUTE) |
338 | phdr.p_flags |= PF_X; |
339 | phdr.p_align = PAGE_SIZE; |
340 | |
341 | ws->secoff += phdr.p_filesz; |
342 | *ws->psections++ = phdr; |
343 | |
344 | return (0); |
345 | } |
346 | |
347 | static void |
348 | coredump_note_procinfo(struct lwp *l, struct note_state *ns) |
349 | { |
350 | struct proc *p; |
351 | struct netbsd_elfcore_procinfo cpi; |
352 | struct lwp *l0; |
353 | sigset_t ss1, ss2; |
354 | |
355 | p = l->l_proc; |
356 | |
357 | /* First, write an elfcore_procinfo. */ |
358 | cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION; |
359 | cpi.cpi_cpisize = sizeof(cpi); |
360 | cpi.cpi_signo = p->p_sigctx.ps_signo; |
361 | cpi.cpi_sigcode = p->p_sigctx.ps_code; |
362 | cpi.cpi_siglwp = p->p_sigctx.ps_lwp; |
363 | |
364 | /* |
365 | * XXX This should be per-LWP. |
366 | */ |
367 | ss1 = p->p_sigpend.sp_set; |
368 | sigemptyset(&ss2); |
369 | LIST_FOREACH(l0, &p->p_lwps, l_sibling) { |
370 | sigplusset(&l0->l_sigpend.sp_set, &ss1); |
371 | sigplusset(&l0->l_sigmask, &ss2); |
372 | } |
373 | memcpy(&cpi.cpi_sigpend, &ss1, sizeof(cpi.cpi_sigpend)); |
374 | memcpy(&cpi.cpi_sigmask, &ss2, sizeof(cpi.cpi_sigmask)); |
375 | memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore, |
376 | sizeof(cpi.cpi_sigignore)); |
377 | memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch, |
378 | sizeof(cpi.cpi_sigcatch)); |
379 | |
380 | cpi.cpi_pid = p->p_pid; |
381 | mutex_enter(proc_lock); |
382 | cpi.cpi_ppid = p->p_pptr->p_pid; |
383 | cpi.cpi_pgrp = p->p_pgid; |
384 | cpi.cpi_sid = p->p_session->s_sid; |
385 | mutex_exit(proc_lock); |
386 | |
387 | cpi.cpi_ruid = kauth_cred_getuid(l->l_cred); |
388 | cpi.cpi_euid = kauth_cred_geteuid(l->l_cred); |
389 | cpi.cpi_svuid = kauth_cred_getsvuid(l->l_cred); |
390 | |
391 | cpi.cpi_rgid = kauth_cred_getgid(l->l_cred); |
392 | cpi.cpi_egid = kauth_cred_getegid(l->l_cred); |
393 | cpi.cpi_svgid = kauth_cred_getsvgid(l->l_cred); |
394 | |
395 | cpi.cpi_nlwps = p->p_nlwps; |
396 | (void)strncpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name)); |
397 | cpi.cpi_name[sizeof(cpi.cpi_name) - 1] = '\0'; |
398 | |
399 | ELFNAMEEND(coredump_savenote)(ns, ELF_NOTE_NETBSD_CORE_PROCINFO, |
400 | ELF_NOTE_NETBSD_CORE_NAME, &cpi, sizeof(cpi)); |
401 | } |
402 | |
403 | static int |
404 | coredump_note_auxv(struct lwp *l, struct note_state *ns) |
405 | { |
406 | struct ps_strings pss; |
407 | int error; |
408 | struct proc *p = l->l_proc; |
409 | void *uauxv, *kauxv; |
410 | |
411 | if ((error = copyin_psstrings(p, &pss)) != 0) |
412 | return error; |
413 | |
414 | if (pss.ps_envstr == NULL) |
415 | return EIO; |
416 | |
417 | size_t ptrsz = PROC_PTRSZ(p); |
418 | uauxv = (void *)((char *)pss.ps_envstr + (pss.ps_nenvstr + 1) * ptrsz); |
419 | size_t len = p->p_execsw->es_arglen * ptrsz; |
420 | |
421 | kauxv = kmem_alloc(len, KM_SLEEP); |
422 | error = copyin_proc(p, uauxv, kauxv, len); |
423 | if (error == 0) { |
424 | ELFNAMEEND(coredump_savenote)(ns, ELF_NOTE_NETBSD_CORE_AUXV, |
425 | ELF_NOTE_NETBSD_CORE_NAME, kauxv, len); |
426 | } |
427 | |
428 | kmem_free(kauxv, len); |
429 | return error; |
430 | } |
431 | |
432 | static int |
433 | ELFNAMEEND(coredump_notes)(struct lwp *l, struct note_state *ns) |
434 | { |
435 | int error; |
436 | struct lwp *l0; |
437 | struct proc *p = l->l_proc; |
438 | |
439 | coredump_note_procinfo(l, ns); |
440 | error = coredump_note_auxv(l, ns); |
441 | if (error) |
442 | return error; |
443 | |
444 | /* XXX Add hook for machdep per-proc notes. */ |
445 | |
446 | /* |
447 | * Now write the register info for the thread that caused the |
448 | * coredump. |
449 | */ |
450 | error = ELFNAMEEND(coredump_note)(l, ns); |
451 | if (error) |
452 | return error; |
453 | |
454 | /* |
455 | * Now, for each LWP, write the register info and any other |
456 | * per-LWP notes. |
457 | * Lock in case this is a gcore requested dump. |
458 | */ |
459 | mutex_enter(p->p_lock); |
460 | LIST_FOREACH(l0, &p->p_lwps, l_sibling) { |
461 | if (l0 == l) /* we've taken care of this thread */ |
462 | continue; |
463 | error = ELFNAMEEND(coredump_note)(l0, ns); |
464 | if (error) |
465 | break; |
466 | } |
467 | mutex_exit(p->p_lock); |
468 | |
469 | return error; |
470 | } |
471 | |
472 | static int |
473 | ELFNAMEEND(coredump_note)(struct lwp *l, struct note_state *ns) |
474 | { |
475 | int error; |
476 | char name[64]; |
477 | elf_reg intreg; |
478 | #ifdef PT_GETFPREGS |
479 | elf_fpreg freg; |
480 | size_t freglen; |
481 | #endif |
482 | |
483 | snprintf(name, sizeof(name), "%s@%d" , |
484 | ELF_NOTE_NETBSD_CORE_NAME, l->l_lid); |
485 | |
486 | error = elf_process_read_regs(l, &intreg); |
487 | if (error) |
488 | return (error); |
489 | |
490 | ELFNAMEEND(coredump_savenote)(ns, PT_GETREGS, name, &intreg, |
491 | sizeof(intreg)); |
492 | |
493 | #ifdef PT_GETFPREGS |
494 | freglen = sizeof(freg); |
495 | error = elf_process_read_fpregs(l, &freg, &freglen); |
496 | if (error) |
497 | return (error); |
498 | |
499 | ELFNAMEEND(coredump_savenote)(ns, PT_GETFPREGS, name, &freg, freglen); |
500 | #endif |
501 | /* XXX Add hook for machdep per-LWP notes. */ |
502 | return (0); |
503 | } |
504 | |
505 | static void |
506 | save_note_bytes(struct note_state *ns, const void *data, size_t len) |
507 | { |
508 | struct note_buf *nb = ns->ns_last; |
509 | size_t copylen; |
510 | unsigned char *wp; |
511 | |
512 | /* |
513 | * Just copy the data into a buffer list. |
514 | * All but the last buffer is full. |
515 | */ |
516 | for (;;) { |
517 | copylen = min(len, sizeof nb->nb_data - ns->ns_offset); |
518 | wp = nb->nb_data + ns->ns_offset; |
519 | memcpy(wp, data, copylen); |
520 | if (copylen == len) |
521 | break; |
522 | nb->nb_next = kmem_alloc(sizeof *nb->nb_next, KM_SLEEP); |
523 | nb = nb->nb_next; |
524 | ns->ns_last = nb; |
525 | ns->ns_count++; |
526 | ns->ns_offset = 0; |
527 | len -= copylen; |
528 | data = (const unsigned char *)data + copylen; |
529 | } |
530 | |
531 | while (copylen & (ELFROUNDSIZE - 1)) |
532 | wp[copylen++] = 0; |
533 | |
534 | ns->ns_offset += copylen; |
535 | } |
536 | |
537 | void |
538 | ELFNAMEEND(coredump_savenote)(struct note_state *ns, unsigned int type, |
539 | const char *name, void *data, size_t data_len) |
540 | { |
541 | Elf_Nhdr nhdr; |
542 | |
543 | nhdr.n_namesz = strlen(name) + 1; |
544 | nhdr.n_descsz = data_len; |
545 | nhdr.n_type = type; |
546 | |
547 | save_note_bytes(ns, &nhdr, sizeof (nhdr)); |
548 | save_note_bytes(ns, name, nhdr.n_namesz); |
549 | save_note_bytes(ns, data, data_len); |
550 | } |
551 | |
552 | #else /* COREDUMP */ |
553 | |
554 | int |
555 | ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) |
556 | { |
557 | |
558 | return ENOSYS; |
559 | } |
560 | |
561 | #endif /* COREDUMP */ |
562 | |