diff options
Diffstat (limited to 'exec')
-rw-r--r-- | exec/exec.c | 385 |
1 files changed, 300 insertions, 85 deletions
diff --git a/exec/exec.c b/exec/exec.c index 531d41e0..d8970036 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -81,11 +81,13 @@ struct execdata bfd *bfd; #else file_t file; - struct exec *header; + struct exec *header, headbuf; #endif memory_object_t filemap, cntlmap; struct shared_io *cntl; + char *file_data; /* File data if already copied in core. */ off_t file_size; + size_t optimal_block; /* Optimal size for io_read from file. */ /* Set by caller of load. */ task_t task; @@ -140,6 +142,8 @@ b2he (error_t deflt) #define b2he() a2he (errno) #endif +static void check_gzip (struct execdata *); + #ifdef BFD /* Check a section, updating the `locations' vector [BFD]. */ @@ -242,24 +246,85 @@ load_section (enum section section, struct execdata *u) { vm_address_t mapstart = round_page (addr); + /* Allocate space in the task and write CONTENTS into it. */ + void write_to_task (vm_address_t mapstart, vm_size_t size, + vm_prot_t vm_prot, vm_address_t contents) + { + vm_size_t off = size % vm_page_size; + /* Allocate with vm_map to set max protections. */ + u->error = vm_map (u->task, + &mapstart, size, 0, 0, + MACH_PORT_NULL, 0, 1, + vm_prot|VM_PROT_WRITE, + VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, + VM_INHERIT_COPY); + if (! u->error && size >= vm_page_size) + u->error = vm_write (u->task, mapstart, contents, size - off); + if (! u->error && off != 0) + { + vm_address_t page = 0; + u->error = vm_allocate (mach_task_self (), + &page, vm_page_size, 1); + if (! u->error) + { + memcpy ((void *) page, + (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); + } + } + /* Reset the current protections to the desired state. */ + if (! u->error && (vm_prot & VM_PROT_WRITE) == 0) + u->error = vm_protect (u->task, mapstart, size, 0, vm_prot); + } + 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 +#define SECTION_IN_MEMORY_P (sec->flags & SEC_IN_MEMORY) +#define SECTION_CONTENTS sec->contents +#else +#define SECTION_IN_MEMORY_P (u->file_data != NULL) +#define SECTION_CONTENTS (u->file_data + filepos) #endif + if (SECTION_IN_MEMORY_P) + /* Data is already in memory; write it into the task. */ + write_to_task (mapstart, secsize - (mapstart - addr), vm_prot, + (vm_address_t) SECTION_CONTENTS + + (mapstart - addr)); + else if (u->filemap != MACH_PORT_NULL) + /* Map the data into the task directly from the file. */ 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); + else + { + /* Cannot map the data. Read it into a buffer and vm_write + it into the task. */ + void *buf; + const vm_size_t size = secsize - (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); + } + } if (u->error) return; } @@ -293,50 +358,14 @@ load_section (enum section section, struct execdata *u) readaddr = (void *) (ourpage + (addr - overlap_page)); readsize = size - (addr - overlap_page); -#ifdef BFD - if (sec->flags & SEC_IN_MEMORY) - bcopy (sec->contents, readaddr, readsize); + if (SECTION_IN_MEMORY_P) + bcopy (SECTION_CONTENTS, readaddr, readsize); else -#endif -#if 1 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) { @@ -481,38 +510,69 @@ 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 (), (vm_address_t) f->__buffer, - f->__bufsize); - - if (f->__target > size) + 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)) + if (e->filemap == MACH_PORT_NULL) { - errno = EIO; - f->__error = 1; - return EOF; - } - f->__bufsize = vm_page_size; - f->__offset = f->__target; + mach_msg_type_number_t nread = f->__bufsize; + char *buffer = f->__buffer; + if (e->error = io_read (e->file, &buffer, &nread, + f->__target, e->optimal_block)) + { + errno = e->error; + f->__error = 1; + return EOF; + } + if (buffer != f->__buffer) + { + /* The data was returned out of line. Discard the old buffer. */ + vm_deallocate (mach_task_self (), (vm_address_t) f->__buffer, + f->__bufsize); + f->__buffer = buffer; + f->__bufsize = round_page (nread); + } - if (f->__target + f->__bufsize > size) - f->__get_limit = f->__buffer + (size - f->__target); + f->__get_limit = f->__buffer + nread; + } else - f->__get_limit = f->__buffer + f->__bufsize; + { + if (f->__buffer != NULL) + vm_deallocate (mach_task_self (), (vm_address_t) f->__buffer, + f->__bufsize); + 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; + + if (e->cntl) + e->cntl->accessed = 1; - if (e->cntl) - e->cntl->accessed = 1; + if (f->__target + f->__bufsize > size) + f->__get_limit = f->__buffer + (size - f->__target); + else + f->__get_limit = f->__buffer + f->__bufsize; + } + f->__offset = f->__target; f->__bufp = f->__buffer; + + if (f->__get_limit == f->__buffer) + { + f->__eof = 1; + return EOF; + } + return (unsigned char) *f->__bufp++; } @@ -529,17 +589,16 @@ close_exec_stream (void *cookie) } -/* Prepare to load FILE. - On successful return, the caller must allocate the - E->locations vector, and map check_section over the BFD. */ +/* Prepare to check and load FILE. */ static inline void -check (file_t file, struct execdata *e) +prepare (file_t file, struct execdata *e) { e->file = file; #ifdef BFD e->bfd = NULL; #endif + e->file_data = NULL; e->cntl = NULL; e->filemap = MACH_PORT_NULL; e->cntlmap = MACH_PORT_NULL; @@ -548,7 +607,7 @@ check (file_t file, struct execdata *e) memory_object_t rd, wr; if (e->error = io_map (file, &rd, &wr)) return; - if (wr) + if (wr != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), wr); if (rd == MACH_PORT_NULL) { @@ -565,6 +624,7 @@ check (file_t file, struct execdata *e) if (e->error = io_stat (file, &st)) return; e->file_size = st.st_size; + e->optimal_block = st.st_blksize; } else e->error = vm_map (mach_task_self (), (vm_address_t *) &e->cntl, @@ -612,15 +672,26 @@ check (file_t file, struct execdata *e) e->stream.__room_funcs.__input = input_room; e->stream.__io_funcs.close = close_exec_stream; e->stream.__cookie = e; + e->stream.__seen = 1; -#ifdef BFD +#ifdef BFD e->bfd = bfd_openstream (&e->stream); if (e->bfd == NULL) { e->error = b2he (ENOEXEC); return; } +#endif +} + +/* Check the magic number, etc. of the file. + On successful return, the caller must allocate the + E->locations vector, and map check_section over the BFD. */ +static void +check (struct execdata *e) +{ +#ifdef BFD bfd_error = no_error; if (!bfd_check_format (e->bfd, bfd_object)) { @@ -643,11 +714,25 @@ check (file_t file, struct execdata *e) 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 (e->file_data) + /* Data already in core. Just use it. */ + e->header = (void *) e->file_data; + else if (e->filemap == MACH_PORT_NULL) + { + /* Cannot map the file. Read the header into a buffer. */ + if (fread (&e->headbuf, sizeof e->headbuf, 1, &e->stream) != 1) + { + e->error = errno; + return; + } + e->header = &e->headbuf; + } + else + 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; @@ -665,7 +750,7 @@ check (file_t file, struct execdata *e) /* Load the file. */ -static inline void +static void load (task_t usertask, struct execdata *e) { if (e->error) @@ -682,7 +767,7 @@ load (task_t usertask, struct execdata *e) } /* Do post-loading processing on the task. */ -static inline void +static void postload (struct execdata *e) { if (e->error) @@ -699,7 +784,7 @@ postload (struct execdata *e) /* Release the conch and clean up mapping the file and control page. */ -static inline void +static void finish_mapping (struct execdata *e) { if (e->cntl != NULL) @@ -716,29 +801,146 @@ finish_mapping (struct execdata *e) spin_unlock (&e->cntl->lock); } vm_deallocate (mach_task_self (), (vm_address_t) e->cntl, vm_page_size); + e->cntl = NULL; } if (e->filemap != MACH_PORT_NULL) - mach_port_deallocate (mach_task_self (), e->filemap); + { + mach_port_deallocate (mach_task_self (), e->filemap); + e->filemap = MACH_PORT_NULL; + } if (e->cntlmap != MACH_PORT_NULL) - mach_port_deallocate (mach_task_self (), e->cntlmap); + { + mach_port_deallocate (mach_task_self (), e->cntlmap); + e->cntlmap = MACH_PORT_NULL; + } } /* Clean up after reading the file (need not be completed). */ -static inline void +static void finish (struct execdata *e) { finish_mapping (e); + fclose (&e->stream); #ifdef BFD if (e->bfd != NULL) (void) bfd_close (e->bfd); #else - if (e->header != NULL) + if (e->header != NULL && e->header != &e->headbuf && + e->header != (void *) e->file_data) vm_deallocate (mach_task_self (), (vm_address_t) e->header, sizeof (*e->header)); - mach_port_deallocate (mach_task_self (), e->file); + e->header = NULL; #endif + if (e->file != MACH_PORT_NULL) + { + mach_port_deallocate (mach_task_self (), e->file); + e->file = MACH_PORT_NULL; + } } +/* Check the file for being a gzip'd image. Return with ENOEXEC means not + a valid gzip 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_gzip (struct execdata *earg) +{ + struct execdata *e = earg; + /* Entry points to unzip engine. */ + int get_method (int); + void unzip (int, int); + extern long int bytes_out; + /* 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; + int zipread (char *buf, size_t maxread) + { + return fread (buf, 1, maxread, &e->stream); + } + void zipwrite (const char *buf, size_t nwrite) + { + if (fwrite (buf, nwrite, 1, zipout) != 1) + longjmp (ziperr, 1); + } + void ziprderr (void) + { + 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; + } + + if (get_method (0) != 0) + { + /* Not a happy gzip file. */ + e->error = ENOEXEC; + return; + } + + /* Matched gzip magic number. Ready to unzip. + Set up the output stream and let 'er rip. */ + + zipout = open_memstream (&zipdata, &zipdatasz); + if (! zipout) + { + e->error = errno; + return; + } + + /* Call the gunzip engine. */ + bytes_out = 0; + unzip (17, 23); /* Arguments ignored. */ + + /* The output is complete. Clean up the stream and store its resultant + buffer and size in the execdata as the file contents. */ + fclose (zipout); + e->file_data = zipdata; + e->file_size = zipdatasz; + + /* Clean up the old exec file stream's state. */ + finish (e); + + /* 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; + + e->error = 0; +} + + static int request_server (mach_msg_header_t *inp, mach_msg_header_t *outp) @@ -857,8 +1059,21 @@ do_exec (mach_port_t execserver, if ((!std_ports || !std_ints) && (flags & (EXEC_SECURE|EXEC_DEFAULTS))) return EIEIO; + /* Prepare E to read the file. */ + prepare (file, &e); + /* Check the file for validity first. */ - check (file, &e); + check (&e); + if (e.error == ENOEXEC) + { + /* See if it is a compressed image. */ + check_gzip (&e); + 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); + } #if 0 if (e.error == ENOEXEC) /* Check for a #! executable file. */ |