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.c268
1 files changed, 188 insertions, 80 deletions
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index 342ed02..4f26838 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.31 1998/09/14 22:46:04 jdp Exp $
+ * $Id: imgact_elf.c,v 1.33 1998/09/15 22:07:20 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,30 @@ 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. */
+};
+
+static void cb_put_phdr __P((vm_map_entry_t, void *));
+static void cb_size_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, void *, 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 +723,31 @@ elf_coredump(p)
struct vattr vattr;
int error, error1;
char *name; /* name of corefile */
+ struct sseg_closure seginfo;
+ void *hdr;
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 +771,39 @@ 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);
+
+
+ /*
+ * Allocate memory for building the header, fill it up,
+ * and write it out.
+ */
+ hdr = malloc(hdrsize, M_TEMP, M_WAITOK);
+ if (hdr == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+ error = elf_corehdr(p, vp, cred, seginfo.count, hdr, hdrsize);
+
+ /* 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 = hdrsize;
+ for (i = 0; i < seginfo.count; i++) {
+ error = vn_rdwr(UIO_WRITE, vp, (caddr_t)php->p_vaddr,
+ php->p_filesz, offset, UIO_USERSPACE,
+ IO_NODELOCKED|IO_UNIT, cred, (int *)NULL, p);
+ if (error != 0)
+ break;
+ offset += php->p_filesz;
+ php++;
+ }
+ }
+ free(hdr, M_TEMP);
+
out:
VOP_UNLOCK(vp, 0, p);
error1 = vn_close(vp, FWRITE, cred, p);
@@ -756,20 +812,111 @@ 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;
+}
+
+/*
+ * 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;
+
+ for (entry = map->header.next; entry != &map->header;
+ entry = entry->next) {
+ vm_object_t obj;
+
+ if (entry->eflags & (MAP_ENTRY_IS_A_MAP|MAP_ENTRY_IS_SUB_MAP) ||
+ (entry->protection & (VM_PROT_READ|VM_PROT_WRITE)) !=
+ (VM_PROT_READ|VM_PROT_WRITE))
+ continue;
+
+ if ((obj = entry->object.vm_object) == NULL)
+ continue;
+
+ /* Find the deepest backing object. */
+ while (obj->backing_object != NULL)
+ obj = obj->backing_object;
+
+ /* Ignore memory-mapped devices and such things. */
+ if (obj->type != OBJT_DEFAULT &&
+ obj->type != OBJT_SWAP &&
+ obj->type != OBJT_VNODE)
+ continue;
+
+ (*func)(entry, closure);
+ }
+}
+
+/*
+ * 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, hdr, hdrsize)
struct proc *p;
struct vnode *vp;
struct ucred *cred;
+ int numsegs;
+ size_t hdrsize;
+ void *hdr;
{
struct vmspace *vm = p->p_vmspace;
size_t off;
- size_t hdrsize;
prstatus_t status;
prfpregset_t fpregset;
prpsinfo_t psinfo;
- void *hdr;
- int error;
/* Gather the information for the header. */
bzero(&status, sizeof status);
@@ -788,58 +935,33 @@ 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 */
-
- /* Allocate memory for building the header. */
- hdrsize = elf_hdrsize();
- hdr = malloc(hdrsize, M_TEMP, M_WAITOK);
- if (hdr == NULL)
- return EINVAL;
- bzero(hdr, hdrsize);
+ /* XXX - We don't fill in the command line arguments properly yet. */
+ strncpy(psinfo.pr_psargs, p->p_comm, PRARGSZ);
/* Fill in the header. */
+ bzero(hdr, hdrsize);
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,
+ return vn_rdwr(UIO_WRITE, vp, hdr, hdrsize, (off_t)0,
UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, NULL, p);
-
- free(hdr, M_TEMP);
- 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 +972,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 +1002,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 +1023,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