diff options
Diffstat (limited to 'exec/exec.c')
-rw-r--r-- | exec/exec.c | 752 |
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 *) ©, 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; |