#include <u.h>
#include <libc.h>
#include "linuxsys.h"
#include "linux.h"
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,
};
typedef struct Elfhdr Elfhdr;
typedef struct Proghdr Proghdr;
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;
};
int
loadelf(char *file, ElfEx *ex)
{
static int zfd = -1;
int fd;
int i, l;
int mapprot;
int mapflags;
ulong mapbase;
ulong loadaddr;
Elfhdr hdr;
Proghdr *phdr;
char *interpreter;
fd = -1;
interpreter = nil;
phdr = nil;
if(zfd < 0){
if((zfd = open("/dev/zero", OREAD)) < 0)
goto errout;
}
if((fd = open(file, OREAD)) < 0)
goto errout;
if(readn(fd, &hdr, sizeof(hdr)) != sizeof(hdr)){
werrstr("cant read elf header: %r");
goto errout;
}
if(memcmp(hdr.ident, "\x7fELF", 4)!=0){
werrstr("no elf magic");
goto errout;
}
l = hdr.phnum * hdr.phentsize;
phdr = malloc(l);
seek(fd, hdr.phoff, 0);
if(readn(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;
for(i=0; i<hdr.phnum; i++){
Proghdr *p;
p = &phdr[i];
if(p->type == ElfPInterp){
if(interpreter){
werrstr("multiple interpeter sections");
goto errout;
}
l = p->filesz;
interpreter = malloc(l+1);
seek(fd, p->offset, 0);
if(readn(fd, interpreter, l)!=l){
werrstr("cant read interpreter section");
goto errout;
}
interpreter[l] = '\0';
}
if(p->type == ElfPLoad){
ulong a;
int diff;
mapprot = PROT_READ;
if(hdr.entry >= p->vaddr && hdr.entry < p->vaddr + p->memsz)
mapprot |= PROT_EXEC;
diff = p->vaddr - TRUNC_PAGE(p->vaddr);
a = (ulong)mmap(
(void*)(mapbase + p->vaddr - diff), (p->filesz + diff),
mapprot,
mapflags,
fd, p->offset - diff);
if(a == (ulong)-1){
werrstr("mmap failed: %r");
goto errout;
}
if(loadaddr == 0)
loadaddr = a;
if(hdr.type == ElfTShared && mapbase == 0){
mapbase = (a - p->vaddr);
mapflags |= MAP_FIXED;
}
if(p->memsz > ROUND_PAGE(p->filesz + diff)){
a = (ulong)mmap(
(void*)ROUND_PAGE(a + p->filesz + diff),
p->memsz - ROUND_PAGE(p->filesz + diff),
mapprot,
mapflags,
zfd, 0);
if(a == (ulong)-1){
werrstr("mmap failed: %r");
goto errout;
}
}
}
}
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(interpreter){
ElfEx interpex;
if(loadelf(interpreter, &interpex) < 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 = ex->base;
}
close(fd);
free(phdr);
return 0;
errout:
if(interpreter)
free(interpreter);
if(fd >= 0)
close(fd);
if(phdr)
free(phdr);
return -1;
}
|