From a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46 Mon Sep 17 00:00:00 2001 From: rsc Date: Mon, 19 Apr 2004 19:29:25 +0000 Subject: libmach --- src/libmach/elf.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 src/libmach/elf.c (limited to 'src/libmach/elf.c') diff --git a/src/libmach/elf.c b/src/libmach/elf.c new file mode 100644 index 00000000..6a387fd3 --- /dev/null +++ b/src/libmach/elf.c @@ -0,0 +1,405 @@ +/* + * Parse 32-bit ELF files. + * Copyright (c) 2004 Russ Cox. See LICENSE. + */ + +#include +#include +#include +#include "elf.h" + +typedef struct ElfHdrBytes ElfHdrBytes; +typedef struct ElfSectBytes ElfSectBytes; +typedef struct ElfProgBytes ElfProgBytes; +typedef struct ElfSymBytes ElfSymBytes; + +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 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 ElfSymBytes +{ + uchar name[4]; + uchar value[4]; + uchar size[4]; + uchar info; /* top4: bind, bottom4: type */ + uchar other; + uchar shndx[2]; +}; + +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]; +}; + +uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' }; + +static void unpackhdr(ElfHdr*, ElfHdrBytes*); +static void unpackprog(ElfHdr*, ElfProg*, ElfProgBytes*); +static void unpacksect(ElfHdr*, ElfSect*, ElfSectBytes*); + +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; + ElfHdrBytes hdrb; + ElfProgBytes progb; + ElfSectBytes sectb; + 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){ + werrstr("bad ELF class - not 32-bit"); + goto err; + } + if(h->encoding != ElfDataLsb && h->encoding != ElfDataMsb){ + werrstr("bad ELF encoding - not LSB, MSB"); + goto err; + } + if(hdrb.ident[6] != h->version){ + werrstr("bad ELF encoding - version mismatch %02ux and %08ux", + (uint)hdrb.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); + for(i=0; inprog; i++){ + if(seek(fd, h->phoff+i*h->phentsize, 0) < 0 + || readn(fd, &progb, sizeof progb) != sizeof progb) + goto err; + unpackprog(h, &e->prog[i], &progb); + } + + e->nsect = h->shnum; + if(e->nsect == 0) + goto nosects; + e->sect = mallocz(sizeof(ElfSect)*e->nsect, 1); + for(i=0; insect; i++){ + if(seek(fd, h->shoff+i*h->shentsize, 0) < 0 + || readn(fd, §b, sizeof sectb) != sizeof sectb) + goto err; + unpacksect(h, &e->sect[i], §b); + } + + 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; insect; i++) + if(e->sect[i].name) + e->sect[i].name = 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; insect; i++) + free(elf->sect[i].base); + free(elf->sect); + free(elf->prog); + free(elf->shstrtab); + free(elf); +} + +static void +unpackhdr(ElfHdr *h, ElfHdrBytes *b) +{ + u16int (*e2)(uchar*); + u32int (*e4)(uchar*); + u64int (*e8)(uchar*); + + 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; + + 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); +} + +static void +unpackprog(ElfHdr *h, ElfProg *p, ElfProgBytes *b) +{ + u32int (*e4)(uchar*); + + 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); +} + +static void +unpacksect(ElfHdr *h, ElfSect *s, ElfSectBytes *b) +{ + u32int (*e4)(uchar*); + + e4 = h->e4; + s->name = (char*)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); +} + +ElfSect* +elfsection(Elf *elf, char *name) +{ + int i; + + for(i=0; insect; 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; + p = symtab->base + i * sizeof(ElfSymBytes); + s = 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); + 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; +} + -- cgit v1.2.3