Changeset View
Standalone View
sys/compat/linux/linux_elf.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-3-Clause | |||||
* | |||||
* Copyright (c) 2021 Edward Tomasz Napierala <trasz@FreeBSD.org> | |||||
* Copyright (c) 2018 Chuck Tuffli | |||||
* Copyright (c) 2017 Dell EMC | |||||
* Copyright (c) 2000 David O'Brien | |||||
* Copyright (c) 1995-1996 Søren Schmidt | |||||
* Copyright (c) 1996 Peter Wemm | |||||
* All rights reserved. | |||||
* | |||||
* This software was developed by the University of Cambridge Computer | |||||
* Laboratory as part of the CHERI for Hypervisors and Operating Systems | |||||
* (CHaOS) project, funded by EPSRC grant EP/V000292/1. | |||||
* | |||||
* 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 | |||||
* in this position and unchanged. | |||||
* 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. The name of the author may not be used to endorse or promote products | |||||
* derived from this software without specific prior written permission | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
emaste: Maybe we can include the hash / date of the version it's based on? | |||||
Done Inline ActionsI'm not really sure what's that version. But there's not much code duplication here anymore, so I'm not sure it even matters. trasz: I'm not really sure what's that version. But there's not much code duplication here anymore… | |||||
Not Done Inline Actions@emaste not understanding anything about FreeBSD or Linux core dumps, the prototype made a copy of imgact_elf.c as I didn't know which parts mattered. And this effort stalled because I knew copying was bad and the common code should be refactored. This whole-sale copy also accounts for the copyright license and holders list. After working a bit on core dumps, it was apparent the code to create Linux core dumps would be significantly different. So to @trasz 's point, there isn't and won't be much duplication. I would suggest removing this comment and potentially, converting this to the standard 2 clause license. chuck: @emaste not understanding anything about FreeBSD or Linux core dumps, the prototype made a copy… | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/exec.h> | |||||
#include <sys/imgact.h> | |||||
#include <sys/imgact_elf.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/procfs.h> | |||||
#include <sys/ptrace.h> | |||||
#include <sys/racct.h> | |||||
#include <sys/sbuf.h> | |||||
#include <sys/sysent.h> | |||||
#include <sys/syslog.h> | |||||
#include <sys/user.h> | |||||
#include <sys/vnode.h> | |||||
#include <machine/elf.h> | |||||
#if defined(COMPAT_LINUX32) && __ELF_WORD_SIZE == 32 | |||||
#define linux_pt_regset linux_pt_regset32 | |||||
#define bsd_to_linux_regset bsd_to_linux_regset32 | |||||
#include <machine/../linux32/linux.h> | |||||
#else | |||||
#include <machine/../linux/linux.h> | |||||
#endif | |||||
#include <compat/linux/linux_elf.h> | |||||
#include <compat/linux/linux_emul.h> | |||||
#include <compat/linux/linux_misc.h> | |||||
#define __linuxN(x) __CONCAT(__CONCAT(__CONCAT(linux,__ELF_WORD_SIZE),_),x) | |||||
#define ELF_NOTE_ROUNDSIZE 4 | |||||
#define CORE_BUF_SIZE (16 * 1024) | |||||
#define LINUX_NT_AUXV 6 | |||||
/* Linux core notes are labeled "CORE" */ | |||||
static const char LINUX_ABI_VENDOR[] = "CORE"; | |||||
/* | |||||
* Code for generating ELF core dumps. | |||||
*/ | |||||
typedef void (*outfunc_t)(void *, struct sbuf *, size_t *); | |||||
struct note_info { | |||||
int type; /* Note type. */ | |||||
outfunc_t outfunc; /* Output function. */ | |||||
void *outarg; /* Argument for the output function. */ | |||||
size_t outsize; /* Output size. */ | |||||
TAILQ_ENTRY(note_info) link; /* Link to the next note info. */ | |||||
}; | |||||
TAILQ_HEAD(note_info_list, note_info); | |||||
static int corehdr(struct coredump_params *, int, void *, size_t, | |||||
struct note_info_list *, size_t, int); | |||||
static void prepare_notes(struct thread *, struct note_info_list *, | |||||
size_t *); | |||||
static void putnote(struct note_info *, struct sbuf *); | |||||
static size_t register_note(struct note_info_list *, int, outfunc_t, void *); | |||||
static void note_fpregset(void *, struct sbuf *, size_t *); | |||||
static void note_prpsinfo(void *, struct sbuf *, size_t *); | |||||
Not Done Inline ActionsExtra () kib: Extra () | |||||
Not Done Inline ActionsYou might want to fix this in imgact_elf.c as a preliminary commit. kib: You might want to fix this in imgact_elf.c as a preliminary commit. | |||||
Done Inline ActionsSpun off as https://reviews.freebsd.org/D30841. trasz: Spun off as https://reviews.freebsd.org/D30841. | |||||
static void note_prstatus(void *, struct sbuf *, size_t *); | |||||
static void note_threadmd(void *, struct sbuf *, size_t *); | |||||
static void note_linux_nt_auxv(void *, struct sbuf *, size_t *); | |||||
int | |||||
__linuxN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) | |||||
{ | |||||
struct ucred *cred = td->td_ucred; | |||||
int error; | |||||
struct sseg_closure seginfo; | |||||
struct note_info_list notelst; | |||||
struct coredump_params params; | |||||
struct note_info *ninfo; | |||||
void *hdr; | |||||
Not Done Inline Actionsseems that error is initialized bellow - at corehdr call dchagin: seems that error is initialized bellow - at corehdr call | |||||
Done Inline ActionsTrue. This matches the similar code in sys/kern/imgact_elf.c, though, and I'd prefer to keep that code similar for now, and fix that in both places afterwards. Same applies to some of the other points too. trasz: True. This matches the similar code in sys/kern/imgact_elf.c, though, and I'd prefer to keep… | |||||
Not Done Inline ActionsNow that there is no common code changed in your patch, I looked at the linux/ diff. I would not call this code similar. This function is a literal copy of the FreeBSD native coredump, modulo the removal of compressed dump support. kib: Now that there is no common code changed in your patch, I looked at the linux/ diff.
I would… | |||||
size_t hdrsize, notesz, coresize; | |||||
hdr = NULL; | |||||
Not Done Inline ActionsWhy did you left the generic name for this (and one below) types? These should be e.g. linux_prpsinfo_t. Do not use elf_ prefix in linux code which you refuse to de-dup. kib: Why did you left the generic name for this (and one below) types?
These should be e.g. | |||||
TAILQ_INIT(¬elst); | |||||
/* Size the program segments. */ | |||||
Not Done Inline ActionsWhat are the differences between this code and FreeBSD native prpsinfo? kib: What are the differences between this code and FreeBSD native prpsinfo? | |||||
Done Inline ActionsI think none at the moment, but there's a large chance they will diverge. This code is in a "good enough" state, but it'll require spending some more time comparing its output with what Linux kernel generates. Thus, I'd rather not deduplicate them just yet. trasz: I think none at the moment, but there's a large chance they will diverge. This code is in a… | |||||
Not Done Inline ActionsI suspect that prpsinfo goes back to SysV, so the format of the note is engraved in stone. kib: I suspect that prpsinfo goes back to SysV, so the format of the note is engraved in stone. | |||||
Done Inline ActionsI'm not sure anything can be engraved in stone when we're talking about Linux :-) In any case: if it turns out these are same between FreeBSD and Linux, I'll deduplicate them later on. trasz: I'm not sure anything can be engraved in stone when we're talking about Linux :-) In any case… | |||||
Not Done Inline ActionsIn fact, I would be more happy about leaving this functions redefined if you used the names like l_prstatus_t instead of using exact same name as the FreeBSD native code. kib: In fact, I would be more happy about leaving this functions redefined if you used the names… | |||||
__elfN(size_segments)(td, &seginfo, flags); | |||||
/* | |||||
* Collect info about the core file header area. | |||||
*/ | |||||
hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); | |||||
if (seginfo.count + 1 >= PN_XNUM) | |||||
hdrsize += sizeof(Elf_Shdr); | |||||
prepare_notes(td, ¬elst, ¬esz); | |||||
Done Inline ActionsThe cast is not needed. kib: The cast is not needed. | |||||
coresize = round_page(hdrsize + notesz) + seginfo.size; | |||||
/* Set up core dump parameters. */ | |||||
Not Done Inline ActionsIf it is still elf_prpsinfo_t, you do not need a copy of the function. Both prpsinfo and prfpregset should be linux-named IMO. kib: If it is still elf_prpsinfo_t, you do not need a copy of the function. Both prpsinfo and… | |||||
Done Inline ActionsIt is elf_prpsinfo_t _for now_; it will likely change later. I'd prefer to avoid a scenario where we add complexity, exporting the native function and using it here, only to find that we actually do need to do something differently here. Deduplicating stuff afterwards is easy. trasz: It is elf_prpsinfo_t _for now_; it will likely change later. I'd prefer to avoid a scenario… | |||||
params.offset = 0; | |||||
params.active_cred = cred; | |||||
params.file_cred = NOCRED; | |||||
params.td = td; | |||||
params.vp = vp; | |||||
params.comp = NULL; | |||||
Not Done Inline Actionsparams can be initialized before use, ie before hdr malloc? dchagin: params can be initialized before use, ie before hdr malloc? | |||||
#ifdef RACCT | |||||
if (racct_enable) { | |||||
PROC_LOCK(td->td_proc); | |||||
error = racct_add(td->td_proc, RACCT_CORE, coresize); | |||||
PROC_UNLOCK(td->td_proc); | |||||
if (error != 0) { | |||||
error = EFAULT; | |||||
goto done; | |||||
} | |||||
} | |||||
#endif | |||||
if (coresize >= limit) { | |||||
error = EFAULT; | |||||
goto done; | |||||
} | |||||
/* | |||||
* Allocate memory for building the header, fill it up, | |||||
Not Done Inline Actionshm, may be this check should be before racct_add()? dchagin: hm, may be this check should be before racct_add()? | |||||
* and write it out following the notes. | |||||
*/ | |||||
hdr = malloc(hdrsize, M_TEMP, M_WAITOK); | |||||
error = corehdr(¶ms, seginfo.count, hdr, hdrsize, ¬elst, | |||||
notesz, flags); | |||||
/* Write the contents of all of the writable segments. */ | |||||
if (error == 0) { | |||||
Elf_Phdr *php; | |||||
off_t offset; | |||||
int i; | |||||
php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; | |||||
offset = round_page(hdrsize + notesz); | |||||
for (i = 0; i < seginfo.count; i++) { | |||||
error = core_output((caddr_t)(uintptr_t)php->p_vaddr, | |||||
php->p_filesz, offset, ¶ms, NULL); | |||||
if (error != 0) | |||||
break; | |||||
offset += php->p_filesz; | |||||
php++; | |||||
} | |||||
} | |||||
Not Done Inline Actionsmay be instead of tmpbuf pass NULL? dchagin: may be instead of tmpbuf pass NULL? | |||||
if (error) { | |||||
log(LOG_WARNING, | |||||
"Failed to write core file for process %s (error %d)\n", | |||||
curproc->p_comm, error); | |||||
} | |||||
done: | |||||
while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { | |||||
TAILQ_REMOVE(¬elst, ninfo, link); | |||||
free(ninfo, M_TEMP); | |||||
} | |||||
Not Done Inline ActionsCast is not needed. kib: Cast is not needed.
Perhaps you can fix these casts in FreeBSD code as the preliminary commit. | |||||
if (hdr != NULL) | |||||
free(hdr, M_TEMP); | |||||
return (error); | |||||
} | |||||
Not Done Inline ActionsThis cast is not needed. kib: This cast is not needed. | |||||
/* | |||||
* Write the core file header to the file, including padding up to | |||||
Not Done Inline Actionsthis can be dobe before done: without NULL check and without initialization to NULL dchagin: this can be dobe before done: without NULL check and without initialization to NULL | |||||
* the page boundary. | |||||
*/ | |||||
static int | |||||
corehdr(struct coredump_params *p, int numsegs, void *hdr, | |||||
size_t hdrsize, struct note_info_list *notelst, size_t notesz, int flags) | |||||
{ | |||||
struct note_info *ninfo; | |||||
struct sbuf *sb; | |||||
int error; | |||||
/* Fill in the header. */ | |||||
bzero(hdr, hdrsize); | |||||
__elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz, flags | SVC_LINUX); | |||||
sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); | |||||
sbuf_set_drain(sb, sbuf_drain_core_output, p); | |||||
Done Inline ActionsAnd this function is again the exact copy of the FreeBSD' corehdr except the added assignment to e_ident. I do not think this is a good approach. Returning to the previous suggestion of adding some sv_ methods, I think this is the way to go instead of copying 90% of coredumping code and making small modifications here and there. If you do not like adding sv_ methods for ELF coredump, consider creating a table of virtual methods just for ELF coredump and passing it as an arg to coredump(). But this copying of ~300 LoC should be fixed IMO. kib: And this function is again the exact copy of the FreeBSD' corehdr except the added assignment… | |||||
Done Inline ActionsI thought there would be too many differences to do this properly, but turns out you were right - it's just two additional sv_ variables and one method, with pretty much no duplication left. trasz: I thought there would be too many differences to do this properly, but turns out you were right… | |||||
Not Done Inline ActionsBut this function is still the exact copy of the FreeBSD function? kib: But this function is still the exact copy of the FreeBSD function? | |||||
sbuf_start_section(sb, NULL); | |||||
sbuf_bcat(sb, hdr, hdrsize); | |||||
TAILQ_FOREACH(ninfo, notelst, link) | |||||
putnote(ninfo, sb); | |||||
/* Align up to a page boundary for the program segments. */ | |||||
sbuf_end_section(sb, -1, PAGE_SIZE, 0); | |||||
error = sbuf_finish(sb); | |||||
sbuf_delete(sb); | |||||
return (error); | |||||
} | |||||
static void | |||||
prepare_notes(struct thread *td, struct note_info_list *list, | |||||
size_t *sizep) | |||||
{ | |||||
struct proc *p; | |||||
struct thread *thr; | |||||
size_t size; | |||||
p = td->td_proc; | |||||
size = 0; | |||||
/* | |||||
* To have the debugger select the right thread (LWP) as the initial | |||||
Not Done Inline ActionsSame code, again? kib: Same code, again? | |||||
* thread, we dump the state of the thread passed to us in td first. | |||||
* This is the thread that causes the core dump and thus likely to | |||||
* be the right thread one wants to have selected in the debugger. | |||||
*/ | |||||
thr = td; | |||||
while (thr != NULL) { | |||||
size += register_note(list, NT_PRSTATUS, note_prstatus, thr); | |||||
size += register_note(list, NT_PRPSINFO, note_prpsinfo, p); | |||||
size += register_note(list, LINUX_NT_AUXV, note_linux_nt_auxv, p); | |||||
size += register_note(list, NT_FPREGSET, note_fpregset, thr); | |||||
size += register_note(list, -1, | |||||
note_threadmd, thr); | |||||
thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : | |||||
TAILQ_NEXT(thr, td_plist); | |||||
if (thr == td) | |||||
thr = TAILQ_NEXT(thr, td_plist); | |||||
} | |||||
*sizep = size; | |||||
} | |||||
static size_t | |||||
register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) | |||||
{ | |||||
struct note_info *ninfo; | |||||
size_t size, notesize; | |||||
size = 0; | |||||
out(arg, NULL, &size); | |||||
Not Done Inline ActionsWhy 320? And similar question for the native note_procstat_auxv, where it might become too short quite fast (128). I mean, it should be based on AT_COUNT and equivalent Linux constant. kib: Why 320?
And similar question for the native note_procstat_auxv, where it might become too… | |||||
ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK); | |||||
ninfo->type = type; | |||||
ninfo->outfunc = out; | |||||
ninfo->outarg = arg; | |||||
ninfo->outsize = size; | |||||
TAILQ_INSERT_TAIL(list, ninfo, link); | |||||
if (type == -1) | |||||
return (size); | |||||
notesize = sizeof(Elf_Note) + /* note header */ | |||||
roundup2(sizeof(LINUX_ABI_VENDOR), ELF_NOTE_ROUNDSIZE) + | |||||
/* note name */ | |||||
roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ | |||||
return (notesize); | |||||
} | |||||
static void | |||||
putnote(struct note_info *ninfo, struct sbuf *sb) | |||||
{ | |||||
Elf_Note note; | |||||
ssize_t old_len, sect_len; | |||||
size_t new_len, descsz, i; | |||||
if (ninfo->type == -1) { | |||||
ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); | |||||
return; | |||||
} | |||||
note.n_namesz = sizeof(LINUX_ABI_VENDOR); | |||||
note.n_descsz = ninfo->outsize; | |||||
note.n_type = ninfo->type; | |||||
sbuf_bcat(sb, ¬e, sizeof(note)); | |||||
sbuf_start_section(sb, &old_len); | |||||
sbuf_bcat(sb, LINUX_ABI_VENDOR, sizeof(LINUX_ABI_VENDOR)); | |||||
sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); | |||||
if (note.n_descsz == 0) | |||||
return; | |||||
sbuf_start_section(sb, &old_len); | |||||
ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); | |||||
sect_len = sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); | |||||
if (sect_len < 0) | |||||
return; | |||||
new_len = (size_t)sect_len; | |||||
descsz = roundup(note.n_descsz, ELF_NOTE_ROUNDSIZE); | |||||
if (new_len < descsz) { | |||||
/* | |||||
* It is expected that individual note emitters will correctly | |||||
* predict their expected output size and fill up to that size | |||||
* themselves, padding in a format-specific way if needed. | |||||
* However, in case they don't, just do it here with zeros. | |||||
*/ | |||||
for (i = 0; i < descsz - new_len; i++) | |||||
sbuf_putc(sb, 0); | |||||
} else if (new_len > descsz) { | |||||
/* | |||||
* We can't always truncate sb -- we may have drained some | |||||
* of it already. | |||||
*/ | |||||
KASSERT(new_len == descsz, ("%s: Note type %u changed as we " | |||||
"read it (%zu > %zu). Since it is longer than " | |||||
"expected, this coredump's notes are corrupt. THIS " | |||||
"IS A BUG in the note_procstat routine for type %u.\n", | |||||
__func__, (unsigned)note.n_type, new_len, descsz, | |||||
(unsigned)note.n_type)); | |||||
} | |||||
} | |||||
/* | |||||
* Miscellaneous note out functions. | |||||
*/ | |||||
#if defined(COMPAT_LINUX32) && __ELF_WORD_SIZE == 32 | |||||
typedef struct linux_elf_prstatus elf_prstatus_t; | |||||
Not Done Inline Actionsis __ELF_WORD_SIZE == 32 really should be here? dchagin: is __ELF_WORD_SIZE == 32 really should be here? | |||||
Done Inline ActionsI think so. Thing is, this code is built twice, but indirectly; see linux_elf64.c and linux_elf32.c. trasz: I think so. Thing is, this code is built twice, but indirectly; see linux_elf64.c and… | |||||
Not Done Inline Actionsok, I’m confused a bit) dchagin: ok, I’m confused a bit)
For i386 we does not define compat_linux32 | |||||
typedef struct prpsinfo32 elf_prpsinfo_t; | |||||
typedef struct fpreg32 elf_prfpregset_t; | |||||
#else | |||||
typedef struct linux_elf_prstatus elf_prstatus_t; | |||||
typedef prpsinfo_t elf_prpsinfo_t; | |||||
typedef prfpregset_t elf_prfpregset_t; | |||||
#endif | |||||
static void | |||||
note_prpsinfo(void *arg, struct sbuf *sb, size_t *sizep) | |||||
{ | |||||
struct sbuf sbarg; | |||||
size_t len; | |||||
char *cp, *end; | |||||
struct proc *p; | |||||
elf_prpsinfo_t *psinfo; | |||||
int error; | |||||
p = (struct proc *)arg; | |||||
if (sb != NULL) { | |||||
KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); | |||||
psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); | |||||
psinfo->pr_version = PRPSINFO_VERSION; | |||||
psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t); | |||||
strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); | |||||
PROC_LOCK(p); | |||||
if (p->p_args != NULL) { | |||||
len = sizeof(psinfo->pr_psargs) - 1; | |||||
if (len > p->p_args->ar_length) | |||||
len = p->p_args->ar_length; | |||||
memcpy(psinfo->pr_psargs, p->p_args->ar_args, len); | |||||
PROC_UNLOCK(p); | |||||
error = 0; | |||||
} else { | |||||
_PHOLD(p); | |||||
PROC_UNLOCK(p); | |||||
sbuf_new(&sbarg, psinfo->pr_psargs, | |||||
sizeof(psinfo->pr_psargs), SBUF_FIXEDLEN); | |||||
error = proc_getargv(curthread, p, &sbarg); | |||||
PRELE(p); | |||||
if (sbuf_finish(&sbarg) == 0) | |||||
len = sbuf_len(&sbarg) - 1; | |||||
else | |||||
len = sizeof(psinfo->pr_psargs) - 1; | |||||
sbuf_delete(&sbarg); | |||||
} | |||||
if (error || len == 0) | |||||
strlcpy(psinfo->pr_psargs, p->p_comm, | |||||
sizeof(psinfo->pr_psargs)); | |||||
else { | |||||
KASSERT(len < sizeof(psinfo->pr_psargs), | |||||
("len is too long: %zu vs %zu", len, | |||||
sizeof(psinfo->pr_psargs))); | |||||
cp = psinfo->pr_psargs; | |||||
end = cp + len - 1; | |||||
for (;;) { | |||||
cp = memchr(cp, '\0', end - cp); | |||||
if (cp == NULL) | |||||
break; | |||||
*cp = ' '; | |||||
} | |||||
} | |||||
psinfo->pr_pid = p->p_pid; | |||||
sbuf_bcat(sb, psinfo, sizeof(*psinfo)); | |||||
free(psinfo, M_TEMP); | |||||
} | |||||
*sizep = sizeof(*psinfo); | |||||
} | |||||
static void | |||||
note_prstatus(void *arg, struct sbuf *sb, size_t *sizep) | |||||
{ | |||||
struct thread *td; | |||||
elf_prstatus_t *status; | |||||
#if defined(COMPAT_LINUX32) && __ELF_WORD_SIZE == 32 | |||||
struct reg32 pr_reg; | |||||
#else | |||||
struct reg pr_reg; | |||||
#endif | |||||
td = (struct thread *)arg; | |||||
if (sb != NULL) { | |||||
KASSERT(*sizep == sizeof(*status), ("invalid size")); | |||||
status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); | |||||
/* | |||||
* XXX: Some fields missing. | |||||
*/ | |||||
status->pr_cursig = td->td_proc->p_sig; | |||||
status->pr_pid = td->td_tid; | |||||
#if defined(COMPAT_LINUX32) && __ELF_WORD_SIZE == 32 | |||||
fill_regs32(td, &pr_reg); | |||||
#else | |||||
fill_regs(td, &pr_reg); | |||||
#endif | |||||
bsd_to_linux_regset(&pr_reg, &status->pr_reg); | |||||
sbuf_bcat(sb, status, sizeof(*status)); | |||||
free(status, M_TEMP); | |||||
} | |||||
*sizep = sizeof(*status); | |||||
} | |||||
static void | |||||
note_fpregset(void *arg, struct sbuf *sb, size_t *sizep) | |||||
{ | |||||
struct thread *td; | |||||
elf_prfpregset_t *fpregset; | |||||
td = (struct thread *)arg; | |||||
if (sb != NULL) { | |||||
KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); | |||||
fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); | |||||
#if defined(COMPAT_LINUX32) && __ELF_WORD_SIZE == 32 | |||||
fill_fpregs32(td, fpregset); | |||||
#else | |||||
fill_fpregs(td, fpregset); | |||||
#endif | |||||
sbuf_bcat(sb, fpregset, sizeof(*fpregset)); | |||||
free(fpregset, M_TEMP); | |||||
} | |||||
*sizep = sizeof(*fpregset); | |||||
} | |||||
/* | |||||
* Allow for MD specific notes, as well as any MD | |||||
* specific preparations for writing MI notes. | |||||
*/ | |||||
static void | |||||
note_threadmd(void *arg, struct sbuf *sb, size_t *sizep) | |||||
{ | |||||
struct thread *td; | |||||
void *buf; | |||||
size_t size; | |||||
td = (struct thread *)arg; | |||||
size = *sizep; | |||||
if (size != 0 && sb != NULL) | |||||
buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); | |||||
else | |||||
buf = NULL; | |||||
size = 0; | |||||
__elfN(dump_thread)(td, buf, &size); | |||||
KASSERT(sb == NULL || *sizep == size, ("invalid size")); | |||||
if (size != 0 && sb != NULL) | |||||
sbuf_bcat(sb, buf, size); | |||||
free(buf, M_TEMP); | |||||
*sizep = size; | |||||
} | |||||
static void | |||||
note_linux_nt_auxv(void *arg, struct sbuf *sb, size_t *sizep) | |||||
{ | |||||
struct proc *p; | |||||
size_t size; | |||||
p = (struct proc *)arg; | |||||
if (sb == NULL) { | |||||
size = 0; | |||||
sb = sbuf_new(NULL, NULL, 320, SBUF_FIXEDLEN); | |||||
sbuf_set_drain(sb, sbuf_count_drain, &size); | |||||
PHOLD(p); | |||||
proc_getauxv(curthread, p, sb); | |||||
PRELE(p); | |||||
sbuf_finish(sb); | |||||
sbuf_delete(sb); | |||||
*sizep = size; | |||||
} else { | |||||
PHOLD(p); | |||||
proc_getauxv(curthread, p, sb); | |||||
PRELE(p); | |||||
} | |||||
} |
Maybe we can include the hash / date of the version it's based on?