#include #include #include #include #include "dat.h" #include "fns.h" #include "linux.h" typedef struct Elfhdr Elfhdr; typedef struct Proghdr Proghdr; typedef struct ElfEx ElfEx; struct Elfhdr { uchar ident[16]; ushort type; ushort machine; ulong version; ulong entry; ulong phoff; ulong shoff; ulong flags; ushort ehsize; ushort phentsize; ushort phnum; ushort shentsize; ushort shnum; ushort shstrndx; }; struct Proghdr { ulong type; ulong offset; ulong vaddr; ulong paddr; ulong filesz; ulong memsz; ulong flags; ulong align; }; struct ElfEx { ulong ientry; ulong ibase; ulong entry; ulong base; ulong phdr; ulong phnum; ulong phent; }; static void padzero(ulong addr) { ulong n; if(n = (pagealign(addr) - addr)) memset((void*)addr, 0, n); } enum { /* file types */ ElfTNone = 0, ElfTReloc = 1, ElfTExec = 2, ElfTShared = 3, ElfTCore = 4, ElfTMax = 5, /* machine architectures */ ElfMNone = 0, ElfM32 = 1, ElfMSparc = 2, ElfM386 = 3, ElfM68 = 4, ElfM88 = 5, ElfM860 = 7, ElfMMips = 8, ElfMMax = 9, /* program segment types */ ElfPNull = 0, ElfPLoad = 1, ElfPDynamic = 2, ElfPInterp = 3, ElfPNote = 4, ElfPShlib = 5, ElfPPhdr = 6, ElfPMax = 7, /* program segment flags */ ElfPFX = 1, ElfPFW = 2, ElfPFR = 4, }; static int loadelf(char *file, ElfEx *ex, int depth) { int fd; int i, l; int mapprot; int mapflags; ulong mapbase; ulong loadaddr; ulong bss; Elfhdr hdr; Proghdr *phdr; char *interpreter; interpreter = nil; phdr = nil; if((fd = sys_open(file, O_RDONLY, 0)) < 0){ werrstr("cant open %s", file); goto errout; } if(sys_read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)){ werrstr("cant read elf header"); goto errout; } if(memcmp(hdr.ident, "\x7fELF", 4)!=0){ werrstr("no elf magic"); goto errout; } l = hdr.phnum * hdr.phentsize; phdr = kmalloc(l); sys_lseek(fd, hdr.phoff, 0); if(sys_read(fd, phdr, l) != l){ werrstr("cant read program headers"); goto errout; } loadaddr = 0; mapbase = 0; mapflags = MAP_PRIVATE; if(hdr.type != ElfTShared) mapflags |= MAP_FIXED; trace("loadelf(): phnum=%d", hdr.phnum); bss = 0; for(i=0; itype == ElfPInterp){ if(interpreter){ werrstr("multiple interpeter sections"); goto errout; } l = p->filesz; interpreter = kmalloc(l+1); sys_lseek(fd, p->offset, 0); if(sys_read(fd, interpreter, l)!=l){ werrstr("cant read interpreter section"); goto errout; } interpreter[l] = '\0'; } if(p->type == ElfPLoad){ ulong a; int diff; trace("loadelf(): phdr %d: vaddr=%lux memsz=%lux filesz=%lux offset=%lux flags=%lux", i, p->vaddr, p->memsz, p->filesz, p->offset, p->flags); mapprot = 0; if(p->flags & ElfPFR) mapprot |= PROT_READ; if(p->flags & ElfPFW) mapprot |= PROT_WRITE; if(p->flags & ElfPFX) mapprot |= PROT_EXEC; if(hdr.entry >= p->vaddr && hdr.entry < p->vaddr + p->memsz) mapprot |= PROT_EXEC; diff = p->vaddr - (p->vaddr & ~(PAGESIZE-1)); /* have to call mapdata() before we do the first mmap */ if(loadaddr == 0 && depth == 0){ if(hdr.type == ElfTShared){ mapbase = pagealign((ulong)end + 0x4000000); mapflags |= MAP_FIXED; } mapdata((mapbase + p->vaddr) - diff); } a = sys_mmap( (mapbase + p->vaddr) - diff, p->filesz + diff, mapprot, mapflags, fd, (p->offset - diff)/PAGESIZE); if(((int)a < 0) && ((int)a > -EMAX)){ werrstr("mmap failed: %E", (int)a); goto errout; } if(loadaddr == 0) loadaddr = a; if(hdr.type == ElfTShared && mapbase == 0){ mapbase = a + diff; mapflags |= MAP_FIXED; } if(mapprot & PROT_WRITE) padzero(mapbase + p->vaddr + p->filesz); if(depth == 0) if(mapbase + p->vaddr + p->memsz > bss) bss = mapbase + p->vaddr + p->memsz; } else { trace("loadelf(): phdr %d: type=%lux", i, p->type); } } ex->base = loadaddr; ex->entry = hdr.entry + ((hdr.type == ElfTShared) ? loadaddr : 0); ex->phdr = loadaddr + hdr.phoff; ex->phent = hdr.phentsize; ex->phnum = hdr.phnum; if(depth == 0){ sys_brk(pagealign(bss)); current->codestart = loadaddr; current->codeend = bss; } if(interpreter){ ElfEx interpex; if(loadelf(interpreter, &interpex, depth+1) < 0){ werrstr("cant load interpreter: %r"); goto errout; } free(interpreter); ex->ientry = interpex.entry; ex->ibase = interpex.base; } else { ex->ientry = ex->entry; ex->ibase = 0; /* no interpreter */ } sys_close(fd); free(phdr); return 0; errout: if(fd >= 0) sys_close(fd); free(interpreter); free(phdr); return -1; } enum { AT_NULL, AT_IGNORE, AT_EXECFD, AT_PHDR, AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_BASE, AT_FLAGS, AT_ENTRY, AT_NOTELF, AT_UID, AT_EUID, AT_GID, AT_EGID, AT_PLATFORM, AT_HWCAP, AT_CLKTCK, AT_SECURE = 23, AT_SYSINFO = 32, AT_SYSINFO_EHDR = 33, }; static void* setupstack(ElfEx *ex, char *argv[], char *envp[]) { int envc; int argc; char **dargv; char **denv; ulong *stack; ulong *p; char *x; int i, n; /* * calculate the size we need on stack */ argc=0; while(argv && argv[argc]) argc++; envc=0; while(envp && envp[envc]) envc++; n = 0; n += sizeof(ulong); // argc n += (argc+1)*sizeof(char*); // argv + nil n += (envc+1)*sizeof(char*); // envp + nil n += 16*(2*sizeof(ulong)); // aux for(i=0; i -EMAX)){ werrstr("mapstack failed: %E", (int)stack); return nil; } stack = (ulong*)(((ulong)stack - n) & ~7); current->stackstart = (ulong)stack; p = stack; *p++ = argc; dargv = (char**)p; p += (argc + 1); denv = (char**)p; p += (envc + 1); #define AUXENT(k, v) {p[0]=k; p[1]=v; p+=2;} AUXENT(AT_PAGESZ, PAGESIZE); AUXENT(AT_CLKTCK, HZ); AUXENT(AT_PHDR, ex->phdr); AUXENT(AT_PHENT, ex->phent); AUXENT(AT_PHNUM, ex->phnum); AUXENT(AT_BASE, ex->ibase); AUXENT(AT_FLAGS, 0); AUXENT(AT_ENTRY, ex->entry); AUXENT(AT_UID, current->uid); AUXENT(AT_EUID, current->uid); AUXENT(AT_GID, current->gid); AUXENT(AT_EGID, current->gid); AUXENT(AT_NULL, 0); AUXENT(AT_NULL, 0); AUXENT(AT_NULL, 0); AUXENT(AT_NULL, 0); #undef AUXENT x = (char*)p; for(i=0; icomm); current->comm = buf; current->ncomm = p - buf; } static void clinote(struct Ureg *ureg) { jmp_buf jmp; ulong pc; ulong sp; ulong ax; pc = ureg->pc; sp = ureg->sp; ax = ureg->ax; if(!setjmp(jmp)) notejmp(ureg, jmp, 1); ureg->pc = pc; ureg->sp = sp; ureg->ax = ax; } struct kexecveargs { char *name; char **argv; char **envp; }; #pragma profile off static int kexecve(void *arg) { struct kexecveargs *args; Ufile *f; ElfEx ex; Ureg u; int r, n; char *b, *p, *e, *x, **a; void *stack; char *name, *exe; char **argv; char **envp; int phase; args = arg; name = args->name; argv = args->argv; envp = args->envp; phase = 0; n = 8192; b = kmalloc(n); p = b; e = b + n; again: if(r = sys_access(name, 05)){ if(r > 0) r = -EACCES; goto errout; } if((r = sys_open(name, O_RDONLY, 0)) < 0) goto errout; exe = "/dev/null"; if(f = fdgetfile(r)){ if(f->path != nil){ strncpy(p, f->path, e-p); p += strlen(exe = p)+1; } putfile(f); } n = sys_read(r, p, (e-p)-1); sys_close(r); r = -ENOEXEC; if(n < 4) goto errout; if(memcmp(p, "#!", 2) == 0){ p[n] = 0; r = -ENAMETOOLONG; if((x = strchr(p, '\n')) == nil) goto errout; *x = 0; a = (char**)&x[1]; n = (e - (char*)a) / sizeof(a[0]); if(n < 2) goto errout; n = getfields(&p[2], a, n, 1, "\t\r\n "); if(n < 1) goto errout; r = -E2BIG; if(&a[n+1] >= (char**)e) goto errout; a[n++] = name; if(argv != nil){ argv++; while(*argv){ if(&a[n+1] >= (char**)e) goto errout; a[n++] = *argv++; } } a[n++] = 0; p = (char*)&a[n]; if(e - p < 4) goto errout; argv = a; name = argv[0]; goto again; } if(memcmp(p, "\x7fELF", 4)!=0) goto errout; /* * the contents on envp[] or argv[] maybe stored in b[], stack or bss of the calling linux * process that is destroyed on free(b) and exitmem()... so we need to temporary * copy them. */ r = -ENOMEM; name = kstrdup(name); phase++; if(argv) argv = copystrings(argv); phase++; if(envp) envp = copystrings(envp); phase++; /* get out of the note before we destroy user stack */ if(current->innote){ clinote(current->ureg); current->innote = 0; } /* this is the point of no return! */ qlock(&proctab); zapthreads(); exitmem(); exitsignal(); initmem(); initsignal(); inittls(); qunlock(&proctab); closexfds(); setcomm(exe, name, argv); if(loadelf(name, &ex, 0) < 0){ trace("kexecve(): loadelf failed: %r"); goto errout; } if((stack = setupstack(&ex, argv, envp)) == nil){ trace("kexecve(): setupstack failed: %r"); goto errout; } memset(&u, 0, sizeof(u)); u.sp = (ulong)stack; u.pc = (ulong)ex.ientry; current->ureg = &u; current->syscall = nil; phase++; trace("kexecve(): startup pc=%lux sp=%lux", current->ureg->pc, current->ureg->sp); errout: switch(phase){ default: free(envp); case 2: free(argv); case 1: free(name); case 0: free(b); } switch(phase){ case 4: retuser(); case 3: exitproc(current, SIGKILL, 1); } return r; } int sys_execve(char *name, char *argv[], char *envp[]) { struct kexecveargs args; trace("sys_execve(%s, %p, %p)", name, argv, envp); args.name = name; args.argv = argv; args.envp = envp; return onstack(kstack, kexecve, &args); } #pragma profile on