aboutsummaryrefslogtreecommitdiff
path: root/exec/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'exec/exec.c')
-rw-r--r--exec/exec.c752
1 files changed, 565 insertions, 187 deletions
diff --git a/exec/exec.c b/exec/exec.c
index a9de454a..4c2fcec1 100644
--- a/exec/exec.c
+++ b/exec/exec.c
@@ -1,5 +1,6 @@
/* GNU Hurd standard exec server.
- Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1992,93,94,95,96,98,99,2000,01,02,04
+ Free Software Foundation, Inc.
Written by Roland McGrath.
Can exec ELF format directly.
@@ -10,6 +11,9 @@
Can exec any executable format the BFD library understands
to be for this flavor of machine.
#endif
+ #ifdef BZIP2
+ Can bunzip2 executables into core on the fly.
+ #endif
This file is part of the GNU Hurd.
@@ -32,9 +36,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "priv.h"
#include <hurd.h>
#include <hurd/exec.h>
-#include <hurd/shared.h>
#include <sys/stat.h>
+#include <sys/param.h>
#include <unistd.h>
+#include <stdbool.h>
mach_port_t procserver; /* Our proc port. */
@@ -70,6 +75,10 @@ b2he (error_t deflt)
static void check_gzip (struct execdata *);
#endif
+#ifdef BZIP2
+static void check_bzip2 (struct execdata *);
+#endif
+
#ifdef BFD
/* Check a section, updating the `locations' vector [BFD]. */
@@ -132,7 +141,7 @@ load_section (void *section, struct execdata *u)
#ifdef BFD
asection *const sec = section;
#endif
- const Elf32_Phdr *const ph = section;
+ const ElfW(Phdr) *const ph = section;
if (u->error)
return;
@@ -173,6 +182,7 @@ load_section (void *section, struct execdata *u)
if (! anywhere)
addr += u->info.elf.loadbase;
else
+#if 0
switch (elf_machine)
{
case EM_386:
@@ -187,6 +197,9 @@ load_section (void *section, struct execdata *u)
default:
break;
}
+#endif
+ if (anywhere && addr < vm_page_size)
+ addr = vm_page_size;
}
if (memsz == 0)
@@ -214,16 +227,19 @@ load_section (void *section, struct execdata *u)
if (! u->error && off != 0)
{
vm_address_t page = 0;
- u->error = vm_allocate (mach_task_self (),
- &page, vm_page_size, 1);
+ page = (vm_address_t) mmap (0, vm_page_size,
+ PROT_READ|PROT_WRITE, MAP_ANON,
+ 0, 0);
+ u->error = (page == -1) ? errno : 0;
if (! u->error)
{
- memcpy ((void *) page,
+ u->error = hurd_safe_copyin ((void *) page, /* XXX/fault */
(void *) (contents + (size - off)),
off);
- u->error = vm_write (u->task, mapstart + (size - off),
- page, vm_page_size);
- vm_deallocate (mach_task_self (), page, vm_page_size);
+ if (! u->error)
+ u->error = vm_write (u->task, mapstart + (size - off),
+ page, vm_page_size);
+ munmap ((caddr_t) page, vm_page_size);
}
}
/* Reset the current protections to the desired state. */
@@ -256,21 +272,10 @@ load_section (void *section, struct execdata *u)
{
/* Cannot map the data. Read it into a buffer and vm_write
it into the task. */
- void *buf;
const vm_size_t size = filesz - (mapstart - addr);
- u->error = vm_allocate (mach_task_self (),
- (vm_address_t *) &buf, size, 1);
- if (! u->error)
- {
- if (fseek (&u->stream,
- filepos + (mapstart - addr), SEEK_SET) ||
- fread (buf, size, 1, &u->stream) != 1)
- u->error = errno;
- else
- write_to_task (mapstart, size, vm_prot,
- (vm_address_t) buf);
- vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
- }
+ void *buf = map (u, filepos + (mapstart - addr), size);
+ if (buf)
+ write_to_task (mapstart, size, vm_prot, (vm_address_t) buf);
}
if (u->error)
return;
@@ -308,8 +313,12 @@ load_section (void *section, struct execdata *u)
&overlap_page, vm_page_size, 0);
size = vm_page_size;
if (!u->error)
- u->error = vm_allocate (mach_task_self (),
- &ourpage, vm_page_size, 1);
+ {
+ ourpage = (vm_address_t) mmap (0, vm_page_size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ u->error = (ourpage == -1) ? errno : 0;
+ }
}
if (u->error)
{
@@ -325,14 +334,17 @@ load_section (void *section, struct execdata *u)
readsize = filesz;
if (SECTION_IN_MEMORY_P)
- bcopy (SECTION_CONTENTS, readaddr, readsize);
+ memcpy (readaddr, SECTION_CONTENTS, readsize);
else
- if (fseek (&u->stream, filepos, SEEK_SET) ||
- fread (readaddr, readsize, 1, &u->stream) != 1)
- {
- u->error = errno;
+ {
+ const void *contents = map (u, filepos, readsize);
+ if (!contents)
goto maplose;
- }
+ u->error = hurd_safe_copyin (readaddr, contents,
+ readsize); /* XXX/fault */
+ if (u->error)
+ goto maplose;
+ }
u->error = vm_write (u->task, overlap_page, ourpage, size);
if (u->error == KERN_PROTECTION_FAILURE)
{
@@ -349,7 +361,7 @@ load_section (void *section, struct execdata *u)
u->error = vm_protect (u->task, overlap_page, size,
0, vm_prot);
}
- vm_deallocate (mach_task_self (), ourpage, size);
+ munmap ((caddr_t) ourpage, size);
if (u->error)
goto maplose;
}
@@ -412,15 +424,122 @@ load_section (void *section, struct execdata *u)
u->error = vm_write (u->task, overlap_page, ourpage, size);
if (! u->error && !(vm_prot & VM_PROT_WRITE))
u->error = vm_protect (u->task, overlap_page, size, 0, vm_prot);
- vm_deallocate (mach_task_self (), ourpage, size);
+ munmap ((caddr_t) ourpage, size);
}
}
}
-/* Make sure our mapping window (or read buffer) covers
- LEN bytes of the file starting at POSN. */
+/* XXX all accesses of the mapped data need to use fault handling
+ to abort the RPC when mapped file data generates bad page faults.
+ I've marked some accesses with XXX/fault comments.
+ --roland */
-static void *
+void *
+map (struct execdata *e, off_t posn, size_t len)
+{
+ const size_t size = e->file_size;
+ size_t offset;
+
+ if ((map_filepos (e) & ~(map_vsize (e) - 1)) == (posn & ~(map_vsize (e) - 1))
+ && posn + len - map_filepos (e) <= map_fsize (e))
+ /* The current mapping window covers it. */
+ offset = posn & (map_vsize (e) - 1);
+ else if (posn + len > size)
+ /* The requested data wouldn't fit in the file. */
+ return NULL;
+ else if (e->file_data != NULL) {
+ return e->file_data + posn;
+ } else if (e->filemap == MACH_PORT_NULL)
+ {
+ /* No mapping for the file. Read the data by RPC. */
+ char *buffer = map_buffer (e);
+ mach_msg_type_number_t nread = map_vsize (e);
+
+ assert (e->file_data == NULL); /* Must be first or second case. */
+
+ /* Read as much as we can get into the buffer right now. */
+ e->error = io_read (e->file, &buffer, &nread, posn, round_page (len));
+ if (e->error)
+ return NULL;
+ if (buffer != map_buffer (e))
+ {
+ /* The data was returned out of line. Discard the old buffer. */
+ if (map_vsize (e) != 0)
+ munmap (map_buffer (e), map_vsize (e));
+ map_buffer (e) = buffer;
+ map_vsize (e) = round_page (nread);
+ }
+
+ map_filepos (e) = posn;
+ map_set_fsize (e, nread);
+ offset = 0;
+ }
+ else
+ {
+ /* Deallocate the old mapping area. */
+ if (map_buffer (e) != NULL)
+ munmap (map_buffer (e), map_vsize (e));
+ map_buffer (e) = NULL;
+
+ /* Make sure our mapping is page-aligned in the file. */
+ offset = posn & (vm_page_size - 1);
+ map_filepos (e) = trunc_page (posn);
+ map_vsize (e) = round_page (posn + len) - map_filepos (e);
+
+ /* Map the data from the file. */
+ if (vm_map (mach_task_self (),
+ (vm_address_t *) &map_buffer (e), map_vsize (e), 0, 1,
+ e->filemap, map_filepos (e), 1, VM_PROT_READ, VM_PROT_READ,
+ VM_INHERIT_NONE))
+ {
+ e->error = EIO;
+ return NULL;
+ }
+
+ if (e->cntl)
+ e->cntl->accessed = 1;
+
+ map_set_fsize (e, MIN (map_vsize (e), size - map_filepos (e)));
+ }
+
+ return map_buffer (e) + offset;
+}
+
+
+/* Initialize E's stdio stream. */
+static void prepare_stream (struct execdata *e);
+
+/* Point the stream at the buffer of file data in E->file_data. */
+static void prepare_in_memory (struct execdata *e);
+
+
+#ifndef EXECDATA_STREAM
+
+/* We don't have a stdio stream, but we have a mapping window
+ we need to initialize. */
+static void
+prepare_stream (struct execdata *e)
+{
+ e->map_buffer = NULL;
+ e->map_vsize = e->map_fsize = 0;
+ e->map_filepos = 0;
+}
+
+static void prepare_in_memory (struct execdata *e)
+{
+ prepare_stream(e);
+}
+
+#else
+
+#ifdef _STDIO_USES_IOSTREAM
+
+# error implement me for libio!
+
+#else /* old GNU stdio */
+
+#if 0
+void *
map (struct execdata *e, off_t posn, size_t len)
{
FILE *f = &e->stream;
@@ -431,6 +550,13 @@ map (struct execdata *e, off_t posn, size_t len)
f->__buffer + (posn + len - f->__offset) < f->__get_limit)
/* The current mapping window covers it. */
offset = posn & (f->__bufsize - 1);
+ else if (e->file_data != NULL)
+ {
+ /* The current "mapping window" is in fact the whole file contents.
+ So if it's not in there, it's not in there. */
+ f->__eof = 1;
+ return NULL;
+ }
else if (e->filemap == MACH_PORT_NULL)
{
/* No mapping for the file. Read the data by RPC. */
@@ -448,8 +574,7 @@ map (struct execdata *e, off_t posn, size_t len)
{
/* The data was returned out of line. Discard the old buffer. */
if (f->__bufsize != 0)
- vm_deallocate (mach_task_self (),
- (vm_address_t) f->__buffer, f->__bufsize);
+ munmap (f->__buffer, f->__bufsize);
f->__buffer = buffer;
f->__bufsize = round_page (nread);
}
@@ -462,8 +587,7 @@ map (struct execdata *e, off_t posn, size_t len)
{
/* Deallocate the old mapping area. */
if (f->__buffer != NULL)
- vm_deallocate (mach_task_self (), (vm_address_t) f->__buffer,
- f->__bufsize);
+ munmap (f->__buffer, f->__bufsize);
f->__buffer = NULL;
/* Make sure our mapping is page-aligned in the file. */
@@ -474,7 +598,7 @@ map (struct execdata *e, off_t posn, size_t len)
/* Map the data from the file. */
if (vm_map (mach_task_self (),
(vm_address_t *) &f->__buffer, f->__bufsize, 0, 1,
- e->filemap, f->__target, 1, VM_PROT_READ, VM_PROT_READ,
+ e->filemap, f->__offset, 1, VM_PROT_READ, VM_PROT_READ,
VM_INHERIT_NONE))
{
errno = e->error = EIO;
@@ -486,7 +610,7 @@ map (struct execdata *e, off_t posn, size_t len)
e->cntl->accessed = 1;
if (f->__offset + f->__bufsize > size)
- f->__get_limit = f->__buffer + (size - f->__target);
+ f->__get_limit = f->__buffer + (size - f->__offset);
else
f->__get_limit = f->__buffer + f->__bufsize;
}
@@ -502,13 +626,26 @@ map (struct execdata *e, off_t posn, size_t len)
return f->__bufp;
}
+#endif
-/* stdio input-room function. */
+/* stdio input-room function.
+ XXX/fault in the stdio case (or libio replacement), i.e. for bfd
+ (if ever revived), need to check all the mapping fault issues */
static int
input_room (FILE *f)
{
- return (map (f->__cookie, f->__target, 1) == NULL ? EOF :
- (unsigned char) *f->__bufp++);
+ struct execdata *e = f->__cookie;
+ char *p = map (e, f->__target, 1);
+ if (p == NULL)
+ {
+ (e->error ? f->__error : f->__eof) = 1;
+ return EOF;
+ }
+
+ f->__target = f->__offset;
+ f->__bufp = p;
+
+ return (unsigned char) *f->__bufp++;
}
static int
@@ -517,12 +654,68 @@ close_exec_stream (void *cookie)
struct execdata *e = cookie;
if (e->stream.__buffer != NULL)
- vm_deallocate (mach_task_self (), (vm_address_t) e->stream.__buffer,
- e->stream.__bufsize);
+ munmap (e->stream.__buffer, e->stream.__bufsize);
+
+ return 0;
+}
+
+/* stdio seek function. */
+static int
+fake_seek (void *cookie, fpos_t *pos, int whence)
+{
+ struct execdata *e = cookie;
+
+ /* Set __target to match the specifed seek location */
+ switch (whence)
+ {
+ case SEEK_END:
+ e->stream.__target = e->file_size + *pos;
+ break;
+ case SEEK_CUR:
+ e->stream.__target += *pos;
+ break;
+
+ case SEEK_SET:
+ e->stream.__target = *pos;
+ break;
+ }
+ *pos = e->stream.__target;
return 0;
}
+/* Initialize E's stdio stream. */
+static void
+prepare_stream (struct execdata *e)
+{
+ memset (&e->stream, 0, sizeof (e->stream));
+ e->stream.__magic = _IOMAGIC;
+ e->stream.__mode.__read = 1;
+ e->stream.__userbuf = 1;
+ e->stream.__room_funcs.__input = input_room;
+ e->stream.__io_funcs.seek = fake_seek;
+ e->stream.__io_funcs.close = close_exec_stream;
+ e->stream.__cookie = e;
+ e->stream.__seen = 1;
+}
+
+/* Point the stream at the buffer of file data. */
+static void
+prepare_in_memory (struct execdata *e)
+{
+ memset (&e->stream, 0, sizeof (e->stream));
+ e->stream.__magic = _IOMAGIC;
+ e->stream.__mode.__read = 1;
+ e->stream.__buffer = e->file_data;
+ e->stream.__bufsize = e->file_size;
+ e->stream.__get_limit = e->stream.__buffer + e->stream.__bufsize;
+ e->stream.__bufp = e->stream.__buffer;
+ e->stream.__seen = 1;
+}
+#endif
+
+#endif
+
/* Prepare to check and load FILE. */
static void
@@ -543,16 +736,7 @@ prepare (file_t file, struct execdata *e)
e->interp.section = NULL;
/* Initialize E's stdio stream. */
- memset (&e->stream, 0, sizeof (e->stream));
- e->stream.__magic = _IOMAGIC;
- e->stream.__mode.__read = 1;
- e->stream.__userbuf = 1;
- e->stream.__room_funcs.__input = input_room;
- /* This never gets called, but fseek returns ESPIPE if it's null. */
- e->stream.__io_funcs.seek = __default_io_functions.seek;
- e->stream.__io_funcs.close = close_exec_stream;
- e->stream.__cookie = e;
- e->stream.__seen = 1;
+ prepare_stream (e);
/* Try to mmap FILE. */
e->error = io_map (file, &rd, &wr);
@@ -569,17 +753,7 @@ prepare (file_t file, struct execdata *e)
e->filemap = rd;
e->error = /* io_map_cntl (file, &e->cntlmap) */ EOPNOTSUPP; /* XXX */
- if (e->error)
- {
- /* No shared page. Do a stat to find the file size. */
- struct stat st;
- e->error = io_stat (file, &st);
- if (e->error)
- return;
- e->file_size = st.st_size;
- e->optimal_block = st.st_blksize;
- }
- else
+ if (!e->error)
e->error = vm_map (mach_task_self (), (vm_address_t *) &e->cntl,
vm_page_size, 0, 1, e->cntlmap, 0, 0,
VM_PROT_READ|VM_PROT_WRITE,
@@ -617,9 +791,17 @@ prepare (file_t file, struct execdata *e)
break;
}
}
- else if (e->error == EOPNOTSUPP)
- /* We can't mmap FILE, but perhaps we can do normal I/O to it. */
- e->error = 0;
+
+ if (!e->cntl && (!e->error || e->error == EOPNOTSUPP))
+ {
+ /* No shared page. Do a stat to find the file size. */
+ struct stat st;
+ e->error = io_stat (file, &st);
+ if (e->error)
+ return;
+ e->file_size = st.st_size;
+ e->optimal_block = st.st_blksize;
+ }
}
/* Check the magic number, etc. of the file.
@@ -669,18 +851,18 @@ check_bfd (struct execdata *e)
static void
check_elf (struct execdata *e)
{
- Elf32_Ehdr *ehdr = map (e, 0, sizeof (Elf32_Ehdr));
- Elf32_Phdr *phdr;
+ ElfW(Ehdr) *ehdr = map (e, 0, sizeof (ElfW(Ehdr)));
+ ElfW(Phdr) *phdr;
if (! ehdr)
{
- if (! ferror (&e->stream))
+ if (!e->error)
e->error = ENOEXEC;
return;
}
- if (*(Elf32_Word *) ehdr != ((union { Elf32_Word word;
- unsigned char string[SELFMAG]; })
+ if (*(ElfW(Word) *) ehdr != ((union { ElfW(Word) word;
+ unsigned char string[SELFMAG]; })
{ string: ELFMAG }).word)
{
e->error = ENOEXEC;
@@ -691,13 +873,15 @@ check_elf (struct execdata *e)
ehdr->e_ident[EI_DATA] != host_ELFDATA ||
ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
ehdr->e_version != EV_CURRENT ||
- ehdr->e_machine != elf_machine ||
ehdr->e_ehsize < sizeof *ehdr ||
- ehdr->e_phentsize != sizeof (Elf32_Phdr))
+ ehdr->e_phentsize != sizeof (ElfW(Phdr)))
{
e->error = ENOEXEC;
return;
}
+ e->error = elf_machine_matches_host (ehdr->e_machine);
+ if (e->error)
+ return;
/* Extract all this information now, while EHDR is mapped.
The `map' call below for the phdrs may reuse the mapping window. */
@@ -707,24 +891,33 @@ check_elf (struct execdata *e)
e->info.elf.loadbase = 0;
e->info.elf.phnum = ehdr->e_phnum;
- phdr = map (e, ehdr->e_phoff, ehdr->e_phnum * sizeof (Elf32_Phdr));
+ phdr = map (e, ehdr->e_phoff, ehdr->e_phnum * sizeof (ElfW(Phdr)));
if (! phdr)
{
- if (! ferror (&e->stream))
+ if (!e->error)
e->error = ENOEXEC;
return;
}
e->info.elf.phdr = phdr;
+ e->info.elf.phdr_addr = ehdr->e_phoff;
}
+/* Copy MAPPED_PHDR into E->info.elf.phdr, filling in E->interp.phdr
+ in the process. */
static void
-check_elf_phdr (struct execdata *e, const Elf32_Phdr *mapped_phdr,
- vm_address_t *phdr_addr, vm_size_t *phdr_size)
+check_elf_phdr (struct execdata *e, const ElfW(Phdr) *mapped_phdr)
{
- const Elf32_Phdr *phdr;
+ const ElfW(Phdr) *phdr;
+ bool seen_phdr = false;
memcpy (e->info.elf.phdr, mapped_phdr,
- e->info.elf.phnum * sizeof (Elf32_Phdr));
+ e->info.elf.phnum * sizeof (ElfW(Phdr)));
+
+ /* Default state if we do not see PT_GNU_STACK telling us what to do.
+ Executable stack is the compatible default.
+ (XXX should be machine-dependent??)
+ */
+ e->info.elf.execstack = 1;
for (phdr = e->info.elf.phdr;
phdr < &e->info.elf.phdr[e->info.elf.phnum];
@@ -734,26 +927,37 @@ check_elf_phdr (struct execdata *e, const Elf32_Phdr *mapped_phdr,
case PT_INTERP:
e->interp.phdr = phdr;
break;
- case PT_PHDR:
- if (phdr_addr)
- *phdr_addr = phdr->p_vaddr & ~(phdr->p_align - 1);
- if (phdr_size)
- *phdr_size = phdr->p_memsz;
- break;
case PT_LOAD:
- /* Sanity check. */
if (e->file_size <= (off_t) (phdr->p_offset +
phdr->p_filesz))
- e->error = ENOEXEC;
+ {
+ e->error = ENOEXEC;
+ return;
+ }
+ /* Check if this is the segment that contains the phdr image. */
+ if (!seen_phdr
+ && (phdr->p_offset & -phdr->p_align) == 0 /* Sanity check. */
+ && phdr->p_offset <= e->info.elf.phdr_addr
+ && e->info.elf.phdr_addr - phdr->p_offset < phdr->p_filesz)
+ {
+ e->info.elf.phdr_addr += phdr->p_vaddr - phdr->p_offset;
+ seen_phdr = true;
+ }
+ break;
+ case PT_GNU_STACK:
+ e->info.elf.execstack = phdr->p_flags & PF_X;
break;
}
+
+ if (!seen_phdr)
+ e->info.elf.phdr_addr = 0;
}
static void
check (struct execdata *e)
{
- check_elf (e);
+ check_elf (e); /* XXX/fault */
#ifdef BFD
if (e->error == ENOEXEC)
{
@@ -781,7 +985,7 @@ finish_mapping (struct execdata *e)
e->cntl->conch_status = USER_HAS_NOT_CONCH;
spin_unlock (&e->cntl->lock);
}
- vm_deallocate (mach_task_self (), (vm_address_t) e->cntl, vm_page_size);
+ munmap (e->cntl, vm_page_size);
e->cntl = NULL;
}
if (e->filemap != MACH_PORT_NULL)
@@ -796,7 +1000,9 @@ finish_mapping (struct execdata *e)
}
}
-/* Clean up after reading the file (need not be completed). */
+/* Clean up after reading the file (need not be completed).
+ Note: this may be called several times for the E, so it must take care
+ of checking what was already freed. */
void
finish (struct execdata *e, int dealloc_file)
{
@@ -809,7 +1015,19 @@ finish (struct execdata *e, int dealloc_file)
}
else
#endif
- fclose (&e->stream);
+ {
+#ifdef EXECDATA_STREAM
+ fclose (&e->stream);
+#else
+ if (e->file_data != NULL) {
+ free (e->file_data);
+ e->file_data = NULL;
+ } else if (map_buffer (e) != NULL) {
+ munmap (map_buffer (e), map_vsize (e));
+ map_buffer (e) = NULL;
+ }
+#endif
+ }
if (dealloc_file && e->file != MACH_PORT_NULL)
{
mach_port_deallocate (mach_task_self (), e->file);
@@ -838,12 +1056,12 @@ load (task_t usertask, struct execdata *e)
else
#endif
{
- Elf32_Word i;
+ ElfW(Word) i;
for (i = 0; i < e->info.elf.phnum; ++i)
if (e->info.elf.phdr[i].p_type == PT_LOAD)
load_section (&e->info.elf.phdr[i], e);
- /* The entry point address is relative to whereever we loaded the
+ /* The entry point address is relative to wherever we loaded the
program text. */
e->entry += e->info.elf.loadbase;
}
@@ -893,7 +1111,7 @@ load (task_t usertask, struct execdata *e)
/* Force it to be paged in. */
(void) *(volatile int *) a;
- vm_deallocate (mach_task_self (), myaddr, mysize);
+ munmap ((caddr_t) myaddr, mysize);
}
}
@@ -927,9 +1145,23 @@ check_gzip (struct execdata *earg)
size_t zipdatasz = 0;
FILE *zipout = NULL;
jmp_buf ziperr;
+ off_t zipread_pos = 0;
int zipread (char *buf, size_t maxread)
{
- return fread (buf, 1, maxread, &e->stream);
+ char *contents = map (e, zipread_pos, 1);
+ size_t n;
+ if (contents == NULL)
+ {
+ errno = e->error;
+ return -1;
+ }
+ n = MIN (maxread, map_buffer (e) + map_fsize (e) - contents);
+ errno = hurd_safe_copyin (buf, contents, n); /* XXX/fault */
+ if (errno)
+ longjmp (ziperr, 2);
+
+ zipread_pos += n;
+ return n;
}
void zipwrite (const char *buf, size_t nwrite)
{
@@ -964,7 +1196,6 @@ check_gzip (struct execdata *earg)
return;
}
- rewind (&e->stream);
if (get_method (0) != 0)
{
/* Not a happy gzip file. */
@@ -989,41 +1220,136 @@ check_gzip (struct execdata *earg)
/* The output is complete. Clean up the stream and store its resultant
buffer and size in the execdata as the file contents. */
fclose (zipout);
+
+ /* Clean up the old exec file stream's state.
+ Now that we have the contents all in memory (in E->file_data),
+ nothing will in fact ever try to use E->stream again. */
+ finish (e, 0);
+
+ /* Prepare the stream state to use the file contents already in memory. */
e->file_data = zipdata;
e->file_size = zipdatasz;
+ prepare_in_memory (e);
+}
+#endif
+
+#ifdef BZIP2
+/* Check the file for being a bzip2'd image. Return with ENOEXEC means not
+ a valid bzip2 file; return with another error means lossage in decoding;
+ return with zero means the file was uncompressed into memory which E now
+ points to, and `check' can be run again. */
+
+static void
+check_bzip2 (struct execdata *earg)
+{
+ struct execdata *e = earg;
+ /* Entry points to bunzip2 engine. */
+ void do_bunzip2 (void);
+ /* Callbacks from unzip for I/O and error interface. */
+ extern int (*unzip_read) (char *buf, size_t maxread);
+ extern void (*unzip_write) (const char *buf, size_t nwrite);
+ extern void (*unzip_read_error) (void);
+ extern void (*unzip_error) (const char *msg);
+
+ char *zipdata = NULL;
+ size_t zipdatasz = 0;
+ FILE *zipout = NULL;
+ jmp_buf ziperr;
+ off_t zipread_pos = 0;
+ int zipread (char *buf, size_t maxread)
+ {
+ char *contents = map (e, zipread_pos, 1);
+ size_t n;
+ if (contents == NULL)
+ {
+ errno = e->error;
+ return -1;
+ }
+ n = MIN (maxread, map_buffer (e) + map_fsize (e) - contents);
+ errno = hurd_safe_copyin (buf, contents, n); /* XXX/fault */
+ if (errno)
+ longjmp (ziperr, 2);
+
+ zipread_pos += n;
+ return n;
+ }
+ void zipwrite (const char *buf, size_t nwrite)
+ {
+ if (fwrite (buf, nwrite, 1, zipout) != 1)
+ longjmp (ziperr, 1);
+ }
+ void ziprderr (void)
+ {
+ errno = ENOEXEC;
+ longjmp (ziperr, 2);
+ }
+ void ziperror (const char *msg)
+ {
+ errno = ENOEXEC;
+ longjmp (ziperr, 2);
+ }
+
+ unzip_read = zipread;
+ unzip_write = zipwrite;
+ unzip_read_error = ziprderr;
+ unzip_error = ziperror;
+
+ if (setjmp (ziperr))
+ {
+ /* Error in unzipping jumped out. */
+ if (zipout)
+ {
+ fclose (zipout);
+ free (zipdata);
+ }
+ e->error = errno;
+ return;
+ }
+
+ zipout = open_memstream (&zipdata, &zipdatasz);
+ if (! zipout)
+ {
+ e->error = errno;
+ return;
+ }
- /* Clean up the old exec file stream's state. */
+ /* Call the bunzip2 engine. */
+ do_bunzip2 ();
+
+ /* The output is complete. Clean up the stream and store its resultant
+ buffer and size in the execdata as the file contents. */
+ fclose (zipout);
+
+ /* Clean up the old exec file stream's state.
+ Now that we have the contents all in memory (in E->file_data),
+ nothing will in fact ever try to use E->stream again. */
finish (e, 0);
- /* Point the stream at the buffer of file data. */
- memset (&e->stream, 0, sizeof (e->stream));
- e->stream.__magic = _IOMAGIC;
- e->stream.__mode.__read = 1;
- e->stream.__buffer = e->file_data;
- e->stream.__bufsize = e->file_size;
- e->stream.__get_limit = e->stream.__buffer + e->stream.__bufsize;
- e->stream.__bufp = e->stream.__buffer;
- e->stream.__seen = 1;
+ /* Prepare the stream state to use the file contents already in memory. */
+ e->file_data = zipdata;
+ e->file_size = zipdatasz;
+ prepare_in_memory (e);
}
#endif
-static inline error_t
-servercopy (void **arg, mach_msg_type_number_t argsize, boolean_t argcopy)
+static inline void *
+servercopy (void *arg, mach_msg_type_number_t argsize, boolean_t argcopy,
+ error_t *errorp)
{
- if (argcopy)
- {
- /* ARG came in-line, so we must copy it. */
- error_t error;
- void *copy;
- error = vm_allocate (mach_task_self (),
- (vm_address_t *) &copy, argsize, 1);
- if (error)
- return error;
- bcopy (*arg, copy, argsize);
- *arg = copy;
+ if (! argcopy)
+ return arg;
+
+ /* ARG came in-line, so we must copy it. */
+ void *copy;
+ copy = mmap (0, argsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (copy == MAP_FAILED)
+ {
+ *errorp = errno;
+ return NULL;
}
- return 0;
+ memcpy (copy, arg, argsize);
+ return copy;
}
@@ -1047,8 +1373,6 @@ do_exec (file_t file,
struct bootinfo *boot = 0;
int *ports_replaced;
int secure, defaults;
- vm_address_t phdr_addr = 0;
- vm_size_t phdr_size = 0;
mach_msg_type_number_t i;
int intarray_dealloc = 0; /* Dealloc INTARRAY before returning? */
int oldtask_trashed = 0; /* Have we trashed the old task? */
@@ -1074,6 +1398,7 @@ do_exec (file_t file,
/* The gzip code is really cheesy, not even close to thread-safe.
So we serialize all uses of it. */
mutex_lock (&lock);
+ e->error = 0;
check_gzip (e);
mutex_unlock (&lock);
if (e->error == 0)
@@ -1083,11 +1408,30 @@ do_exec (file_t file,
check (e);
}
#endif
+#ifdef BZIP2
+ if (e->error == ENOEXEC)
+ {
+ /* See if it is a compressed image. */
+ static struct mutex lock = MUTEX_INITIALIZER;
+ /* The bzip2 code is really cheesy, not even close to thread-safe.
+ So we serialize all uses of it. */
+ mutex_lock (&lock);
+ e->error = 0;
+ check_bzip2 (e);
+ mutex_unlock (&lock);
+ if (e->error == 0)
+ /* The file was uncompressed into memory, and now E describes the
+ uncompressed image rather than the actual file. Check it again
+ for a valid magic number. */
+ check (e);
+ }
+#endif
}
/* Here is the main body of the function. */
+ interp.file = MACH_PORT_NULL;
/* Catch this error now, rather than later. */
/* XXX For EXEC_DEFAULTS, this is only an error if one of the user's
@@ -1135,13 +1479,11 @@ do_exec (file_t file,
else
#endif
{
- const Elf32_Phdr *phdr = e.info.elf.phdr;
- e.info.elf.phdr = alloca (e.info.elf.phnum * sizeof (Elf32_Phdr));
- check_elf_phdr (&e, phdr, &phdr_addr, &phdr_size);
+ const ElfW(Phdr) *phdr = e.info.elf.phdr;
+ e.info.elf.phdr = alloca (e.info.elf.phnum * sizeof (ElfW(Phdr)));
+ check_elf_phdr (&e, phdr);
}
- interp.file = MACH_PORT_NULL;
-
if (oldtask == MACH_PORT_NULL)
flags |= EXEC_NEWTASK;
@@ -1152,6 +1494,9 @@ do_exec (file_t file,
e.error = task_create (((flags & EXEC_SECURE) ||
oldtask == MACH_PORT_NULL) ?
mach_task_self () : oldtask,
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
0, &newtask);
if (e.error)
goto out;
@@ -1178,6 +1523,7 @@ do_exec (file_t file,
if (new != MACH_PORT_NULL && reauth)
{
mach_port_t ref = mach_reply_port (), authed;
+ /* MAKE_SEND is safe here because we destroy REF ourselves. */
e.error = io_reauthenticate (new, ref, MACH_MSG_TYPE_MAKE_SEND);
if (! e.error)
e.error = auth_user_authenticate
@@ -1209,11 +1555,6 @@ do_exec (file_t file,
}
bzero (&boot->pi + 1, (char *) &boot[1] - (char *) (&boot->pi + 1));
- /* First record some information about the image itself. */
- boot->phdr_addr = phdr_addr;
- boot->phdr_size = phdr_size;
- boot->user_entry = e.entry;
-
/* These flags say the information we pass through to the new program
may need to be modified. */
secure = (flags & EXEC_SECURE);
@@ -1225,18 +1566,18 @@ do_exec (file_t file,
boot->flags = flags;
- e.error = servercopy ((void **) &argv, argvlen, argv_copy);
+ argv = servercopy (argv, argvlen, argv_copy, &e.error);
if (e.error)
goto stdout;
boot->argv = argv;
boot->argvlen = argvlen;
- e.error = servercopy ((void **) &envp, envplen, envp_copy);
+ envp = servercopy (envp, envplen, envp_copy, &e.error);
if (e.error)
goto stdout;
boot->envp = envp;
boot->envplen = envplen;
- e.error = servercopy ((void **) &dtable, dtablesize * sizeof (mach_port_t),
- dtable_copy);
+ dtable = servercopy (dtable, dtablesize * sizeof (mach_port_t),
+ dtable_copy, &e.error);
if (e.error)
goto stdout;
boot->dtable = dtable;
@@ -1249,10 +1590,8 @@ do_exec (file_t file,
round_page (INIT_INT_MAX * sizeof (int))))
{
/* Allocate a new vector that is big enough. */
- vm_allocate (mach_task_self (),
- (vm_address_t *) &boot->intarray,
- INIT_INT_MAX * sizeof (int),
- 1);
+ boot->intarray = mmap (0, INIT_INT_MAX * sizeof (int),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
memcpy (boot->intarray, intarray, nints * sizeof (int));
intarray_dealloc = !intarray_copy;
}
@@ -1262,8 +1601,8 @@ do_exec (file_t file,
}
else
{
- e.error = servercopy ((void **) &intarray, nints * sizeof (int),
- intarray_copy);
+ intarray = servercopy (intarray, nints * sizeof (int), intarray_copy,
+ &e.error);
if (e.error)
goto stdout;
boot->intarray = intarray;
@@ -1276,9 +1615,8 @@ do_exec (file_t file,
/* Now choose the ports to give the new program. */
boot->nports = nports < INIT_PORT_MAX ? INIT_PORT_MAX : nports;
- vm_allocate (mach_task_self (),
- (vm_address_t *) &boot->portarray,
- boot->nports * sizeof (mach_port_t), 1);
+ boot->portarray = mmap (0, boot->nports * sizeof (mach_port_t),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
/* Start by copying the array as passed. */
for (i = 0; i < nports; ++i)
boot->portarray[i] = portarray[i];
@@ -1374,7 +1712,7 @@ do_exec (file_t file,
name = map (&e, (e.interp.phdr->p_offset
& ~(e.interp.phdr->p_align - 1)),
e.interp.phdr->p_filesz);
- if (! name && ! ferror (&e.stream))
+ if (! name && ! e.error)
e.error = ENOEXEC;
}
@@ -1398,8 +1736,11 @@ do_exec (file_t file,
errno = EBADF;
return MACH_PORT_NULL;
}
+ mach_port_mod_refs (mach_task_self (), boot->dtable[fd],
+ MACH_PORT_RIGHT_SEND, +1);
return boot->dtable[fd];
}
+ /* XXX/fault */
e.error = hurd_file_name_lookup (&user_port, &user_fd, 0,
name, O_READ, 0, &interp.file);
}
@@ -1421,10 +1762,10 @@ do_exec (file_t file,
else
#endif
{
- const Elf32_Phdr *phdr = interp.info.elf.phdr;
+ const ElfW(Phdr) *phdr = interp.info.elf.phdr;
interp.info.elf.phdr = alloca (interp.info.elf.phnum *
- sizeof (Elf32_Phdr));
- check_elf_phdr (&interp, phdr, NULL, NULL);
+ sizeof (ElfW(Phdr)));
+ check_elf_phdr (&interp, phdr);
}
}
e.error = interp.error;
@@ -1439,7 +1780,7 @@ do_exec (file_t file,
if (newtask == oldtask)
{
- thread_array_t threads;
+ thread_t *threads;
mach_msg_type_number_t nthreads, i;
/* Terminate all the threads of the old task. */
@@ -1452,8 +1793,7 @@ do_exec (file_t file,
thread_terminate (threads[i]);
mach_port_deallocate (mach_task_self (), threads[i]);
}
- vm_deallocate (mach_task_self (),
- (vm_address_t) threads, nthreads * sizeof (thread_t));
+ munmap ((caddr_t) threads, nthreads * sizeof (thread_t));
/* Deallocate the entire virtual address space of the task. */
@@ -1500,6 +1840,20 @@ do_exec (file_t file,
/* Clean up. */
finish (&e, 0);
+ /* Now record some essential addresses from the image itself that the
+ program's startup code will need to know. We do this after loading
+ the image so that a load-anywhere image gets the adjusted addresses. */
+ if (e.info.elf.phdr_addr != 0)
+ {
+#ifdef BFD
+ if (!e.bfd)
+#endif
+ e.info.elf.phdr_addr += e.info.elf.loadbase;
+ boot->phdr_addr = e.info.elf.phdr_addr;
+ boot->phdr_size = e.info.elf.phnum * sizeof (ElfW(Phdr));
+ }
+ boot->user_entry = e.entry; /* already adjusted in `load' */
+
/* Create the initial thread. */
e.error = thread_create (newtask, &thread);
if (e.error)
@@ -1513,6 +1867,17 @@ do_exec (file_t file,
&boot->stack_base, &boot->stack_size);
if (e.error)
goto out;
+#ifdef BFD
+ if (!e.bfd)
+#endif
+ {
+ /* It would probably be better to change mach_setup_thread so
+ it does a vm_map with the right permissions to start with. */
+ if (!e.info.elf.execstack)
+ e.error = vm_protect (newtask, boot->stack_base, boot->stack_size,
+ 0, VM_PROT_READ | VM_PROT_WRITE);
+ }
+
if (oldtask != newtask && oldtask != MACH_PORT_NULL)
{
@@ -1538,8 +1903,6 @@ do_exec (file_t file,
proc_reassign (proc, newtask);
mach_port_deallocate (mach_task_self (), proc);
}
-
- mach_port_deallocate (mach_task_self (), oldtask);
}
/* Make sure the proc server has the right idea of our identity. */
@@ -1566,36 +1929,53 @@ do_exec (file_t file,
authenticated anyhow. */
proc_setowner (boot->portarray[INIT_PORT_PROC],
neuids ? euids[0] : 0, !neuids);
-
+
/* Clean up */
if (euids != euidbuf)
- vm_deallocate (mach_task_self (), (vm_address_t) euids,
- neuids * sizeof (uid_t));
+ munmap (euids, neuids * sizeof (uid_t));
if (egids != egidbuf)
- vm_deallocate (mach_task_self (), (vm_address_t) egids,
- negids * sizeof (uid_t));
+ munmap (egids, negids * sizeof (uid_t));
if (auids != auidbuf)
- vm_deallocate (mach_task_self (), (vm_address_t) auids,
- nauids * sizeof (uid_t));
+ munmap (auids, nauids * sizeof (uid_t));
if (agids != agidbuf)
- vm_deallocate (mach_task_self (), (vm_address_t) agids,
- nagids * sizeof (uid_t));
+ munmap (agids, nagids * sizeof (uid_t));
}
}
{
- mach_port_t btport = ports_get_right (boot);
- mach_port_insert_right (mach_task_self (), btport, btport,
- MACH_MSG_TYPE_MAKE_SEND);
+ mach_port_t btport = ports_get_send_right (boot);
e.error = task_set_bootstrap_port (newtask, btport);
mach_port_deallocate (mach_task_self (), btport);
}
out:
- if (e.interp.section)
+ if (interp.file != MACH_PORT_NULL)
finish (&interp, 1);
finish (&e, !e.error);
+ if (!e.error && (flags & EXEC_SIGTRAP)) /* XXX && !secure ? */
+ {
+ /* This is a "traced" exec, i.e. the new task is to be debugged. The
+ caller has requested that the new process stop with SIGTRAP before
+ it starts. Since the process has no signal thread yet to do its
+ own POSIX signal mechanics, we simulate it by notifying the proc
+ server of the signal and leaving the initial thread with a suspend
+ count of one, as it would be if the process were stopped by a
+ POSIX signal. */
+ mach_port_t proc;
+ if (boot->nports > INIT_PORT_PROC)
+ proc = boot->portarray[INIT_PORT_PROC];
+ else
+ /* Ask the proc server for the proc port for this task. */
+ e.error = proc_task2proc (procserver, newtask, &proc);
+ if (!e.error)
+ /* Tell the proc server that the process has stopped with the
+ SIGTRAP signal. Don't bother to check for errors from the RPC
+ here; for non-secure execs PROC may be the user's own proc
+ server its confusion shouldn't make the exec fail. */
+ proc_mark_stop (proc, SIGTRAP, 0);
+ }
+
if (boot)
{
/* Release the original reference. Now there is only one
@@ -1619,7 +1999,8 @@ do_exec (file_t file,
if (thread != MACH_PORT_NULL)
{
- thread_resume (thread);
+ if (!e.error && !(flags & EXEC_SIGTRAP))
+ thread_resume (thread);
mach_port_deallocate (mach_task_self (), thread);
}
@@ -1666,13 +2047,9 @@ do_exec (file_t file,
portarray, and we are not saving those pointers in BOOT for later
transfer, deallocate the original space now. */
if (intarray_dealloc)
- vm_deallocate (mach_task_self (),
- (vm_address_t) intarray,
- nints * sizeof intarray[0]);
+ munmap (intarray, nints * sizeof intarray[0]);
if (!portarray_copy)
- vm_deallocate (mach_task_self (),
- (vm_address_t) portarray,
- nports * sizeof portarray[0]);
+ munmap (portarray, nports * sizeof portarray[0]);
}
return e.error;
@@ -1804,13 +2181,16 @@ S_exec_setexecdata (struct trivfs_protid *protid,
if (nports < INIT_PORT_MAX || nints < INIT_INT_MAX)
return EINVAL; /* */
- err = servercopy ((void **) &ports, nports * sizeof (mach_port_t),
- ports_copy);
+ err = 0;
+ ports = servercopy (ports, nports * sizeof (mach_port_t), ports_copy, &err);
if (err)
return err;
- err = servercopy ((void **) &ints, nints * sizeof (int), ints_copy);
+ ints = servercopy (ints, nints * sizeof (int), ints_copy, &err);
if (err)
- return err;
+ {
+ munmap (ports, nports * sizeof (mach_port_t));
+ return err;
+ }
rwlock_writer_lock (&std_lock);
@@ -1819,16 +2199,14 @@ S_exec_setexecdata (struct trivfs_protid *protid,
mach_msg_type_number_t i;
for (i = 0; i < std_nports; ++i)
mach_port_deallocate (mach_task_self (), std_ports[i]);
- vm_deallocate (mach_task_self (), (vm_address_t)std_ports,
- std_nports * sizeof (mach_port_t));
+ munmap (std_ports, std_nports * sizeof (mach_port_t));
}
std_ports = ports;
std_nports = nports;
if (std_ints)
- vm_deallocate (mach_task_self (), (vm_address_t)std_ints,
- std_nints * sizeof (int));
+ munmap (std_ints, std_nints * sizeof (int));
std_ints = ints;
std_nints = nints;