blob: 2459a29176c0bc8fa2026cc2faf0ee60a693c2b9 [file] [log] [blame]
/*
* Parse 32-bit ELF files.
* Copyright (c) 2004 Russ Cox. See LICENSE.
*/
#include <u.h>
#include <libc.h>
#include <mach.h>
#include "elf.h"
typedef struct ElfHdrBytes ElfHdrBytes;
typedef struct ElfSectBytes ElfSectBytes;
typedef struct ElfProgBytes ElfProgBytes;
typedef struct ElfSymBytes ElfSymBytes;
typedef struct ElfHdrBytes64 ElfHdrBytes64;
typedef struct ElfSectBytes64 ElfSectBytes64;
typedef struct ElfProgBytes64 ElfProgBytes64;
typedef struct ElfSymBytes64 ElfSymBytes64;
struct ElfHdrBytes
{
uchar ident[16];
uchar type[2];
uchar machine[2];
uchar version[4];
uchar entry[4];
uchar phoff[4];
uchar shoff[4];
uchar flags[4];
uchar ehsize[2];
uchar phentsize[2];
uchar phnum[2];
uchar shentsize[2];
uchar shnum[2];
uchar shstrndx[2];
};
struct ElfHdrBytes64
{
uchar ident[16];
uchar type[2];
uchar machine[2];
uchar version[4];
uchar entry[8];
uchar phoff[8];
uchar shoff[8];
uchar flags[4];
uchar ehsize[2];
uchar phentsize[2];
uchar phnum[2];
uchar shentsize[2];
uchar shnum[2];
uchar shstrndx[2];
};
struct ElfSectBytes
{
uchar name[4];
uchar type[4];
uchar flags[4];
uchar addr[4];
uchar offset[4];
uchar size[4];
uchar link[4];
uchar info[4];
uchar align[4];
uchar entsize[4];
};
struct ElfSectBytes64
{
uchar name[4];
uchar type[4];
uchar flags[8];
uchar addr[8];
uchar offset[8];
uchar size[8];
uchar link[4];
uchar info[4];
uchar align[8];
uchar entsize[8];
};
struct ElfSymBytes
{
uchar name[4];
uchar value[4];
uchar size[4];
uchar info; /* top4: bind, bottom4: type */
uchar other;
uchar shndx[2];
};
struct ElfSymBytes64
{
uchar name[4];
uchar info;
uchar other;
uchar shndx[2];
uchar value[8];
uchar size[8];
};
struct ElfProgBytes
{
uchar type[4];
uchar offset[4];
uchar vaddr[4];
uchar paddr[4];
uchar filesz[4];
uchar memsz[4];
uchar flags[4];
uchar align[4];
};
struct ElfProgBytes64
{
uchar type[4];
uchar flags[4];
uchar offset[8];
uchar vaddr[8];
uchar paddr[8];
uchar filesz[8];
uchar memsz[8];
uchar align[8];
};
uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' };
static void unpackhdr(ElfHdr*, void*);
static void unpackprog(ElfHdr*, ElfProg*, void*);
static void unpacksect(ElfHdr*, ElfSect*, void*);
static char *elftypes[] = {
"none",
"relocatable",
"executable",
"shared object",
"core",
};
char*
elftype(int t)
{
if(t < 0 || t >= nelem(elftypes))
return "unknown";
return elftypes[t];
}
static char *elfmachs[] = {
"none",
"32100",
"sparc",
"386",
"68000",
"88000",
"486",
"860",
"MIPS",
};
char*
elfmachine(int t)
{
if(t < 0 || t >= nelem(elfmachs))
return "unknown";
return elfmachs[t];
}
Elf*
elfopen(char *name)
{
int fd;
Elf *e;
if((fd = open(name, OREAD)) < 0)
return nil;
if((e = elfinit(fd)) == nil)
close(fd);
return e;
}
Elf*
elfinit(int fd)
{
int i;
Elf *e;
ElfHdr *h;
union {
ElfHdrBytes h32;
ElfHdrBytes64 h64;
} hdrb;
void *p;
ElfSect *s;
e = mallocz(sizeof(Elf), 1);
if(e == nil)
return nil;
e->fd = fd;
/*
* parse header
*/
seek(fd, 0, 0);
if(readn(fd, &hdrb, sizeof hdrb) != sizeof hdrb)
goto err;
h = &e->hdr;
unpackhdr(h, &hdrb);
if(h->class != ElfClass32 && h->class != ElfClass64){
werrstr("bad ELF class - not 32-bit, 64-bit");
goto err;
}
if(h->encoding != ElfDataLsb && h->encoding != ElfDataMsb){
werrstr("bad ELF encoding - not LSB, MSB");
goto err;
}
if(hdrb.h32.ident[6] != h->version){
werrstr("bad ELF encoding - version mismatch %02ux and %08ux",
(uint)hdrb.h32.ident[6], (uint)h->version);
goto err;
}
/*
* the prog+section info is almost always small - just load it into memory.
*/
e->nprog = h->phnum;
e->prog = mallocz(sizeof(ElfProg)*e->nprog, 1);
p = mallocz(h->phentsize, 1);
for(i=0; i<e->nprog; i++){
if(seek(fd, h->phoff+i*h->phentsize, 0) < 0
|| readn(fd, p, h->phentsize) != h->phentsize)
goto err;
unpackprog(h, &e->prog[i], p);
}
free(p);
e->nsect = h->shnum;
if(e->nsect == 0)
goto nosects;
e->sect = mallocz(sizeof(ElfSect)*e->nsect, 1);
p = mallocz(h->shentsize, 1);
for(i=0; i<e->nsect; i++){
if(seek(fd, h->shoff+i*h->shentsize, 0) < 0
|| readn(fd, p, h->shentsize) != h->shentsize)
goto err;
unpacksect(h, &e->sect[i], p);
}
free(p);
if(h->shstrndx >= e->nsect){
fprint(2, "warning: bad string section index %d >= %d", h->shstrndx, e->nsect);
h->shnum = 0;
e->nsect = 0;
goto nosects;
}
s = &e->sect[h->shstrndx];
if(elfmap(e, s) < 0)
goto err;
for(i=0; i<e->nsect; i++)
if(e->sect[i].name)
e->sect[i].name = (char*)s->base + (ulong)e->sect[i].name;
e->symtab = elfsection(e, ".symtab");
if(e->symtab){
if(e->symtab->link >= e->nsect)
e->symtab = nil;
else{
e->symstr = &e->sect[e->symtab->link];
e->nsymtab = e->symtab->size / sizeof(ElfSymBytes);
}
}
e->dynsym = elfsection(e, ".dynsym");
if(e->dynsym){
if(e->dynsym->link >= e->nsect)
e->dynsym = nil;
else{
e->dynstr = &e->sect[e->dynsym->link];
e->ndynsym = e->dynsym->size / sizeof(ElfSymBytes);
}
}
e->bss = elfsection(e, ".bss");
nosects:
return e;
err:
free(e->sect);
free(e->prog);
free(e->shstrtab);
free(e);
return nil;
}
void
elfclose(Elf *elf)
{
int i;
for(i=0; i<elf->nsect; i++)
free(elf->sect[i].base);
free(elf->sect);
free(elf->prog);
free(elf->shstrtab);
free(elf);
}
static void
unpackhdr(ElfHdr *h, void *v)
{
u16int (*e2)(uchar*);
u32int (*e4)(uchar*);
u64int (*e8)(uchar*);
ElfHdrBytes *b;
ElfHdrBytes64 *b64;
b = v;
memmove(h->magic, b->ident, 4);
h->class = b->ident[4];
h->encoding = b->ident[5];
switch(h->encoding){
case ElfDataLsb:
e2 = leload2;
e4 = leload4;
e8 = leload8;
break;
case ElfDataMsb:
e2 = beload2;
e4 = beload4;
e8 = beload8;
break;
default:
return;
}
h->abi = b->ident[7];
h->abiversion = b->ident[8];
h->e2 = e2;
h->e4 = e4;
h->e8 = e8;
if(h->class == ElfClass64)
goto b64;
h->type = e2(b->type);
h->machine = e2(b->machine);
h->version = e4(b->version);
h->entry = e4(b->entry);
h->phoff = e4(b->phoff);
h->shoff = e4(b->shoff);
h->flags = e4(b->flags);
h->ehsize = e2(b->ehsize);
h->phentsize = e2(b->phentsize);
h->phnum = e2(b->phnum);
h->shentsize = e2(b->shentsize);
h->shnum = e2(b->shnum);
h->shstrndx = e2(b->shstrndx);
return;
b64:
b64 = v;
h->type = e2(b64->type);
h->machine = e2(b64->machine);
h->version = e4(b64->version);
h->entry = e8(b64->entry);
h->phoff = e8(b64->phoff);
h->shoff = e8(b64->shoff);
h->flags = e4(b64->flags);
h->ehsize = e2(b64->ehsize);
h->phentsize = e2(b64->phentsize);
h->phnum = e2(b64->phnum);
h->shentsize = e2(b64->shentsize);
h->shnum = e2(b64->shnum);
h->shstrndx = e2(b64->shstrndx);
return;
}
static void
unpackprog(ElfHdr *h, ElfProg *p, void *v)
{
u32int (*e4)(uchar*);
u64int (*e8)(uchar*);
if(h->class == ElfClass32) {
ElfProgBytes *b;
b = v;
e4 = h->e4;
p->type = e4(b->type);
p->offset = e4(b->offset);
p->vaddr = e4(b->vaddr);
p->paddr = e4(b->paddr);
p->filesz = e4(b->filesz);
p->memsz = e4(b->memsz);
p->flags = e4(b->flags);
p->align = e4(b->align);
} else {
ElfProgBytes64 *b;
b = v;
e4 = h->e4;
e8 = h->e8;
p->type = e4(b->type);
p->offset = e8(b->offset);
p->vaddr = e8(b->vaddr);
p->paddr = e8(b->paddr);
p->filesz = e8(b->filesz);
p->memsz = e8(b->memsz);
p->flags = e4(b->flags);
p->align = e8(b->align);
}
}
static void
unpacksect(ElfHdr *h, ElfSect *s, void *v)
{
u32int (*e4)(uchar*);
u64int (*e8)(uchar*);
if(h->class == ElfClass32) {
ElfSectBytes *b;
b = v;
e4 = h->e4;
s->name = (char*)(uintptr)e4(b->name);
s->type = e4(b->type);
s->flags = e4(b->flags);
s->addr = e4(b->addr);
s->offset = e4(b->offset);
s->size = e4(b->size);
s->link = e4(b->link);
s->info = e4(b->info);
s->align = e4(b->align);
s->entsize = e4(b->entsize);
} else {
ElfSectBytes64 *b;
b = v;
e4 = h->e4;
e8 = h->e8;
s->name = (char*)(uintptr)e4(b->name);
s->type = e4(b->type);
s->flags = e8(b->flags);
s->addr = e8(b->addr);
s->offset = e8(b->offset);
s->size = e8(b->size);
s->link = e4(b->link);
s->info = e4(b->info);
s->align = e8(b->align);
s->entsize = e8(b->entsize);
}
}
ElfSect*
elfsection(Elf *elf, char *name)
{
int i;
for(i=0; i<elf->nsect; i++){
if(elf->sect[i].name == name)
return &elf->sect[i];
if(elf->sect[i].name && name
&& strcmp(elf->sect[i].name, name) == 0)
return &elf->sect[i];
}
werrstr("elf section '%s' not found", name);
return nil;
}
int
elfmap(Elf *elf, ElfSect *sect)
{
if(sect->base)
return 0;
if((sect->base = malloc(sect->size)) == nil)
return -1;
werrstr("short read");
if(seek(elf->fd, sect->offset, 0) < 0
|| readn(elf->fd, sect->base, sect->size) != sect->size){
free(sect->base);
sect->base = nil;
return -1;
}
return 0;
}
int
elfsym(Elf *elf, int i, ElfSym *sym)
{
ElfSect *symtab, *strtab;
uchar *p;
char *s;
ulong x;
if(i < 0){
werrstr("bad index %d in elfsym", i);
return -1;
}
if(i < elf->nsymtab){
symtab = elf->symtab;
strtab = elf->symstr;
extract:
if(elfmap(elf, symtab) < 0 || elfmap(elf, strtab) < 0)
return -1;
if(elf->hdr.class == ElfClass32) {
p = symtab->base + i * sizeof(ElfSymBytes);
s = (char*)strtab->base;
x = elf->hdr.e4(p);
if(x >= strtab->size){
werrstr("bad symbol name offset 0x%lux", x);
return -1;
}
sym->name = s + x;
sym->value = elf->hdr.e4(p+4);
sym->size = elf->hdr.e4(p+8);
x = p[12];
sym->bind = x>>4;
sym->type = x & 0xF;
sym->other = p[13];
sym->shndx = elf->hdr.e2(p+14);
} else {
p = symtab->base + i * sizeof(ElfSymBytes64);
s = (char*)strtab->base;
x = elf->hdr.e4(p);
if(x >= strtab->size){
werrstr("bad symbol name offset 0x%lux", x);
return -1;
}
sym->name = s + x;
x = p[4];
sym->bind = x>>4;
sym->type = x & 0xF;
sym->other = p[5];
sym->shndx = elf->hdr.e2(p+6);
sym->value = elf->hdr.e8(p+8);
sym->size = elf->hdr.e8(p+16);
}
return 0;
}
i -= elf->nsymtab;
if(i < elf->ndynsym){
symtab = elf->dynsym;
strtab = elf->dynstr;
goto extract;
}
/* i -= elf->ndynsym */
werrstr("symbol index out of range");
return -1;
}