diff options
-rw-r--r-- | exec/exec.c | 1516 |
1 files changed, 1516 insertions, 0 deletions
diff --git a/exec/exec.c b/exec/exec.c new file mode 100644 index 00000000..2e489711 --- /dev/null +++ b/exec/exec.c @@ -0,0 +1,1516 @@ +/* GNU Hurd standard exec server. + Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. + Written by Roland McGrath. + + #ifdef BFD + Can exec any executable format the BFD library understands + to be for this flavor of machine. [requires nonexistent BFD support] + #endif + #ifdef AOUT + Can exec a.out format. + #endif + +This file is part of the GNU Hurd. + +The GNU Hurd is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The GNU Hurd is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the GNU Hurd; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <errno.h> +#include <mach.h> +#include <mach/notify.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/stat.h> +#include <hurd.h> +#include <hurd/startup.h> +#include <hurd/shared.h> +#include <hurd/fsys.h> +#include <hurd/exec.h> +#include "exec_server.h" +#include "fsys_S.h" + + +/* Default: BFD or a.out? */ +#if !defined (BFD) && !defined (AOUT) +#define AOUT +#endif + + +#ifdef BFD +#include <bfd.h> + +/* Uses the following nonexistent functions: */ +bfd *bfd_openstream (FILE *); + +extern error_t bfd_mach_host_arch_mach (host_t host, + bfd_architecture *arch, + bfd_machine *machine); +#else +#include A_OUT_H + +extern error_t aout_mach_host_machine (host_t host, int *host_machine); +#endif + + +/* Data shared between check, check_section, + load, load_section, and finish. */ +struct execdata + { + /* Passed out to caller. */ + error_t error; + + /* Set by check. */ + vm_address_t entry; +#ifdef BFD + FILE stream; + bfd *bfd; +#else + file_t file; + struct exec *header; +#endif + memory_object_t filemap, cntlmap; + struct shared_io *cntl; + off_t file_size; + + /* Set by caller of load. */ + task_t task; + +#ifdef BFD + /* Vector indexed by section index, + information passed from check_section to load_section. + Set by caller of check_section and load. */ + vm_offset_t *locations; +#endif + }; + +#ifdef BFD +static bfd host_bfd; /* A BFD whose architecture and machine type + reflect those of the running system. */ +#else +static enum machine_type host_machine; /* a.out machine_type of the host. */ +#endif + +static file_t realnode; +static mach_port_t execserver; /* Port doing exec protocol. */ +static mach_port_t fsys; /* Port doing fsys protocol. */ +static mach_port_t request_portset; /* Portset we receive on. */ +char *exec_version = "0.0 pre-alpha"; +char **save_argv; + +/* Standard exec data for secure execs. */ +static mach_port_t *std_ports; +static int *std_ints; +static size_t std_nports, std_nints; + +#ifdef BFD +/* Return a Hurd error code corresponding to the most recent BFD error. */ +static error_t +b2he (error_t deflt) +{ + switch (bfd_error) + { + case system_call_error: + return a2he (errno); + + case no_memory: + return ENOMEM; + + default: + return deflt; + } +} +#else +#define b2he() a2he (errno) +#endif + +#ifdef BFD + +/* Check a section, updating the `locations' vector [BFD]. */ +static void +check_section (bfd *bfd, asection *sec, void *userdata) +{ + struct execdata *u = userdata; + vm_address_t addr; + + if (u->error) + return; + + if (!(sec->flags & SEC_ALLOC|SEC_LOAD) || + (sec->flags & SEC_NEVER_LOAD)) + /* Nothing to do for this section. */ + return; + + if (sec->flags & SEC_RELOC) + { + u->error = EINVAL; + return; + } + + addr = (vm_address_t) sec->bfd_vma; + + if (sec->flags & SEC_LOAD) + { + file_ptr section_offset; + + u->locations[sec->index] = sec->filepos; + if ((off_t) sec->filepos < 0 || (off_t) sec->filepos > e->file_size) + u->error = EINVAL; + } +} +#endif + +enum section { text, data, bss }; + +/* Load or allocate a section. */ +static void +#ifdef BFD +load_section (bfd *bfd, asection *sec, void *userdata) +#else +load_section (enum section section, struct execdata *u) +#endif +{ +#ifdef BFD + struct execdata *u = userdata; +#endif + vm_address_t addr = 0; + vm_offset_t filepos = 0; + vm_size_t secsize = 0; + vm_prot_t vm_prot; + + if (u->error) + return; + +#ifdef BFD + if (sec->flags & SEC_NEVER_LOAD) + /* Nothing to do for this section. */ + return; +#endif + + vm_prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + +#ifdef BFD + addr = (vm_address_t) sec->bfd_vma; + filepos = u->locations[sec->index]; + secsize = sec->size; + if (sec->flags & (SEC_READONLY|SEC_ROM)) + vm_prot &= ~VM_PROT_WRITE; +#else + switch (section) + { + case text: + addr = (vm_address_t) N_TXTADDR (*u->header); + filepos = (vm_offset_t) N_TXTOFF (*u->header); + secsize = N_TXTLEN (*u->header); + vm_prot &= ~VM_PROT_WRITE; + break; + case data: + addr = (vm_address_t) N_DATADDR (*u->header); + filepos = (vm_offset_t) N_DATOFF (*u->header); + secsize = N_DATLEN (*u->header); + break; + case bss: + addr = (vm_address_t) N_BSSADDR (*u->header); + secsize = N_BSSLEN (*u->header); + break; + } +#endif + + if ( +#ifdef BFD + sec->flags & SEC_LOAD +#else + section != bss +#endif + ) + { + vm_address_t mapstart = round_page (addr); + + if (mapstart - addr < secsize) + { + /* MAPSTART is the first page that starts inside the section. + Map all the pages that start inside the section. */ + +#ifdef BFD + if (sec->flags & SEC_IN_MEMORY) + u->error = vm_write (u->task, mapstart, + contents + (mapstart - addr), + secsize - (mapstart - addr)); + else +#endif + u->error = vm_map (u->task, + &mapstart, secsize - (mapstart - addr), 0, 0, + u->filemap, filepos + (mapstart - addr), 1, + vm_prot, + VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, + VM_INHERIT_COPY); + if (u->error) + return; + } + + if (mapstart > addr) + { + /* We must read and copy in the space in the section before the + first page boundary. */ + vm_address_t overlap_page = trunc_page (addr); + vm_address_t ourpage = 0; + vm_size_t size = 0; + void *readaddr; + size_t readsize; + +#ifdef AOUT + if (N_MAGIC (*u->header) == NMAGIC || N_MAGIC (*u->header) == ZMAGIC) + { + u->error = ENOEXEC; + goto maplose; + } +#endif + + if (u->error = vm_read (u->task, overlap_page, vm_page_size, + &ourpage, &size)) + { + maplose: + vm_deallocate (u->task, mapstart, secsize); + return; + } + + readaddr = (void *) (ourpage + (addr - overlap_page)); + readsize = size - (addr - overlap_page); + +#ifdef BFD + if (sec->flags & SEC_IN_MEMORY) + bcopy (sec->contents, readaddr, readsize); + else if (fread (readaddr, readsize, 1, u->stream) != 1) + { + u->error = errno; + goto maplose; + } +#else + if (u->cntl) + { + /* We cannot call io_read while holding the conch, + so we must read by mapping the file ourselves. */ + vm_address_t data; + if (u->error = vm_map (mach_task_self (), &data, readsize, 0, 1, + u->filemap, filepos, 1, + VM_PROT_READ, VM_PROT_READ, + VM_INHERIT_COPY)) + goto maplose; + bcopy ((void *) data, readaddr, readsize); + vm_deallocate (mach_task_self (), data, readsize); + } + else + do + { + char *data; + unsigned int nread; + data = readaddr; + if (u->error = io_read (u->file, &data, &nread, + filepos, readsize)) + goto maplose; + if (data != readaddr) + { + bcopy (data, readaddr, nread); + vm_deallocate (mach_task_self (), (vm_address_t) data, + nread); + } + readaddr += nread; + readsize -= nread; + } while (readsize > 0); +#endif + u->error = vm_write (u->task, overlap_page, ourpage, size); + if (u->error == KERN_PROTECTION_FAILURE) + { + /* The overlap page is not writable; the section + that appears in preceding memory is read-only. + Change the page's protection so we can write it. */ + u->error = vm_protect (u->task, overlap_page, size, + 0, vm_prot | VM_PROT_WRITE); + if (!u->error) + u->error = vm_write (u->task, overlap_page, ourpage, size); + /* If this section is not supposed to be writable either, + restore the page's protection to read-only. */ + 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); + if (u->error) + goto maplose; + } + + if (u->cntl) + u->cntl->accessed = 1; + } +#ifdef BFD + else if (sec->flags & SEC_ALLOC) +#else + else +#endif + { + /* SEC_ALLOC: Allocate zero-filled memory for the section. */ + + vm_address_t mapstart = round_page (addr); + + if (mapstart - addr < secsize) + { + /* MAPSTART is the first page that starts inside the section. + Allocate all the pages that start inside the section. */ + + if (u->error = vm_allocate (u->task, &mapstart, + secsize - (mapstart - addr), 0)) + return; + } + + if (mapstart > addr +#ifdef BFD + && (sec->flags & SEC_HAS_CONTENTS) +#endif + ) + { + /* Zero space in the section before the first page boundary. */ + vm_address_t overlap_page = trunc_page (addr); + vm_address_t ourpage = 0; + vm_size_t size = 0; + if (u->error = vm_read (u->task, overlap_page, vm_page_size, + &ourpage, &size)) + { + vm_deallocate (u->task, mapstart, secsize); + return; + } + bzero ((void *) (ourpage + (addr - overlap_page)), + size - (addr - overlap_page)); + u->error = vm_write (u->task, overlap_page, ourpage, size); + vm_deallocate (mach_task_self (), ourpage, size); + } + } +} + +/* Do post-loading processing for a section. This consists of peeking the + pages of non-demand-paged executables. */ + +static void +#ifdef BFD +postload_section (bfd *bfd, asection *sec, void *userdata) +#else +postload_section (enum section section, struct execdata *u) +#endif +{ +#ifdef BFD + struct execdata *u = userdata; +#endif + vm_address_t addr = 0; + vm_size_t secsize = 0; + +#ifdef BFD + addr = (vm_address_t) sec->bfd_vma; + secsize = sec->size; +#else + switch (section) + { + case text: + addr = (vm_address_t) N_TXTADDR (*u->header); + secsize = N_TXTLEN (*u->header); + break; + case data: + addr = (vm_address_t) N_DATADDR (*u->header); + secsize = N_DATLEN (*u->header); + break; + case bss: + addr = (vm_address_t) N_BSSADDR (*u->header); + secsize = N_BSSLEN (*u->header); + break; + } +#endif + + if ( +#ifdef AOUT + section != bss && N_MAGIC (*u->header) == NMAGIC +#else + (sec->flags & SEC_LOAD) && !(bfd->flags & D_PAGED) +#endif + ) + { + /* Pre-load the section by peeking every mapped page. */ + vm_address_t myaddr, a; + vm_size_t mysize; + myaddr = 0; + + /* We have already mapped the file into the task in load_section. + Now read from the task's memory into our own address space so we + can peek each page and cause it to be paged in. */ + if (u->error = vm_read (u->task, trunc_page (addr), round_page (secsize), + &myaddr, &mysize)) + return; + + /* Peek at the first word of each page. */ + for (a = ((myaddr + mysize) & ~(vm_page_size - 1)); + a >= myaddr; a -= vm_page_size) + /* Force it to be paged in. */ + (void) *(volatile int *) a; + + vm_deallocate (mach_task_self (), myaddr, mysize); + } +} + + + +#ifdef BFD +/* stdio input-room function. */ +static int +input_room (FILE *f) +{ + struct execdata *e = f->__cookie; + const size_t size = e->file_size; + + if (f->__buffer != NULL) + vm_deallocate (mach_task_self (), f->__buffer, f->__bufsize); + + if (f->__target > size) + { + f->__eof = 1; + return EOF; + } + + f->__buffer = NULL; + if (vm_map (mach_task_self (), + (vm_address_t *) &f->__buffer, vm_page_size, 0, 1, + e->filemap, f->__target, 1, VM_PROT_READ, VM_PROT_READ, + VM_INHERIT_NONE)) + { + errno = EIO; + f.__error = 1; + return EOF; + } + f->__bufsize = vm_page_size; + f->__offset = f->__target; + + if (f->__target + f->__bufsize > size) + f->__get_limit = f->__buffer + (size - f->__target); + else + f->__get_limit = f->__buffer + f->__bufsize; + + if (e->cntl) + e->cntl->accessed = 1; + + f->__bufp = f->__buffer; + return (unsigned char) *f->__bufp++; +} + +static int +close_stdio_bfd (void *cookie) +{ + struct execdata *e = cookie; + + if (e->stream.__buffer != NULL) + vm_deallocate (e->stream.__buffer, e->stream.__bufsize); + + return 0; +} +#endif + + +/* Prepare to load FILE. + On successful return, the caller must allocate the + E->locations vector, and map check_section over the BFD. */ +static inline void +check (file_t file, struct execdata *e) +{ + e->file = file; + +#ifdef BFD + e->bfd = NULL; +#endif + e->cntl = NULL; + e->filemap = MACH_PORT_NULL; + e->cntlmap = MACH_PORT_NULL; + + { + memory_object_t rd, wr; + if (e->error = io_map (file, &rd, &wr)) + return; + if (wr) + mach_port_deallocate (mach_task_self (), wr); + if (rd == MACH_PORT_NULL) + { + e->error = EBADF; /* ? XXX */ + return; + } + 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; + if (e->error = io_stat (file, &st)) + return; + e->file_size = st.st_size; + } + else + 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, + VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); + + if (e->cntl) + while (1) + { + spin_lock (&e->cntl->lock); + switch (e->cntl->conch_status) + { + case USER_COULD_HAVE_CONCH: + e->cntl->conch_status = USER_HAS_CONCH; + case USER_HAS_CONCH: + spin_unlock (&e->cntl->lock); + /* Break out of the loop. */ + break; + case USER_RELEASE_CONCH: + case USER_HAS_NOT_CONCH: + default: /* Oops. */ + spin_unlock (&e->cntl->lock); + if (e->error = io_get_conch (e->file)) + return; + /* Continue the loop. */ + continue; + } + + /* Get here if we are now IT. */ + e->file_size = 0; + if (e->cntl->use_file_size) + e->file_size = e->cntl->file_size; + if (e->cntl->use_read_size && e->cntl->read_size > e->file_size) + e->file_size = e->cntl->read_size; + break; + } + } + +#ifdef BFD + /* Open a BFD to do mapped i/o to the file. */ + 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.__close = close_stdio_bfd; + e->stream.__cookie = &e; + + e->bfd = bfd_openstream (&e->stream); + if (e->bfd == NULL) + { + e->error = b2he (ENOEXEC); + return; + } + + bfd_error = no_error; + if (!bfd_check_format (e->bfd, bfd_object)) + { + e->error = b2he (ENOEXEC); + return; + } + else if (!bfd_arch_compatible (e->bfd, &host_bfd, NULL, NULL) || + !(bfd->flags & EXEC_P)) + { + e->error = b2he (EINVAL); + return; + } + + e->entry = bfd->start_address; +#else + /* Map in the a.out header. */ + if (e->file_size < sizeof (*e->header)) + { + e->error = EINVAL; + return; + } + e->header = NULL; + if (e->error = vm_map (mach_task_self (), + (vm_address_t *) &e->header, sizeof (*e->header), + 0, 1, e->filemap, 0, 1, VM_PROT_READ, VM_PROT_READ, + VM_INHERIT_NONE)) + return; + if (N_BADMAG (*e->header)) + { + e->error = ENOEXEC; + return; + } + if (N_MACHTYPE (*e->header) && N_MACHTYPE (*e->header) != host_machine) + { + e->error = EINVAL; + return; + } + + e->entry = e->header->a_entry; +#endif +} + + +/* Load the file. */ +static inline void +load (task_t usertask, struct execdata *e) +{ + if (e->error) + return; + + e->task = usertask; +#ifdef BFD + bfd_map_over_sections (e->bfd, load_section, e); +#else + load_section (text, e); + load_section (data, e); + load_section (bss, e); +#endif +} + +/* Do post-loading processing on the task. */ +static inline void +postload (struct execdata *e) +{ + if (e->error) + return; + +#ifdef BFD + bfd_map_over_sections (e->bfd, postload_section, e); +#else + postload_section (text, e); + postload_section (data, e); + postload_section (bss, e); +#endif +} + + +/* Release the conch and clean up mapping the file and control page. */ +static inline void +finish_mapping (struct execdata *e) +{ + if (e->cntl != NULL) + { + spin_lock (&e->cntl->lock); + if (e->cntl->conch_status == USER_RELEASE_CONCH) + { + spin_unlock (&e->cntl->lock); + io_release_conch (e->file); + } + else + { + 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); + } + if (e->filemap != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), e->filemap); + if (e->cntlmap != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), e->cntlmap); +} + +/* Clean up after reading the file (need not be completed). */ +static inline void +finish (struct execdata *e) +{ + finish_mapping (e); +#ifdef BFD + if (e->bfd != NULL) + (void) bfd_close (e->bfd); +#else + if (e->header != NULL) + vm_deallocate (mach_task_self (), + (vm_address_t) e->header, sizeof (*e->header)); + mach_port_deallocate (mach_task_self (), e->file); +#endif +} + +static int +request_server (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + extern int notify_server (), exec_server (), fsys_server (); + + return (notify_server (inp, outp) || + exec_server (inp, outp) || + fsys_server (inp, outp)); +} + + +/* Allocate SIZE bytes of storage, and make the + resulting pointer a name for a new receive right. */ +static void * +alloc_recv (size_t size) +{ + void *obj = malloc (size); + if (obj == NULL) + return NULL; + + if (mach_port_allocate_name (mach_task_self (), + MACH_PORT_RIGHT_RECEIVE, + (mach_port_t) obj) + == KERN_NAME_EXISTS) + { + void *new = alloc_recv (size); /* Bletch. */ + free (obj); + return new; + } + + return obj; +} + +/* Information kept around to be given to a new task + in response to a message on the task's bootstrap port. */ +struct bootinfo + { + vm_address_t stack_base; + vm_size_t stack_size; + int flags; + char *argv, *envp; + size_t argvlen, envplen, dtablesize, nports, nints; + mach_port_t *dtable, *portarray; + int *intarray; + }; + +static inline error_t +servercopy (void **arg, u_int argsize, boolean_t argcopy) +{ + if (argcopy) + { + /* ARG came in-line, so we must copy it. */ + error_t error; + void *copy; + if (error = vm_allocate (mach_task_self (), + (vm_address_t *) ©, argsize, 1)) + return error; + bcopy (*arg, copy, argsize); + *arg = copy; + } + return 0; +} + +static error_t +do_exec (mach_port_t execserver, + file_t file, + task_t oldtask, + int flags, + char *argv, u_int argvlen, boolean_t argv_copy, + char *envp, u_int envplen, boolean_t envp_copy, + mach_port_t *dtable, u_int dtablesize, boolean_t dtable_copy, + mach_port_t *portarray, u_int nports, boolean_t portarray_copy, + int *intarray, u_int nints, boolean_t intarray_copy, + mach_port_t *deallocnames, u_int ndeallocnames, + mach_port_t *destroynames, u_int ndestroynames) +{ + struct execdata e; + int finished = 0; + task_t newtask = MACH_PORT_NULL; + thread_t thread = MACH_PORT_NULL; + struct bootinfo *boot = 0; + + if (oldtask != MACH_PORT_NULL && (e.error = task_suspend (oldtask))) + return e.error; + + check (file, &e); + if (e.error) + goto out; +#ifdef BFD + e.locations = alloca (e.bfd->section_count * sizeof (vm_offset_t)); + bfd_map_over_sections (e.bfd, check_section, e); + if (e.error) + goto out; +#endif + + if (oldtask == MACH_PORT_NULL) + flags |= EXEC_NEWTASK; + + if (flags & (EXEC_NEWTASK|EXEC_SECURE)) + { + /* Create the new task. If we are not being secure, then use OLDTASK + for the task_create RPC, in case it is something magical. */ + if (e.error = task_create (flags & EXEC_SECURE || !oldtask ? + mach_task_self () : oldtask, + 0, &newtask)) + goto out; + } + else + { + thread_array_t threads; + mach_msg_type_number_t nthreads, i; + + /* Terminate all the threads of the old task. */ + + if (e.error = task_threads (oldtask, &threads, &nthreads)) + goto out; + for (i = 0; i < nthreads; ++i) + { + 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)); + + /* Deallocate the entire virtual address space of the task. */ + + vm_deallocate (oldtask, + VM_MIN_ADDRESS, VM_MAX_ADDRESS - VM_MIN_ADDRESS); + + /* Deallocate and destroy the ports requested by the caller. + These are ports the task wants not to lose if the exec call + fails, but wants removed from the new program task. */ + + for (i = 0; i < ndeallocnames; ++i) + mach_port_deallocate (oldtask, deallocnames[i]); + + for (i = 0; i < ndestroynames; ++i) + mach_port_destroy (oldtask, destroynames[i]); + + newtask = oldtask; + } + + /* Load the file into the task. */ + load (newtask, &e); + if (e.error) + goto out; + + /* Release the conch for the file. */ + finish_mapping (&e); + + /* Further frobnicate the task after loading from the file. */ + postload (&e); + if (e.error) + goto out; + + /* Clean up. */ + finish (&e); + finished = 1; + + /* Create the initial thread. */ + if (e.error = thread_create (newtask, &thread)) + goto out; + + /* Store the data that we will give in response + to the RPC on the new task's bootstrap port. */ + + boot = alloc_recv (sizeof (*boot)); + if (boot == NULL) + { + e.error = ENOMEM; + goto out; + } + + if (nports <= INIT_PORT_BOOTSTRAP) + { + mach_port_t *new; + vm_allocate (mach_task_self (), + (vm_address_t *) &new, + INIT_PORT_MAX * sizeof (mach_port_t), 1); + memcpy (new, portarray, nports * sizeof (mach_port_t)); + bzero (&new[nports], (INIT_PORT_MAX - nports) * sizeof (mach_port_t)); + } + if (portarray[INIT_PORT_BOOTSTRAP] == MACH_PORT_NULL && + oldtask != MACH_PORT_NULL) + task_get_bootstrap_port (oldtask, &portarray[INIT_PORT_BOOTSTRAP]); + + if (e.error = mach_port_insert_right (mach_task_self (), + (mach_port_t) boot, + (mach_port_t) boot, + MACH_MSG_TYPE_MAKE_SEND)) + goto out; + if (e.error = task_set_bootstrap_port (newtask, (mach_port_t) boot)) + goto out; + mach_port_deallocate (mach_task_self (), (mach_port_t) boot); + + if (e.error = servercopy ((void **) &argv, argvlen, argv_copy)) + goto bootout; + boot->argv = argv; + boot->argvlen = argvlen; + if (e.error = servercopy ((void **) &envp, envplen, envp_copy)) + goto bootout; + boot->envp = envp; + boot->envplen = envplen; + if (e.error = servercopy ((void **) &dtable, + dtablesize * sizeof (mach_port_t), + dtable_copy)) + goto bootout; + boot->dtable = dtable; + boot->dtablesize = dtablesize; + if (e.error = servercopy ((void **) &portarray, + nports * sizeof (mach_port_t), + portarray_copy)) + goto bootout; + boot->portarray = portarray; + boot->nports = nports; + if (e.error = servercopy ((void **) &intarray, + nints * sizeof (mach_port_t), + intarray_copy)) + goto bootout; + boot->intarray = intarray; + boot->nints = nints; + boot->flags = flags; + + { + mach_port_t unused; + mach_port_request_notification (mach_task_self (), + (mach_port_t) boot, + MACH_NOTIFY_NO_SENDERS, 0, + (mach_port_t) boot, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &unused); + } + + if (flags & EXEC_SECURE) + { +#ifdef notyet + mach_port_t new; + if (e.error = __USEPORT (PROC, proc_task2proc (port, newtask, &new))) + goto bootout; + + if (nports < INIT_PORT_MAX) + { + /* Allocate a new vector that is big enough. */ + vm_allocate (mach_task_self (), + (vm_address_t *) &boot->portarray, + INIT_PORT_MAX * sizeof (mach_port_t), + 1); + memcpy (boot->portarray, portarray, + nports * sizeof (mach_port_t)); + vm_deallocate (mach_task_self (), (vm_address_t) portarray, + nports * sizeof (mach_port_t)); + nports = INIT_PORT_MAX; + } + + mach_port_deallocate (mach_task_self (), + boot->portarray[INIT_PORT_PROC]); + boot->portarray[INIT_PORT_PROC] = new; + mach_port_deallocate (mach_task_self (), + boot->portarray[INIT_PORT_CRDIR]); + boot->portarray[INIT_PORT_CRDIR] + = std_ports ? std_ports[INIT_PORT_CRDIR] : MACH_PORT_NULL; + mach_port_deallocate (mach_task_self (), + boot->portarray[INIT_PORT_CWDIR]); + boot->portarray[INIT_PORT_CWDIR] + = std_ports ? std_ports[INIT_PORT_CWDIR] : MACH_PORT_NULL; + + if (nints < INIT_INT_MAX) + { + /* Allocate a new vector that is big enough. */ + vm_allocate (mach_task_self (), + (vm_address_t *) &boot->intarray, + INIT_INT_MAX * sizeof (int), + 1); + memcpy (boot->intarray, intarray, nints * sizeof (int)); + vm_deallocate (mach_task_self (), (vm_address_t) intarray, + nints * sizeof (int)); + nints = INIT_INT_MAX; + } + + boot->intarray[INIT_UMASK] + = std_ints ? std_ints[INIT_UMASK] : CMASK; +#else + abort (); +#endif + } + + if (nports > INIT_PORT_PROC) + proc_mark_exec (boot->portarray[INIT_PORT_PROC]); + + /* Start up the initial thread at the entry point. */ + boot->stack_base = 0, boot->stack_size = 0; /* Don't care about values. */ + if (e.error = mach_setup_thread (newtask, thread, (void *) e.entry, + &boot->stack_base, &boot->stack_size)) + goto bootout; + + if (oldtask != newtask && oldtask != MACH_PORT_NULL) + { +#if 0 + /* The program is on its way. The old task can be nuked. */ + process_t proc; + /* Use the canonical proc server if secure, or there is none other. + When not secure, it is nice to let processes associate with + whatever proc server turns them on, regardless of which exec + itself is using. */ + process_t psrv = ((nports > INIT_PORT_PROC && !(flags & EXEC_SECURE)) ? + boot->portarray[INIT_PORT_PROC] : procserver); + /* XXX there is a race here for SIGKILLing the process. */ + if (! proc_task2proc (procserver, oldtask, &proc)) + { + /* XXX check for errors?? */ + proc_reassign (proc, newtask); + mach_port_deallocate (mach_task_self (), proc); + } +#endif + e.error = EINVAL; + goto bootout; + } + + newtask = MACH_PORT_NULL; + + bootout: + if (e.error) + { + mach_port_deallocate (mach_task_self (), (vm_address_t) boot); + if (boot->portarray != portarray) + vm_deallocate (mach_task_self (), + (vm_address_t) boot->portarray, + boot->nports * sizeof (mach_port_t)); + free (boot); + } + + out: + if (newtask != MACH_PORT_NULL) + { + task_terminate (newtask); + mach_port_deallocate (mach_task_self (), newtask); + } + if (!finished) + finish (&e); + + task_resume (oldtask); + thread_resume (thread); + + if (thread != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), thread); + + mach_port_deallocate (mach_task_self (), oldtask); + + mach_port_move_member (mach_task_self (), + (mach_port_t) boot, request_portset); + + return e.error; +} + +kern_return_t +S_exec_exec (mach_port_t execserver, + file_t file, + task_t oldtask, + int flags, + char *argv, u_int argvlen, boolean_t argv_copy, + char *envp, u_int envplen, boolean_t envp_copy, + mach_port_t *dtable, u_int dtablesize, boolean_t dtable_copy, + mach_port_t *portarray, u_int nports, boolean_t portarray_copy, + int *intarray, u_int nints, boolean_t intarray_copy, + mach_port_t *deallocnames, u_int ndeallocnames, + mach_port_t *destroynames, u_int ndestroynames) +{ + if (!(flags & EXEC_SECURE)) + { + const char envar[] = "\0EXECSERVERS="; + char *p = NULL; + if (envplen >= sizeof (envar) && + !memcmp (&envar[1], envp, sizeof (envar) - 2)) + p = envp - 1; + else + p = memmem (envp, envplen, envar, sizeof (envar) - 1); + if (p != NULL) + { + size_t len; + char *list; + int tried = 0; + p += sizeof (envar) - 1; + len = strlen (p) + 1; + list = alloca (len); + memcpy (list, p, len); + while (p = strsep (&list, ":")) + { + file_t server; + if (!hurd_path_lookup (portarray[INIT_PORT_CRDIR], + portarray[INIT_PORT_CWDIR], + p, 0, 0, &server)) + { + error_t err = (server == execserver ? + do_exec (server, file, oldtask, 0, + argv, argvlen, argv_copy, + envp, envplen, envp_copy, + dtable, dtablesize, dtable_copy, + portarray, nports, portarray_copy, + intarray, nints, intarray_copy, + deallocnames, ndeallocnames, + destroynames, ndestroynames) : + exec_exec (server, + file, MACH_MSG_TYPE_MOVE_SEND, + oldtask, 0, + argv, argvlen, + envp, envplen, + dtable, MACH_MSG_TYPE_MOVE_SEND, + dtablesize, + portarray, MACH_MSG_TYPE_MOVE_SEND, + nports, + intarray, nints, + deallocnames, ndeallocnames, + destroynames, ndestroynames)); + mach_port_deallocate (mach_task_self (), server); + if (err != ENOEXEC) + return err; + tried = 1; + } + } + if (tried) + /* At least one exec server got a crack at it and gave up. */ + return ENOEXEC; + } + } + + /* There were no user-specified exec servers, + or none of them could be found. */ + + return do_exec (execserver, file, oldtask, 0, + argv, argvlen, argv_copy, + envp, envplen, envp_copy, + dtable, dtablesize, dtable_copy, + portarray, nports, portarray_copy, + intarray, nints, intarray_copy, + deallocnames, ndeallocnames, + destroynames, ndestroynames); +} + +kern_return_t +S_exec_setexecdata (mach_port_t me, + mach_port_t *ports, u_int nports, int ports_copy, + int *ints, u_int nints, int ints_copy) +{ + error_t err; + + /* XXX needs authentication */ + + if (nports < INIT_PORT_MAX || nints < INIT_INT_MAX) + return EINVAL; + + if (std_ports) + vm_deallocate (mach_task_self (), (vm_address_t)std_ports, + std_nports * sizeof (mach_port_t)); + if (err = servercopy ((void **) &ports, nports * sizeof (mach_port_t), + ports_copy)) + return err; + + std_ports = ports; + std_nports = nports; + + if (std_ints) + vm_deallocate (mach_task_self (), (vm_address_t)std_ints, + std_nints * sizeof (int)); + if (err = servercopy ((void **) &ints, nints * sizeof (int), ints_copy)) + return err; + + std_ints = ints; + std_nints = nints; + + return 0; +} + +/* fsys server. */ + +kern_return_t +S_fsys_getroot (fsys_t fsys, + uid_t *uids, u_int nuids, + gid_t *gids, u_int ngids, + int flags, + retry_type *retry, + char *retry_name, + file_t *rootfile, + mach_msg_type_name_t *rootfilePoly) +{ + /* XXX eventually this should return a user-specific port which has an + associated access-restricted realnode port which file ops get + forwarded to. */ + + *rootfile = execserver; + *rootfilePoly = MACH_MSG_TYPE_MAKE_SEND; + + *retry = FS_RETRY_NONE; + *retry_name = '\0'; + return 0; +} + +kern_return_t +S_fsys_goaway (fsys_t fsys, int flags) +{ + if (!(flags & FSYS_GOAWAY_FORCE)) + { + mach_port_t *serving; + mach_msg_type_number_t nserving, i; + mach_port_get_set_status (mach_task_self (), request_portset, + &serving, &nserving); + for (i = 0; i < nserving; ++i) + mach_port_deallocate (mach_task_self (), serving[i]); + if (nserving > 2) + /* Not just fsys and execserver. + We are also waiting on some bootstrap ports. */ + return EBUSY; + } + mach_port_mod_refs (mach_task_self (), request_portset, + MACH_PORT_TYPE_RECEIVE, -1); + return 0; +} + +kern_return_t +S_fsys_startup (mach_port_t bootstrap, + fsys_t control, + mach_port_t *node, + mach_msg_type_name_t *realnodePoly, + mach_port_t *dotdot, + mach_msg_type_name_t *dotdotPoly) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_fsys_getfile (fsys_t fsys, + uid_t *uids, + u_int nuids, + uid_t *gids, + u_int ngids, + char *filehandle, + u_int filehandlelen, + mach_port_t *file, + mach_msg_type_name_t *filetype) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_fsys_getpriv (fsys_t fsys, + mach_port_t *hp, + mach_port_t *dm, + mach_port_t *tk) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_fsys_init (fsys_t fsys, + mach_port_t reply, mach_msg_type_name_t replytype, + mach_port_t ps, + mach_port_t ah) +{ + return EOPNOTSUPP; +} + + + + +/* RPC sent on the bootstrap port. */ + +kern_return_t +S_exec_startup (mach_port_t port, + vm_address_t *stack_base, vm_size_t *stack_size, + int *flags, + char **argvp, u_int *argvlen, + char **envpp, u_int *envplen, + mach_port_t **dtable, mach_msg_type_name_t *dtablepoly, + u_int *dtablesize, + mach_port_t **portarray, mach_msg_type_name_t *portpoly, + u_int *nports, + int **intarray, u_int *nints) +{ + struct bootinfo *boot = (struct bootinfo *)port; + if ((mach_port_t) boot == execserver || (mach_port_t) boot == fsys) + return EOPNOTSUPP; + + *stack_base = boot->stack_base; + *stack_size = boot->stack_size; + + *argvp = boot->argv; + *argvlen = boot->argvlen; + + *envpp = boot->envp; + *envplen = boot->envplen; + + *dtable = boot->dtable; + *dtablesize = boot->dtablesize; + *dtablepoly = MACH_MSG_TYPE_MOVE_SEND; + + *intarray = boot->intarray; + *nints = boot->nints; + + *portarray = boot->portarray; + *nports = boot->nports; + *portpoly = MACH_MSG_TYPE_MOVE_SEND; + + *flags = boot->flags; + + mach_port_move_member (mach_task_self (), (mach_port_t) boot, + MACH_PORT_NULL); /* XXX what is this XXX here for? */ + + mach_port_mod_refs (mach_task_self (), (mach_port_t) boot, + MACH_PORT_TYPE_RECEIVE, -1); + free (boot); + + return 0; +} + +/* Notice when a receive right has no senders. Either this is the + bootstrap port of a stillborn task, or it is the execserver port itself. */ + +kern_return_t +do_mach_notify_no_senders (mach_port_t port, mach_port_mscount_t mscount) +{ + if (port != execserver && port != fsys) + { + /* Free the resources we were saving to give the task + which can no longer ask for them. */ + + struct bootinfo *boot = (struct bootinfo *) port; + size_t i; + + vm_deallocate (mach_task_self (), + (vm_address_t) boot->argv, boot->argvlen); + vm_deallocate (mach_task_self (), + (vm_address_t) boot->envp, boot->envplen); + + for (i = 0; i < boot->dtablesize; ++i) + mach_port_deallocate (mach_task_self (), boot->dtable[i]); + for (i = 0; i < boot->nports; ++i) + mach_port_deallocate (mach_task_self (), boot->portarray[i]); + vm_deallocate (mach_task_self (), + (vm_address_t) boot->portarray, + boot->nports * sizeof (mach_port_t)); + vm_deallocate (mach_task_self (), + (vm_address_t) boot->intarray, + boot->nints * sizeof (int)); + + free (boot); + } + + /* Deallocate the request port. */ + mach_port_mod_refs (mach_task_self (), port, MACH_PORT_TYPE_RECEIVE, -1); + + mach_port_mod_refs (mach_task_self (), request_portset, + MACH_PORT_TYPE_RECEIVE, -1); + + return KERN_SUCCESS; +} + +/* Sent by the bootstrap filesystem after the other essential + servers have been started up. */ + +kern_return_t +S_exec_init (mach_port_t server, auth_t auth, process_t proc) +{ + mach_port_t host_priv, dev_master, startup; + error_t err; + + if (_hurd_ports[INIT_PORT_PROC].port != MACH_PORT_NULL) + /* Can only be done once. */ + return EPERM; + + _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], proc); + _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], auth); + + /* Do initial setup with the proc server. */ + _hurd_proc_init (save_argv); + + err = get_privileged_ports (&host_priv, &dev_master); + if (!err) + { + proc_register_version (proc, host_priv, "exec", HURD_RELEASE, + exec_version); + mach_port_deallocate (mach_task_self (), dev_master); + err = proc_getmsgport (proc, 1, &startup); + if (!err) + { + startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL, + "exec", host_priv); + mach_port_deallocate (mach_task_self (), startup); + } + mach_port_deallocate (mach_task_self (), host_priv); + } + +#if 0 + /* Have the proc server notify us when the canonical ints and ports change. + The notification comes as a normal RPC on the message port, which + the C library's signal thread handles. */ + __USEPORT (PROC, proc_execdata_notify (port, execserver)); +#endif + + return 0; +} + + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t boot, dotdot; + + /* XXX */ + stdout = mach_open_devstream (getdport (1), "w"); + stderr = mach_open_devstream (getdport (2), "w"); + /* End XXX */ + + save_argv = argv; + +#ifdef BFD + /* Put the Mach kernel's idea of what flavor of machine this is into the + fake BFD against which architecture compatibility checks are made. */ + err = bfd_mach_host_arch_mach (mach_host_self (), + &host_bfd.obj_machine, + &host_bfd.obj_arch); +#else + err = aout_mach_host_machine (mach_host_self (), (int *)&host_machine); +#endif + if (err) + return err; + + mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &fsys); + mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &execserver); + + task_get_bootstrap_port (mach_task_self (), &boot); + fsys_startup (boot, fsys, MACH_MSG_TYPE_MAKE_SEND, &realnode, &dotdot); + mach_port_deallocate (mach_task_self (), dotdot); /* Don't care. */ + + mach_port_allocate (mach_task_self (), + MACH_PORT_RIGHT_PORT_SET, &request_portset); + mach_port_move_member (mach_task_self (), fsys, request_portset); + mach_port_move_member (mach_task_self (), execserver, request_portset); + + while (1) + { + err = mach_msg_server (request_server, vm_page_size, request_portset); + fprintf (stderr, "%s: mach_msg_server: %s\n", + (argv && argv[0]) ? argv[0] : "exec server", + strerror (err)); + } +} + +/* Nops */ +kern_return_t +do_mach_notify_port_deleted (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_msg_accepted (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_port_destroyed (mach_port_t notify, + mach_port_t rights) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_send_once (mach_port_t notify) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_dead_name (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + |