This source file includes following definitions.
- valuecmp
- ldelf
- section
- map
- readsym
- rbyoff
- reltype
#include "l.h"
#include "lib.h"
#include "../ld/elf.h"
enum
{
ElfClassNone = 0,
ElfClass32,
ElfClass64,
ElfDataNone = 0,
ElfDataLsb,
ElfDataMsb,
ElfTypeNone = 0,
ElfTypeRelocatable,
ElfTypeExecutable,
ElfTypeSharedObject,
ElfTypeCore,
ElfMachNone = 0,
ElfMach32100,
ElfMachSparc,
ElfMach386,
ElfMach68000,
ElfMach88000,
ElfMach486,
ElfMach860,
ElfMachMips,
ElfMachS370,
ElfMachMipsLe,
ElfMachParisc = 15,
ElfMachVpp500 = 17,
ElfMachSparc32Plus,
ElfMach960,
ElfMachPower,
ElfMachPower64,
ElfMachS390,
ElfMachV800 = 36,
ElfMachFr20,
ElfMachRh32,
ElfMachRce,
ElfMachArm,
ElfMachAlpha,
ElfMachSH,
ElfMachSparc9,
ElfMachAmd64 = 62,
ElfAbiNone = 0,
ElfAbiSystemV = 0,
ElfAbiHPUX,
ElfAbiNetBSD,
ElfAbiLinux,
ElfAbiSolaris = 6,
ElfAbiAix,
ElfAbiIrix,
ElfAbiFreeBSD,
ElfAbiTru64,
ElfAbiModesto,
ElfAbiOpenBSD,
ElfAbiARM = 97,
ElfAbiEmbedded = 255,
ElfSectNone = 0,
ElfSectProgbits,
ElfSectSymtab,
ElfSectStrtab,
ElfSectRela,
ElfSectHash,
ElfSectDynamic,
ElfSectNote,
ElfSectNobits,
ElfSectRel,
ElfSectShlib,
ElfSectDynsym,
ElfSectFlagWrite = 0x1,
ElfSectFlagAlloc = 0x2,
ElfSectFlagExec = 0x4,
ElfSymBindLocal = 0,
ElfSymBindGlobal,
ElfSymBindWeak,
ElfSymTypeNone = 0,
ElfSymTypeObject,
ElfSymTypeFunc,
ElfSymTypeSection,
ElfSymTypeFile,
ElfSymShnNone = 0,
ElfSymShnAbs = 0xFFF1,
ElfSymShnCommon = 0xFFF2,
ElfProgNone = 0,
ElfProgLoad,
ElfProgDynamic,
ElfProgInterp,
ElfProgNote,
ElfProgShlib,
ElfProgPhdr,
ElfProgFlagExec = 0x1,
ElfProgFlagWrite = 0x2,
ElfProgFlagRead = 0x4,
ElfNotePrStatus = 1,
ElfNotePrFpreg = 2,
ElfNotePrPsinfo = 3,
ElfNotePrTaskstruct = 4,
ElfNotePrAuxv = 6,
ElfNotePrXfpreg = 0x46e62b7f
};
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 off[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 off[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;
uchar other;
uchar shndx[2];
};
struct ElfSymBytes64
{
uchar name[4];
uchar info;
uchar other;
uchar shndx[2];
uchar value[8];
uchar size[8];
};
typedef struct ElfSect ElfSect;
typedef struct ElfObj ElfObj;
typedef struct ElfSym ElfSym;
struct ElfSect
{
char *name;
uint32 type;
uint64 flags;
uint64 addr;
uint64 off;
uint64 size;
uint32 link;
uint32 info;
uint64 align;
uint64 entsize;
uchar *base;
LSym *sym;
};
struct ElfObj
{
Biobuf *f;
int64 base;
int64 len;
int is64;
char *name;
Endian *e;
ElfSect *sect;
uint nsect;
char *shstrtab;
int nsymtab;
ElfSect *symtab;
ElfSect *symstr;
uint32 type;
uint32 machine;
uint32 version;
uint64 entry;
uint64 phoff;
uint64 shoff;
uint32 flags;
uint32 ehsize;
uint32 phentsize;
uint32 phnum;
uint32 shentsize;
uint32 shnum;
uint32 shstrndx;
};
struct ElfSym
{
char* name;
uint64 value;
uint64 size;
uchar bind;
uchar type;
uchar other;
uint16 shndx;
LSym* sym;
};
uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' };
static ElfSect* section(ElfObj*, char*);
static int map(ElfObj*, ElfSect*);
static int readsym(ElfObj*, int i, ElfSym*, int);
static int reltype(char*, int, uchar*);
int
valuecmp(LSym *a, LSym *b)
{
if(a->value < b->value)
return -1;
if(a->value > b->value)
return +1;
return 0;
}
void
ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
{
int32 base;
uint64 add, info;
char *name;
int i, j, rela, is64, n;
uchar hdrbuf[64];
uchar *p;
ElfHdrBytes *hdr;
ElfObj *obj;
ElfSect *sect, *rsect;
ElfSym sym;
Endian *e;
Reloc *r, *rp;
LSym *s;
LSym **symbols;
symbols = nil;
if(debug['v'])
Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn);
ctxt->version++;
base = Boffset(f);
if(Bread(f, hdrbuf, sizeof hdrbuf) != sizeof hdrbuf)
goto bad;
hdr = (ElfHdrBytes*)hdrbuf;
if(memcmp(hdr->ident, ElfMagic, 4) != 0)
goto bad;
switch(hdr->ident[5]) {
case ElfDataLsb:
e = ≤
break;
case ElfDataMsb:
e = &be;
break;
default:
goto bad;
}
obj = mal(sizeof *obj);
obj->e = e;
obj->f = f;
obj->base = base;
obj->len = len;
obj->name = pn;
is64 = 0;
if(hdr->ident[4] == ElfClass64) {
ElfHdrBytes64* hdr;
is64 = 1;
hdr = (ElfHdrBytes64*)hdrbuf;
obj->type = e->e16(hdr->type);
obj->machine = e->e16(hdr->machine);
obj->version = e->e32(hdr->version);
obj->phoff = e->e64(hdr->phoff);
obj->shoff = e->e64(hdr->shoff);
obj->flags = e->e32(hdr->flags);
obj->ehsize = e->e16(hdr->ehsize);
obj->phentsize = e->e16(hdr->phentsize);
obj->phnum = e->e16(hdr->phnum);
obj->shentsize = e->e16(hdr->shentsize);
obj->shnum = e->e16(hdr->shnum);
obj->shstrndx = e->e16(hdr->shstrndx);
} else {
obj->type = e->e16(hdr->type);
obj->machine = e->e16(hdr->machine);
obj->version = e->e32(hdr->version);
obj->entry = e->e32(hdr->entry);
obj->phoff = e->e32(hdr->phoff);
obj->shoff = e->e32(hdr->shoff);
obj->flags = e->e32(hdr->flags);
obj->ehsize = e->e16(hdr->ehsize);
obj->phentsize = e->e16(hdr->phentsize);
obj->phnum = e->e16(hdr->phnum);
obj->shentsize = e->e16(hdr->shentsize);
obj->shnum = e->e16(hdr->shnum);
obj->shstrndx = e->e16(hdr->shstrndx);
}
obj->is64 = is64;
if(hdr->ident[6] != obj->version)
goto bad;
if(e->e16(hdr->type) != ElfTypeRelocatable) {
diag("%s: elf but not elf relocatable object", pn);
return;
}
switch(thechar) {
default:
diag("%s: elf %s unimplemented", pn, thestring);
return;
case '5':
if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) {
diag("%s: elf object but not arm", pn);
return;
}
break;
case '6':
if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) {
diag("%s: elf object but not amd64", pn);
return;
}
break;
case '8':
if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) {
diag("%s: elf object but not 386", pn);
return;
}
break;
}
obj->sect = mal(obj->shnum*sizeof obj->sect[0]);
obj->nsect = obj->shnum;
for(i=0; i<obj->nsect; i++) {
if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0)
goto bad;
sect = &obj->sect[i];
if(is64) {
ElfSectBytes64 b;
werrstr("short read");
if(Bread(f, &b, sizeof b) != sizeof b)
goto bad;
sect->name = (char*)(uintptr)e->e32(b.name);
sect->type = e->e32(b.type);
sect->flags = e->e64(b.flags);
sect->addr = e->e64(b.addr);
sect->off = e->e64(b.off);
sect->size = e->e64(b.size);
sect->link = e->e32(b.link);
sect->info = e->e32(b.info);
sect->align = e->e64(b.align);
sect->entsize = e->e64(b.entsize);
} else {
ElfSectBytes b;
werrstr("short read");
if(Bread(f, &b, sizeof b) != sizeof b)
goto bad;
sect->name = (char*)(uintptr)e->e32(b.name);
sect->type = e->e32(b.type);
sect->flags = e->e32(b.flags);
sect->addr = e->e32(b.addr);
sect->off = e->e32(b.off);
sect->size = e->e32(b.size);
sect->link = e->e32(b.link);
sect->info = e->e32(b.info);
sect->align = e->e32(b.align);
sect->entsize = e->e32(b.entsize);
}
}
if(obj->shstrndx >= obj->nsect) {
werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect);
goto bad;
}
sect = &obj->sect[obj->shstrndx];
if(map(obj, sect) < 0)
goto bad;
for(i=0; i<obj->nsect; i++)
if(obj->sect[i].name != nil)
obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name;
obj->symtab = section(obj, ".symtab");
if(obj->symtab == nil) {
return;
}
if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) {
diag("%s: elf object has symbol table with invalid string table link", pn);
return;
}
obj->symstr = &obj->sect[obj->symtab->link];
if(is64)
obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64);
else
obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes);
if(map(obj, obj->symtab) < 0)
goto bad;
if(map(obj, obj->symstr) < 0)
goto bad;
for(i=0; i<obj->nsect; i++) {
sect = &obj->sect[i];
if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc))
continue;
if(sect->type != ElfSectNobits && map(obj, sect) < 0)
goto bad;
name = smprint("%s(%s)", pkg, sect->name);
s = linklookup(ctxt, name, ctxt->version);
free(name);
switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) {
default:
werrstr("unexpected flags for ELF section %s", sect->name);
goto bad;
case ElfSectFlagAlloc:
s->type = SRODATA;
break;
case ElfSectFlagAlloc + ElfSectFlagWrite:
s->type = SDATA;
break;
case ElfSectFlagAlloc + ElfSectFlagExec:
s->type = STEXT;
break;
}
if(sect->type == ElfSectProgbits) {
s->p = sect->base;
s->np = sect->size;
}
s->size = sect->size;
s->align = sect->align;
sect->sym = s;
}
symbols = malloc(obj->nsymtab * sizeof(symbols[0]));
if(symbols == nil) {
diag("out of memory");
errorexit();
}
for(i=1; i<obj->nsymtab; i++) {
if(readsym(obj, i, &sym, 1) < 0)
goto bad;
symbols[i] = sym.sym;
if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone)
continue;
if(sym.shndx == ElfSymShnCommon) {
s = sym.sym;
if(s->size < sym.size)
s->size = sym.size;
if(s->type == 0 || s->type == SXREF)
s->type = SBSS;
continue;
}
if(sym.shndx >= obj->nsect || sym.shndx == 0)
continue;
if(sym.sym == S)
continue;
sect = obj->sect+sym.shndx;
if(sect->sym == nil) {
diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type);
continue;
}
s = sym.sym;
if(s->outer != S) {
if(s->dupok)
continue;
diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
errorexit();
}
s->sub = sect->sym->sub;
sect->sym->sub = s;
s->type = sect->sym->type | (s->type&~SMASK) | SSUB;
if(!(s->cgoexport & CgoExportDynamic))
s->dynimplib = nil;
s->value = sym.value;
s->size = sym.size;
s->outer = sect->sym;
if(sect->sym->type == STEXT) {
if(s->external && !s->dupok)
diag("%s: duplicate definition of %s", pn, s->name);
s->external = 1;
}
}
for(i=0; i<obj->nsect; i++) {
s = obj->sect[i].sym;
if(s == S)
continue;
if(s->sub)
s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub));
if(s->type == STEXT) {
if(s->onlist)
sysfatal("symbol %s listed multiple times", s->name);
s->onlist = 1;
if(ctxt->etextp)
ctxt->etextp->next = s;
else
ctxt->textp = s;
ctxt->etextp = s;
for(s = s->sub; s != S; s = s->sub) {
if(s->onlist)
sysfatal("symbol %s listed multiple times", s->name);
s->onlist = 1;
ctxt->etextp->next = s;
ctxt->etextp = s;
}
}
}
for(i=0; i<obj->nsect; i++) {
rsect = &obj->sect[i];
if(rsect->type != ElfSectRela && rsect->type != ElfSectRel)
continue;
if(rsect->info >= obj->nsect || obj->sect[rsect->info].base == nil)
continue;
sect = &obj->sect[rsect->info];
if(map(obj, rsect) < 0)
goto bad;
rela = rsect->type == ElfSectRela;
n = rsect->size/(4+4*is64)/(2+rela);
r = mal(n*sizeof r[0]);
p = rsect->base;
for(j=0; j<n; j++) {
add = 0;
rp = &r[j];
if(is64) {
rp->off = e->e64(p);
p += 8;
info = e->e64(p);
p += 8;
if(rela) {
add = e->e64(p);
p += 8;
}
} else {
rp->off = e->e32(p);
p += 4;
info = e->e32(p);
info = info>>8<<32 | (info&0xff);
p += 4;
if(rela) {
add = e->e32(p);
p += 4;
}
}
if((info & 0xffffffff) == 0) {
j--;
n--;
continue;
}
if((info >> 32) == 0) {
rp->sym = S;
} else {
if(readsym(obj, info>>32, &sym, 0) < 0)
goto bad;
sym.sym = symbols[info>>32];
if(sym.sym == nil) {
werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d",
sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type);
goto bad;
}
rp->sym = sym.sym;
}
rp->type = reltype(pn, (uint32)info, &rp->siz);
if(rela)
rp->add = add;
else {
if(rp->siz == 4)
rp->add = e->e32(sect->base+rp->off);
else if(rp->siz == 8)
rp->add = e->e64(sect->base+rp->off);
else
diag("invalid rela size %d", rp->siz);
}
if(rp->siz == 4)
rp->add = (int32)rp->add;
}
qsort(r, n, sizeof r[0], rbyoff);
s = sect->sym;
s->r = r;
s->nr = n;
}
free(symbols);
return;
bad:
diag("%s: malformed elf file: %r", pn);
free(symbols);
}
static ElfSect*
section(ElfObj *obj, char *name)
{
int i;
for(i=0; i<obj->nsect; i++)
if(obj->sect[i].name && name && strcmp(obj->sect[i].name, name) == 0)
return &obj->sect[i];
return nil;
}
static int
map(ElfObj *obj, ElfSect *sect)
{
if(sect->base != nil)
return 0;
if(sect->off+sect->size > obj->len) {
werrstr("elf section past end of file");
return -1;
}
sect->base = mal(sect->size);
werrstr("short read");
if(Bseek(obj->f, obj->base+sect->off, 0) < 0 || Bread(obj->f, sect->base, sect->size) != sect->size)
return -1;
return 0;
}
static int
readsym(ElfObj *obj, int i, ElfSym *sym, int needSym)
{
LSym *s;
if(i >= obj->nsymtab || i < 0) {
werrstr("invalid elf symbol index");
return -1;
}
if(i == 0) {
diag("readym: read null symbol!");
}
if(obj->is64) {
ElfSymBytes64 *b;
b = (ElfSymBytes64*)(obj->symtab->base + i*sizeof *b);
sym->name = (char*)obj->symstr->base + obj->e->e32(b->name);
sym->value = obj->e->e64(b->value);
sym->size = obj->e->e64(b->size);
sym->shndx = obj->e->e16(b->shndx);
sym->bind = b->info>>4;
sym->type = b->info&0xf;
sym->other = b->other;
} else {
ElfSymBytes *b;
b = (ElfSymBytes*)(obj->symtab->base + i*sizeof *b);
sym->name = (char*)obj->symstr->base + obj->e->e32(b->name);
sym->value = obj->e->e32(b->value);
sym->size = obj->e->e32(b->size);
sym->shndx = obj->e->e16(b->shndx);
sym->bind = b->info>>4;
sym->type = b->info&0xf;
sym->other = b->other;
}
s = nil;
if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0)
sym->name = ".got";
switch(sym->type) {
case ElfSymTypeSection:
s = obj->sect[sym->shndx].sym;
break;
case ElfSymTypeObject:
case ElfSymTypeFunc:
case ElfSymTypeNone:
switch(sym->bind) {
case ElfSymBindGlobal:
if(needSym) {
s = linklookup(ctxt, sym->name, 0);
if(s && sym->other == 2) {
s->type |= SHIDDEN;
s->dupok = 1;
}
}
break;
case ElfSymBindLocal:
if(!(thechar == '5' && (strcmp(sym->name, "$a") == 0 || strcmp(sym->name, "$d") == 0)))
if(needSym) {
s = linknewsym(ctxt, sym->name, ctxt->version);
s->type |= SHIDDEN;
}
break;
case ElfSymBindWeak:
if(needSym) {
s = linknewsym(ctxt, sym->name, 0);
if(sym->other == 2)
s->type |= SHIDDEN;
}
break;
default:
werrstr("%s: invalid symbol binding %d", sym->name, sym->bind);
return -1;
}
break;
}
if(s != nil && s->type == 0 && sym->type != ElfSymTypeSection)
s->type = SXREF;
sym->sym = s;
return 0;
}
int
rbyoff(const void *va, const void *vb)
{
Reloc *a, *b;
a = (Reloc*)va;
b = (Reloc*)vb;
if(a->off < b->off)
return -1;
if(a->off > b->off)
return +1;
return 0;
}
#define R(x, y) ((x)|((y)<<24))
static int
reltype(char *pn, int elftype, uchar *siz)
{
switch(R(thechar, elftype)) {
default:
diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype);
case R('5', R_ARM_ABS32):
case R('5', R_ARM_GOT32):
case R('5', R_ARM_PLT32):
case R('5', R_ARM_GOTOFF):
case R('5', R_ARM_GOTPC):
case R('5', R_ARM_THM_PC22):
case R('5', R_ARM_REL32):
case R('5', R_ARM_CALL):
case R('5', R_ARM_V4BX):
case R('5', R_ARM_GOT_PREL):
case R('5', R_ARM_PC24):
case R('5', R_ARM_JUMP24):
case R('6', R_X86_64_PC32):
case R('6', R_X86_64_PLT32):
case R('6', R_X86_64_GOTPCREL):
case R('8', R_386_32):
case R('8', R_386_PC32):
case R('8', R_386_GOT32):
case R('8', R_386_PLT32):
case R('8', R_386_GOTOFF):
case R('8', R_386_GOTPC):
*siz = 4;
break;
case R('6', R_X86_64_64):
*siz = 8;
break;
}
return 256+elftype;
}