. make exec() callers (i.e. vfs and rs) determine the memory layout by explicitly reserving regions using mmap() calls on behalf of the exec()ing process, i.e. handling all of the exec logic, thereby eliminating all special exec() knowledge from VM. . the new procedure is: clear the exec()ing process first, then call third-party mmap()s to reserve memory, then copy the executable file section contents in, all using callbacks tailored to the caller's way of starting an executable . i.e. no more explicit EXEC_NEWMEM-style calls in PM or VM as with rigid 2-section arguments . this naturally allows generalizing exec() by simply loading all ELF sections . drop/merge of lots of duplicate exec() code into libexec . not copying the code sections to vfs and into the executable again is a measurable performance improvement (about 3.3% faster for 'make' in src/servers/)
202 lines
5.2 KiB
C
202 lines
5.2 KiB
C
#include "inc.h"
|
|
#include <a.out.h>
|
|
#include <assert.h>
|
|
#include <libexec.h>
|
|
#include <machine/vmparam.h>
|
|
|
|
static int do_exec(int proc_e, char *exec, size_t exec_len, char *progname,
|
|
char *frame, int frame_len);
|
|
static int exec_restart(int proc_e, int result, vir_bytes pc);
|
|
static int read_seg(struct exec_info *execi, off_t off,
|
|
off_t seg_addr, size_t seg_bytes);
|
|
|
|
/* Array of loaders for different object formats */
|
|
static struct exec_loaders {
|
|
libexec_exec_loadfunc_t load_object;
|
|
} const exec_loaders[] = {
|
|
{ libexec_load_elf },
|
|
{ NULL }
|
|
};
|
|
|
|
int srv_execve(int proc_e, char *exec, size_t exec_len, char **argv,
|
|
char **UNUSED(Xenvp))
|
|
{
|
|
char * const *ap;
|
|
char * const *ep;
|
|
char *frame;
|
|
char **vp;
|
|
char *sp, *progname;
|
|
size_t argc;
|
|
size_t frame_size;
|
|
size_t string_off;
|
|
size_t n;
|
|
int ov;
|
|
int r;
|
|
|
|
/* Assumptions: size_t and char *, it's all the same thing. */
|
|
|
|
/* Create a stack image that only needs to be patched up slightly
|
|
* by the kernel to be used for the process to be executed.
|
|
*/
|
|
|
|
ov= 0; /* No overflow yet. */
|
|
frame_size= 0; /* Size of the new initial stack. */
|
|
string_off= 0; /* Offset to start of the strings. */
|
|
argc= 0; /* Argument count. */
|
|
|
|
for (ap= argv; *ap != NULL; ap++) {
|
|
n = sizeof(*ap) + strlen(*ap) + 1;
|
|
frame_size+= n;
|
|
if (frame_size < n) ov= 1;
|
|
string_off+= sizeof(*ap);
|
|
argc++;
|
|
}
|
|
|
|
/* Add an argument count and two terminating nulls. */
|
|
frame_size+= sizeof(argc) + sizeof(*ap) + sizeof(*ep);
|
|
string_off+= sizeof(argc) + sizeof(*ap) + sizeof(*ep);
|
|
|
|
/* Align. */
|
|
frame_size= (frame_size + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
|
|
|
|
/* The party is off if there is an overflow. */
|
|
if (ov || frame_size < 3 * sizeof(char *)) {
|
|
errno= E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
/* Allocate space for the stack frame. */
|
|
frame = (char *) malloc(frame_size);
|
|
if (!frame) {
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
/* Set arg count, init pointers to vector and string tables. */
|
|
* (size_t *) frame = argc;
|
|
vp = (char **) (frame + sizeof(argc));
|
|
sp = frame + string_off;
|
|
|
|
/* Load the argument vector and strings. */
|
|
for (ap= argv; *ap != NULL; ap++) {
|
|
*vp++= (char *) (sp - frame);
|
|
n= strlen(*ap) + 1;
|
|
memcpy(sp, *ap, n);
|
|
sp+= n;
|
|
}
|
|
*vp++= NULL;
|
|
|
|
#if 0
|
|
/* Load the environment vector and strings. */
|
|
for (ep= envp; *ep != NULL; ep++) {
|
|
*vp++= (char *) (sp - frame);
|
|
n= strlen(*ep) + 1;
|
|
memcpy(sp, *ep, n);
|
|
sp+= n;
|
|
}
|
|
#endif
|
|
*vp++= NULL;
|
|
|
|
/* Padding. */
|
|
while (sp < frame + frame_size) *sp++= 0;
|
|
|
|
(progname=strrchr(argv[0], '/')) ? progname++ : (progname=argv[0]);
|
|
r = do_exec(proc_e, exec, exec_len, progname, frame, frame_size);
|
|
|
|
/* Return the memory used for the frame and exit. */
|
|
free(frame);
|
|
return r;
|
|
}
|
|
|
|
|
|
static int do_exec(int proc_e, char *exec, size_t exec_len, char *progname,
|
|
char *frame, int frame_len)
|
|
{
|
|
int r;
|
|
vir_bytes vsp;
|
|
struct exec_info execi;
|
|
int i;
|
|
|
|
memset(&execi, 0, sizeof(execi));
|
|
|
|
execi.stack_size = DEFAULT_STACK_LIMIT;
|
|
execi.proc_e = proc_e;
|
|
execi.hdr = exec;
|
|
execi.hdr_len = exec_len;
|
|
strncpy(execi.progname, progname, PROC_NAME_LEN-1);
|
|
execi.progname[PROC_NAME_LEN-1] = '\0';
|
|
execi.frame_len = frame_len;
|
|
execi.load = read_seg;
|
|
|
|
for(i = 0; exec_loaders[i].load_object != NULL; i++) {
|
|
r = (*exec_loaders[i].load_object)(&execi);
|
|
/* Loaded successfully, so no need to try other loaders */
|
|
if (r == OK) break;
|
|
}
|
|
|
|
/* No exec loader could load the object */
|
|
if (r != OK) {
|
|
printf("RS: do_exec: loading error %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Patch up stack and copy it from RS to new core image. */
|
|
vsp = execi.stack_high;
|
|
vsp -= frame_len;
|
|
libexec_patch_ptr(frame, vsp);
|
|
r = sys_datacopy(SELF, (vir_bytes) frame,
|
|
proc_e, (vir_bytes) vsp, (phys_bytes)frame_len);
|
|
if (r != OK) {
|
|
printf("do_exec: copying out new stack failed: %d\n", r);
|
|
exec_restart(proc_e, r, execi.pc);
|
|
return r;
|
|
}
|
|
|
|
return exec_restart(proc_e, OK, execi.pc);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* exec_restart *
|
|
*===========================================================================*/
|
|
static int exec_restart(int proc_e, int result, vir_bytes pc)
|
|
{
|
|
int r;
|
|
message m;
|
|
|
|
m.m_type= EXEC_RESTART;
|
|
m.EXC_RS_PROC= proc_e;
|
|
m.EXC_RS_RESULT= result;
|
|
m.EXC_RS_PC= (void*)pc;
|
|
r= sendrec(PM_PROC_NR, &m);
|
|
if (r != OK)
|
|
return r;
|
|
return m.m_type;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* read_seg *
|
|
*===========================================================================*/
|
|
static int read_seg(
|
|
struct exec_info *execi, /* various data needed for exec */
|
|
off_t off, /* offset in file */
|
|
off_t seg_addr, /* address to load segment */
|
|
size_t seg_bytes /* how much is to be transferred? */
|
|
)
|
|
{
|
|
/*
|
|
* The byte count on read is usually smaller than the segment count, because
|
|
* a segment is padded out to a click multiple, and the data segment is only
|
|
* partially initialized.
|
|
*/
|
|
|
|
int r;
|
|
|
|
if (off+seg_bytes > execi->hdr_len) return ENOEXEC;
|
|
if((r= sys_vircopy(SELF, D, ((vir_bytes)execi->hdr)+off,
|
|
execi->proc_e, D, seg_addr, seg_bytes)) != OK) {
|
|
printf("RS: exec read_seg: copy 0x%lx bytes into %d at 0x%lx failed: %d\n",
|
|
seg_bytes, execi->proc_e, seg_addr, r);
|
|
}
|
|
return r;
|
|
}
|