| /* |
| * 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; |
| } |
| |