Changeset View
Standalone View
sys/kern/imgact_elf.c
Show First 20 Lines • Show All 1,465 Lines • ▼ Show 20 Lines | |||||
extern int compress_user_cores; | extern int compress_user_cores; | ||||
extern int compress_user_cores_level; | extern int compress_user_cores_level; | ||||
static void cb_put_phdr(vm_map_entry_t, void *); | static void cb_put_phdr(vm_map_entry_t, void *); | ||||
static void cb_size_segment(vm_map_entry_t, void *); | static void cb_size_segment(vm_map_entry_t, void *); | ||||
static int core_write(struct coredump_params *, const void *, size_t, off_t, | static int core_write(struct coredump_params *, const void *, size_t, off_t, | ||||
enum uio_seg, size_t *); | enum uio_seg, size_t *); | ||||
static void each_dumpable_segment(struct thread *, segment_callback, void *); | static void each_dumpable_segment(struct thread *, segment_callback, void *, | ||||
int); | |||||
static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t, | static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t, | ||||
struct note_info_list *, size_t); | struct note_info_list *, size_t, int); | ||||
static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, | static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, | ||||
size_t *); | size_t *); | ||||
static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t); | static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t, int); | ||||
static void __elfN(putnote)(struct note_info *, struct sbuf *); | static void __elfN(putnote)(struct note_info *, struct sbuf *); | ||||
static size_t register_note(struct note_info_list *, int, outfunc_t, void *); | static size_t register_note(struct note_info_list *, int, outfunc_t, void *); | ||||
static int sbuf_drain_core_output(void *, const char *, int); | static int sbuf_drain_core_output(void *, const char *, int); | ||||
static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); | static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); | ||||
static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); | static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); | ||||
static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); | static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); | ||||
static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); | static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); | ||||
▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | sbuf_drain_core_output(void *arg, const char *data, int len) | ||||
p->offset += len; | p->offset += len; | ||||
return (len); | return (len); | ||||
} | } | ||||
int | int | ||||
__elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) | __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) | ||||
{ | { | ||||
struct ucred *cred = td->td_ucred; | struct ucred *cred = td->td_ucred; | ||||
int error = 0; | int compm, error = 0; | ||||
struct sseg_closure seginfo; | struct sseg_closure seginfo; | ||||
struct note_info_list notelst; | struct note_info_list notelst; | ||||
struct coredump_params params; | struct coredump_params params; | ||||
struct note_info *ninfo; | struct note_info *ninfo; | ||||
void *hdr, *tmpbuf; | void *hdr, *tmpbuf; | ||||
size_t hdrsize, notesz, coresize; | size_t hdrsize, notesz, coresize; | ||||
hdr = NULL; | hdr = NULL; | ||||
tmpbuf = NULL; | tmpbuf = NULL; | ||||
TAILQ_INIT(¬elst); | TAILQ_INIT(¬elst); | ||||
/* Size the program segments. */ | /* Size the program segments. */ | ||||
seginfo.count = 0; | seginfo.count = 0; | ||||
seginfo.size = 0; | seginfo.size = 0; | ||||
each_dumpable_segment(td, cb_size_segment, &seginfo); | each_dumpable_segment(td, cb_size_segment, &seginfo, flags); | ||||
/* | /* | ||||
* Collect info about the core file header area. | * Collect info about the core file header area. | ||||
*/ | */ | ||||
hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); | hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); | ||||
if (seginfo.count + 1 >= PN_XNUM) | if (seginfo.count + 1 >= PN_XNUM) | ||||
hdrsize += sizeof(Elf_Shdr); | hdrsize += sizeof(Elf_Shdr); | ||||
__elfN(prepare_notes)(td, ¬elst, ¬esz); | __elfN(prepare_notes)(td, ¬elst, ¬esz); | ||||
Show All 19 Lines | #ifdef RACCT | ||||
} | } | ||||
#endif | #endif | ||||
if (coresize >= limit) { | if (coresize >= limit) { | ||||
error = EFAULT; | error = EFAULT; | ||||
goto done; | goto done; | ||||
} | } | ||||
/* Create a compression stream if necessary. */ | /* Create a compression stream if necessary. */ | ||||
if (compress_user_cores != 0) { | compm = compress_user_cores; | ||||
if ((flags & (SVC_PT_COREDUMP | SVC_NOCOMPRESS)) == SVC_PT_COREDUMP && | |||||
compm == 0) | |||||
compm = COMPRESS_GZIP; | |||||
if (compm != 0) { | |||||
emaste: So if global `compress_user_cores` is not set `PT_COREDUMP` cores are also uncompressed? | |||||
Done Inline ActionsInitial intent was to make a flag to adjust global settings. But ok, I now made PC_COMPRESS completely independent from the configuration for automatic core dump. Next logical step would be to add an option to gcore to request compressed dump, but then it raises the question why this option is only implemented for -k. kib: Initial intent was to make a flag to adjust global settings. But ok, I now made PC_COMPRESS… | |||||
Done Inline ActionsIf it's completely independent, shouldn't the check be if (((flags & SVC_PT_COREDUMP) == 0 && compress_user_cores) || (flags & (SVC_PT_COREDUMP | SVC_NOCOMPRESS)) == SVC_PT_COREDUMP)? Also we may use either zlib or zstd for compression, and this is controlled by a different global setting. markj: If it's completely independent, shouldn't the check be `if (((flags & SVC_PT_COREDUMP) == 0 &&… | |||||
Done Inline ActionsI think it is good enough for now to not provide a selection of compression method through flags. kib: I think it is good enough for now to not provide a selection of compression method through… | |||||
Done Inline ActionsI'd be fine with simply not supporting compression at all through this interface, at least initially. In fact I believe the interface is somewhat broken: if SVC_NOCOMPRESS is not set, we will try to use the value of compress_user_cores as the compression algorithm, and if it is not configured compressor_init() will return an error. I think it was correct before my earlier suggestion, sorry. At the very least I think this knob, kern.compress_user_cores, should be referenced in the documentation. markj: I'd be fine with simply not supporting compression at all through this interface, at least… | |||||
params.comp = compressor_init(core_compressed_write, | params.comp = compressor_init(core_compressed_write, | ||||
compress_user_cores, CORE_BUF_SIZE, | compm, CORE_BUF_SIZE, | ||||
compress_user_cores_level, ¶ms); | compress_user_cores_level, ¶ms); | ||||
if (params.comp == NULL) { | if (params.comp == NULL) { | ||||
error = EFAULT; | error = EFAULT; | ||||
goto done; | goto done; | ||||
} | } | ||||
tmpbuf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); | tmpbuf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); | ||||
} | } | ||||
/* | /* | ||||
* Allocate memory for building the header, fill it up, | * Allocate memory for building the header, fill it up, | ||||
* and write it out following the notes. | * and write it out following the notes. | ||||
*/ | */ | ||||
hdr = malloc(hdrsize, M_TEMP, M_WAITOK); | hdr = malloc(hdrsize, M_TEMP, M_WAITOK); | ||||
error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, | error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, | ||||
notesz); | notesz, flags); | ||||
/* Write the contents of all of the writable segments. */ | /* Write the contents of all of the writable segments. */ | ||||
if (error == 0) { | if (error == 0) { | ||||
Elf_Phdr *php; | Elf_Phdr *php; | ||||
off_t offset; | off_t offset; | ||||
int i; | int i; | ||||
php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; | php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* For each writable segment in the process's memory map, call the given | * For each writable segment in the process's memory map, call the given | ||||
* function with a pointer to the map entry and some arbitrary | * function with a pointer to the map entry and some arbitrary | ||||
* caller-supplied data. | * caller-supplied data. | ||||
*/ | */ | ||||
static void | static void | ||||
each_dumpable_segment(struct thread *td, segment_callback func, void *closure) | each_dumpable_segment(struct thread *td, segment_callback func, void *closure, | ||||
int flags) | |||||
{ | { | ||||
struct proc *p = td->td_proc; | struct proc *p = td->td_proc; | ||||
vm_map_t map = &p->p_vmspace->vm_map; | vm_map_t map = &p->p_vmspace->vm_map; | ||||
vm_map_entry_t entry; | vm_map_entry_t entry; | ||||
vm_object_t backing_object, object; | vm_object_t backing_object, object; | ||||
bool ignore_entry; | bool ignore_entry; | ||||
vm_map_lock_read(map); | vm_map_lock_read(map); | ||||
VM_MAP_ENTRY_FOREACH(entry, map) { | VM_MAP_ENTRY_FOREACH(entry, map) { | ||||
/* | /* | ||||
* Don't dump inaccessible mappings, deal with legacy | * Don't dump inaccessible mappings, deal with legacy | ||||
* coredump mode. | * coredump mode. | ||||
* | * | ||||
* Note that read-only segments related to the elf binary | * Note that read-only segments related to the elf binary | ||||
* are marked MAP_ENTRY_NOCOREDUMP now so we no longer | * are marked MAP_ENTRY_NOCOREDUMP now so we no longer | ||||
* need to arbitrarily ignore such segments. | * need to arbitrarily ignore such segments. | ||||
*/ | */ | ||||
if ((flags & SVC_ALL) == 0) { | |||||
if (elf_legacy_coredump) { | if (elf_legacy_coredump) { | ||||
if ((entry->protection & VM_PROT_RW) != VM_PROT_RW) | if ((entry->protection & VM_PROT_RW) != | ||||
VM_PROT_RW) | |||||
continue; | continue; | ||||
} else { | } else { | ||||
if ((entry->protection & VM_PROT_ALL) == 0) | if ((entry->protection & VM_PROT_ALL) == 0) | ||||
continue; | continue; | ||||
} | } | ||||
} | |||||
/* | /* | ||||
* Dont include memory segment in the coredump if | * Dont include memory segment in the coredump if | ||||
* MAP_NOCORE is set in mmap(2) or MADV_NOCORE in | * MAP_NOCORE is set in mmap(2) or MADV_NOCORE in | ||||
* madvise(2). Do not dump submaps (i.e. parts of the | * madvise(2). Do not dump submaps (i.e. parts of the | ||||
* kernel map). | * kernel map). | ||||
*/ | */ | ||||
if (entry->eflags & (MAP_ENTRY_NOCOREDUMP|MAP_ENTRY_IS_SUB_MAP)) | if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0) | ||||
continue; | continue; | ||||
if ((entry->eflags & MAP_ENTRY_NOCOREDUMP) != 0 && | |||||
(flags & SVC_ALL) == 0) | |||||
continue; | |||||
if ((object = entry->object.vm_object) == NULL) | if ((object = entry->object.vm_object) == NULL) | ||||
continue; | continue; | ||||
/* Ignore memory-mapped devices and such things. */ | /* Ignore memory-mapped devices and such things. */ | ||||
VM_OBJECT_RLOCK(object); | VM_OBJECT_RLOCK(object); | ||||
while ((backing_object = object->backing_object) != NULL) { | while ((backing_object = object->backing_object) != NULL) { | ||||
VM_OBJECT_RLOCK(backing_object); | VM_OBJECT_RLOCK(backing_object); | ||||
VM_OBJECT_RUNLOCK(object); | VM_OBJECT_RUNLOCK(object); | ||||
Show All 10 Lines | |||||
} | } | ||||
/* | /* | ||||
* Write the core file header to the file, including padding up to | * Write the core file header to the file, including padding up to | ||||
* the page boundary. | * the page boundary. | ||||
*/ | */ | ||||
static int | static int | ||||
__elfN(corehdr)(struct coredump_params *p, int numsegs, void *hdr, | __elfN(corehdr)(struct coredump_params *p, int numsegs, void *hdr, | ||||
size_t hdrsize, struct note_info_list *notelst, size_t notesz) | size_t hdrsize, struct note_info_list *notelst, size_t notesz, | ||||
int flags) | |||||
{ | { | ||||
struct note_info *ninfo; | struct note_info *ninfo; | ||||
struct sbuf *sb; | struct sbuf *sb; | ||||
int error; | int error; | ||||
/* Fill in the header. */ | /* Fill in the header. */ | ||||
bzero(hdr, hdrsize); | bzero(hdr, hdrsize); | ||||
__elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz); | __elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz, flags); | ||||
sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); | sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); | ||||
sbuf_set_drain(sb, sbuf_drain_core_output, p); | sbuf_set_drain(sb, sbuf_drain_core_output, p); | ||||
sbuf_start_section(sb, NULL); | sbuf_start_section(sb, NULL); | ||||
sbuf_bcat(sb, hdr, hdrsize); | sbuf_bcat(sb, hdr, hdrsize); | ||||
TAILQ_FOREACH(ninfo, notelst, link) | TAILQ_FOREACH(ninfo, notelst, link) | ||||
__elfN(putnote)(ninfo, sb); | __elfN(putnote)(ninfo, sb); | ||||
/* Align up to a page boundary for the program segments. */ | /* Align up to a page boundary for the program segments. */ | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | __elfN(prepare_notes)(struct thread *td, struct note_info_list *list, | ||||
size += register_note(list, NT_PROCSTAT_AUXV, | size += register_note(list, NT_PROCSTAT_AUXV, | ||||
__elfN(note_procstat_auxv), p); | __elfN(note_procstat_auxv), p); | ||||
*sizep = size; | *sizep = size; | ||||
} | } | ||||
static void | static void | ||||
__elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, | __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, | ||||
size_t notesz) | size_t notesz, int flags) | ||||
{ | { | ||||
Elf_Ehdr *ehdr; | Elf_Ehdr *ehdr; | ||||
Elf_Phdr *phdr; | Elf_Phdr *phdr; | ||||
Elf_Shdr *shdr; | Elf_Shdr *shdr; | ||||
struct phdr_closure phc; | struct phdr_closure phc; | ||||
ehdr = (Elf_Ehdr *)hdr; | ehdr = (Elf_Ehdr *)hdr; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, | ||||
phdr->p_memsz = 0; | phdr->p_memsz = 0; | ||||
phdr->p_flags = PF_R; | phdr->p_flags = PF_R; | ||||
phdr->p_align = ELF_NOTE_ROUNDSIZE; | phdr->p_align = ELF_NOTE_ROUNDSIZE; | ||||
phdr++; | phdr++; | ||||
/* All the writable segments from the program. */ | /* All the writable segments from the program. */ | ||||
phc.phdr = phdr; | phc.phdr = phdr; | ||||
phc.offset = round_page(hdrsize + notesz); | phc.offset = round_page(hdrsize + notesz); | ||||
each_dumpable_segment(td, cb_put_phdr, &phc); | each_dumpable_segment(td, cb_put_phdr, &phc, flags); | ||||
} | } | ||||
static size_t | static size_t | ||||
register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) | register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) | ||||
{ | { | ||||
struct note_info *ninfo; | struct note_info *ninfo; | ||||
size_t size, notesize; | size_t size, notesize; | ||||
▲ Show 20 Lines • Show All 800 Lines • Show Last 20 Lines |
So if global compress_user_cores is not set PT_COREDUMP cores are also uncompressed?