summaryrefslogtreecommitdiffstats
path: root/sys/kern/imgact_elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/imgact_elf.c')
-rw-r--r--sys/kern/imgact_elf.c267
1 files changed, 199 insertions, 68 deletions
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index e212932..2691c12 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -26,7 +26,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: imgact_elf.c,v 1.30 1998/09/14 05:36:49 jdp Exp $
+ * $Id: imgact_elf.c,v 1.31 1998/09/14 22:46:04 jdp Exp $
*/
#include "opt_rlimit.h"
@@ -59,6 +59,7 @@
#include <vm/pmap.h>
#include <sys/lock.h>
#include <vm/vm_map.h>
+#include <vm/vm_object.h>
#include <vm/vm_prot.h>
#include <vm/vm_extern.h>
@@ -684,13 +685,40 @@ elf_freebsd_fixup(long **stack_base, struct image_params *imgp)
* Code for generating ELF core dumps.
*/
-static int elf_corehdr __P((struct proc *, struct vnode *, struct ucred *));
-static size_t elf_hdrsize(void);
-static void elf_puthdr(void *, size_t *, const prstatus_t *,
- const prfpregset_t *, const prpsinfo_t *, const void *, size_t,
- const void *, size_t);
-static void elf_putnote(void *, size_t *, const char *, int, const void *,
- size_t);
+typedef void (*segment_callback) __P((vm_map_entry_t, void *));
+
+/* Closure for cb_put_phdr(). */
+struct phdr_closure {
+ Elf_Phdr *phdr; /* Program header to fill in */
+ Elf_Off offset; /* Offset of segment in core file */
+};
+
+/* Closure for cb_size_segment(). */
+struct sseg_closure {
+ int count; /* Count of writable segments. */
+ size_t size; /* Total size of all writable segments. */
+};
+
+/* Closure for cb_write_segment(). */
+struct wseg_closure {
+ struct proc *p;
+ struct vnode *vp;
+ struct ucred *cred;
+ off_t offset; /* Position in file at which to write. */
+ int error;
+};
+
+static void cb_put_phdr __P((vm_map_entry_t, void *));
+static void cb_size_segment __P((vm_map_entry_t, void *));
+static void cb_write_segment __P((vm_map_entry_t, void *));
+static void each_writable_segment __P((struct proc *, segment_callback,
+ void *));
+static int elf_corehdr __P((struct proc *, struct vnode *, struct ucred *,
+ int, size_t));
+static void elf_puthdr __P((struct proc *, void *, size_t *,
+ const prstatus_t *, const prfpregset_t *, const prpsinfo_t *, int));
+static void elf_putnote __P((void *, size_t *, const char *, int,
+ const void *, size_t));
extern int osreldate;
@@ -705,15 +733,30 @@ elf_coredump(p)
struct vattr vattr;
int error, error1;
char *name; /* name of corefile */
+ struct sseg_closure seginfo;
size_t hdrsize;
STOPEVENT(p, S_CORE, 0);
if (sugid_coredump == 0 && p->p_flag & P_SUGID)
return (EFAULT);
- hdrsize = elf_hdrsize();
- if (hdrsize + ctob(vm->vm_dsize + vm->vm_ssize) >=
- p->p_rlimit[RLIMIT_CORE].rlim_cur)
+
+ /* Size the program segments. */
+ seginfo.count = 0;
+ seginfo.size = 0;
+ each_writable_segment(p, cb_size_segment, &seginfo);
+
+ /*
+ * Calculate the size of the core file header area by making
+ * a dry run of generating it. Nothing is written, but the
+ * size is calculated.
+ */
+ hdrsize = 0;
+ elf_puthdr((struct proc *)NULL, (void *)NULL, &hdrsize,
+ (const prstatus_t *)NULL, (const prfpregset_t *)NULL,
+ (const prpsinfo_t *)NULL, seginfo.count);
+
+ if (hdrsize + seginfo.size >= p->p_rlimit[RLIMIT_CORE].rlim_cur)
return (EFAULT);
name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
if (name == NULL)
@@ -737,17 +780,20 @@ elf_coredump(p)
VOP_LEASE(vp, p, cred, LEASE_WRITE);
VOP_SETATTR(vp, &vattr, cred, p);
p->p_acflag |= ACORE;
- error = elf_corehdr(p, vp, cred);
- if (error == 0)
- error = vn_rdwr(UIO_WRITE, vp, vm->vm_daddr,
- (int)ctob(vm->vm_dsize), (off_t)hdrsize, UIO_USERSPACE,
- IO_NODELOCKED|IO_UNIT, cred, (int *) NULL, p);
- if (error == 0)
- error = vn_rdwr(UIO_WRITE, vp,
- (caddr_t) trunc_page(USRSTACK - ctob(vm->vm_ssize)),
- round_page(ctob(vm->vm_ssize)),
- (off_t)hdrsize + ctob(vm->vm_dsize), UIO_USERSPACE,
- IO_NODELOCKED|IO_UNIT, cred, (int *) NULL, p);
+ error = elf_corehdr(p, vp, cred, seginfo.count, hdrsize);
+ if (error == 0) {
+ struct wseg_closure wsc;
+
+ wsc.p = p;
+ wsc.vp = vp;
+ wsc.cred = cred;
+ wsc.offset = hdrsize;
+ wsc.error = 0;
+
+ each_writable_segment(p, cb_write_segment, &wsc);
+ error = wsc.error;
+ }
+
out:
VOP_UNLOCK(vp, 0, p);
error1 = vn_close(vp, FWRITE, cred, p);
@@ -756,15 +802,131 @@ out:
return (error);
}
+/*
+ * A callback for each_writable_segment() to write out the segment's
+ * program header entry.
+ */
+static void
+cb_put_phdr(entry, closure)
+ vm_map_entry_t entry;
+ void *closure;
+{
+ struct phdr_closure *phc = (struct phdr_closure *)closure;
+ Elf_Phdr *phdr = phc->phdr;
+
+ phc->offset = round_page(phc->offset);
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = phc->offset;
+ phdr->p_vaddr = entry->start;
+ phdr->p_paddr = 0;
+ phdr->p_filesz = phdr->p_memsz = entry->end - entry->start;
+ phdr->p_align = PAGE_SIZE;
+ phdr->p_flags = 0;
+ if (entry->protection & VM_PROT_READ)
+ phdr->p_flags |= PF_R;
+ if (entry->protection & VM_PROT_WRITE)
+ phdr->p_flags |= PF_W;
+ if (entry->protection & VM_PROT_EXECUTE)
+ phdr->p_flags |= PF_X;
+
+ phc->offset += phdr->p_filesz;
+ phc->phdr++;
+}
+
+/*
+ * A callback for each_writable_segment() to gather information about
+ * the number of segments and their total size.
+ */
+static void
+cb_size_segment(entry, closure)
+ vm_map_entry_t entry;
+ void *closure;
+{
+ struct sseg_closure *ssc = (struct sseg_closure *)closure;
+
+ ssc->count++;
+ ssc->size += entry->end - entry->start;
+}
+
+/*
+ * A callback for each_writable_segment() to write out the segment contents.
+ */
+static void
+cb_write_segment(entry, closure)
+ vm_map_entry_t entry;
+ void *closure;
+{
+ struct wseg_closure *wsc = (struct wseg_closure *)closure;
+
+ if (wsc->error == 0) {
+ wsc->error = vn_rdwr(UIO_WRITE, wsc->vp, (caddr_t)entry->start,
+ entry->end - entry->start, wsc->offset, UIO_USERSPACE,
+ IO_NODELOCKED|IO_UNIT, wsc->cred, (int *)NULL, wsc->p);
+ if (wsc->error == 0)
+ wsc->offset += entry->end - entry->start;
+ }
+}
+
+/*
+ * For each writable segment in the process's memory map, call the given
+ * function with a pointer to the map entry and some arbitrary
+ * caller-supplied data.
+ */
+static void
+each_writable_segment(p, func, closure)
+ struct proc *p;
+ segment_callback func;
+ void *closure;
+{
+ vm_map_t map = &p->p_vmspace->vm_map;
+ vm_map_entry_t entry;
+
+ if (map != &curproc->p_vmspace->vm_map)
+ vm_map_lock_read(map);
+
+ for (entry = map->header.next; entry != &map->header;
+ entry = entry->next) {
+ vm_object_t obj;
+ vm_object_t backobj;
+
+ if (entry->eflags & (MAP_ENTRY_IS_A_MAP|MAP_ENTRY_IS_SUB_MAP) ||
+ !(entry->protection & VM_PROT_WRITE))
+ continue;
+
+ /* Find the deepest backing object. */
+ backobj = obj = entry->object.vm_object;
+ if (backobj != NULL)
+ while (backobj->backing_object != NULL)
+ backobj = backobj->backing_object;
+
+ /* Ignore memory-mapped devices and such things. */
+ if (backobj->type != OBJT_DEFAULT &&
+ backobj->type != OBJT_SWAP &&
+ backobj->type != OBJT_VNODE)
+ continue;
+
+ (*func)(entry, closure);
+ }
+
+ if (map != &curproc->p_vmspace->vm_map)
+ vm_map_unlock_read(map);
+}
+
+/*
+ * Write the core file header to the file, including padding up to
+ * the page boundary.
+ */
static int
-elf_corehdr(p, vp, cred)
+elf_corehdr(p, vp, cred, numsegs, hdrsize)
struct proc *p;
struct vnode *vp;
struct ucred *cred;
+ int numsegs;
+ size_t hdrsize;
{
struct vmspace *vm = p->p_vmspace;
size_t off;
- size_t hdrsize;
prstatus_t status;
prfpregset_t fpregset;
prpsinfo_t psinfo;
@@ -788,10 +950,10 @@ elf_corehdr(p, vp, cred)
psinfo.pr_version = PRPSINFO_VERSION;
psinfo.pr_psinfosz = sizeof(prpsinfo_t);
strncpy(psinfo.pr_fname, p->p_comm, MAXCOMLEN);
- psinfo.pr_psargs[0] = '\0'; /* XXX - args not implemented yet */
+ /* XXX - We don't fill in the command line arguments properly yet. */
+ strncpy(psinfo.pr_psargs, p->p_comm, PRARGSZ);
/* Allocate memory for building the header. */
- hdrsize = elf_hdrsize();
hdr = malloc(hdrsize, M_TEMP, M_WAITOK);
if (hdr == NULL)
return EINVAL;
@@ -799,10 +961,7 @@ elf_corehdr(p, vp, cred)
/* Fill in the header. */
off = 0;
- elf_puthdr(hdr, &off, &status, &fpregset, &psinfo,
- vm->vm_daddr, ctob(vm->vm_dsize),
- (void *)trunc_page(USRSTACK - ctob(vm->vm_ssize)),
- ctob(vm->vm_ssize));
+ elf_puthdr(p, hdr, &off, &status, &fpregset, &psinfo, numsegs);
/* Write it to the core file. */
error = vn_rdwr(UIO_WRITE, vp, hdr, hdrsize, (off_t)0,
@@ -812,34 +971,20 @@ elf_corehdr(p, vp, cred)
return error;
}
-static size_t
-elf_hdrsize(void)
-{
- size_t off;
-
- off = 0;
- elf_puthdr(NULL, &off, NULL, NULL, NULL, NULL, 0, NULL, 0);
- return off;
-}
-
static void
-elf_puthdr(void *dst, size_t *off, const prstatus_t *status,
- const prfpregset_t *fpregset, const prpsinfo_t *psinfo,
- const void *data, size_t datasz, const void *stack, size_t stacksz)
+elf_puthdr(struct proc *p, void *dst, size_t *off, const prstatus_t *status,
+ const prfpregset_t *fpregset, const prpsinfo_t *psinfo, int numsegs)
{
size_t ehoff;
size_t phoff;
size_t noteoff;
size_t notesz;
- size_t dataoff;
- size_t stackoff;
- int numsegs = 3;
ehoff = *off;
*off += sizeof(Elf_Ehdr);
phoff = *off;
- *off += numsegs * sizeof(Elf_Phdr);
+ *off += (numsegs + 1) * sizeof(Elf_Phdr);
noteoff = *off;
elf_putnote(dst, off, "FreeBSD", NT_PRSTATUS, status,
@@ -850,12 +995,13 @@ elf_puthdr(void *dst, size_t *off, const prstatus_t *status,
sizeof *psinfo);
notesz = *off - noteoff;
- /* Align up to a page boundary for the data segment. */
+ /* Align up to a page boundary for the program segments. */
*off = round_page(*off);
if (dst != NULL) {
Elf_Ehdr *ehdr;
Elf_Phdr *phdr;
+ struct phdr_closure phc;
/*
* Fill in the ELF header.
@@ -879,7 +1025,7 @@ elf_puthdr(void *dst, size_t *off, const prstatus_t *status,
ehdr->e_flags = 0;
ehdr->e_ehsize = sizeof(Elf_Ehdr);
ehdr->e_phentsize = sizeof(Elf_Phdr);
- ehdr->e_phnum = numsegs;
+ ehdr->e_phnum = numsegs + 1;
ehdr->e_shentsize = sizeof(Elf_Shdr);
ehdr->e_shnum = 0;
ehdr->e_shstrndx = SHN_UNDEF;
@@ -900,25 +1046,10 @@ elf_puthdr(void *dst, size_t *off, const prstatus_t *status,
phdr->p_align = 0;
phdr++;
- /* The data segment. */
- phdr->p_type = PT_LOAD;
- phdr->p_offset = *off;
- phdr->p_vaddr = (Elf_Addr)data;
- phdr->p_paddr = 0;
- phdr->p_filesz = phdr->p_memsz = datasz;
- phdr->p_align = PAGE_SIZE;
- phdr->p_flags = PF_R | PF_W | PF_X;
- phdr++;
-
- /* The stack segment. */
- phdr->p_type = PT_LOAD;
- phdr->p_offset = *off + datasz;
- phdr->p_vaddr = (Elf_Addr)stack;
- phdr->p_paddr = 0;
- phdr->p_filesz = phdr->p_memsz = stacksz;
- phdr->p_align = PAGE_SIZE;
- phdr->p_flags = PF_R | PF_W | PF_X;
- phdr++;
+ /* All the writable segments from the program. */
+ phc.phdr = phdr;
+ phc.offset = *off;
+ each_writable_segment(p, cb_put_phdr, &phc);
}
}
OpenPOWER on IntegriCloud