blob: de9e6b8541e27fb8ec1e64d8fd7575b033ac5e63 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "l.h"
#include "lib.h"
#include "../ld/elf.h"
/*
* We use the 64-bit data structures on both 32- and 64-bit machines
* in order to write the code just once. The 64-bit data structure is
* written in the 32-bit format on the 32-bit machines.
*/
#define NSECT 32
int iself;
static int elf64;
static ElfEhdr hdr;
static ElfPhdr *phdr[NSECT];
static ElfShdr *shdr[NSECT];
static char *interp;
typedef struct Elfstring Elfstring;
struct Elfstring
{
char *s;
int off;
};
static Elfstring elfstr[100];
static int nelfstr;
/*
Initialize the global variable that describes the ELF header. It will be updated as
we write section and prog headers.
*/
void
elfinit(void)
{
iself = 1;
switch(thechar) {
// 64-bit architectures
case '6':
elf64 = 1;
hdr.phoff = ELF64HDRSIZE; /* Must be be ELF64HDRSIZE: first PHdr must follow ELF header */
hdr.shoff = ELF64HDRSIZE; /* Will move as we add PHeaders */
hdr.ehsize = ELF64HDRSIZE; /* Must be ELF64HDRSIZE */
hdr.phentsize = ELF64PHDRSIZE; /* Must be ELF64PHDRSIZE */
hdr.shentsize = ELF64SHDRSIZE; /* Must be ELF64SHDRSIZE */
break;
// 32-bit architectures
default:
hdr.phoff = ELF32HDRSIZE; /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */
hdr.shoff = ELF32HDRSIZE; /* Will move as we add PHeaders */
hdr.ehsize = ELF32HDRSIZE; /* Must be ELF32HDRSIZE */
hdr.phentsize = ELF32PHDRSIZE; /* Must be ELF32PHDRSIZE */
hdr.shentsize = ELF32SHDRSIZE; /* Must be ELF32SHDRSIZE */
}
}
void
elf64phdr(ElfPhdr *e)
{
LPUT(e->type);
LPUT(e->flags);
VPUT(e->off);
VPUT(e->vaddr);
VPUT(e->paddr);
VPUT(e->filesz);
VPUT(e->memsz);
VPUT(e->align);
}
void
elf32phdr(ElfPhdr *e)
{
int frag;
if(e->type == PT_LOAD) {
// Correct ELF loaders will do this implicitly,
// but buggy ELF loaders like the one in some
// versions of QEMU won't.
frag = e->vaddr&(e->align-1);
e->off -= frag;
e->vaddr -= frag;
e->paddr -= frag;
e->filesz += frag;
e->memsz += frag;
}
LPUT(e->type);
LPUT(e->off);
LPUT(e->vaddr);
LPUT(e->paddr);
LPUT(e->filesz);
LPUT(e->memsz);
LPUT(e->flags);
LPUT(e->align);
}
void
elf64shdr(ElfShdr *e)
{
LPUT(e->name);
LPUT(e->type);
VPUT(e->flags);
VPUT(e->addr);
VPUT(e->off);
VPUT(e->size);
LPUT(e->link);
LPUT(e->info);
VPUT(e->addralign);
VPUT(e->entsize);
}
void
elf32shdr(ElfShdr *e)
{
LPUT(e->name);
LPUT(e->type);
LPUT(e->flags);
LPUT(e->addr);
LPUT(e->off);
LPUT(e->size);
LPUT(e->link);
LPUT(e->info);
LPUT(e->addralign);
LPUT(e->entsize);
}
uint32
elfwriteshdrs(void)
{
int i;
if (elf64) {
for (i = 0; i < hdr.shnum; i++)
elf64shdr(shdr[i]);
return hdr.shnum * ELF64SHDRSIZE;
}
for (i = 0; i < hdr.shnum; i++)
elf32shdr(shdr[i]);
return hdr.shnum * ELF32SHDRSIZE;
}
void
elfsetstring(char *s, int off)
{
if(nelfstr >= nelem(elfstr)) {
diag("too many elf strings");
errorexit();
}
elfstr[nelfstr].s = s;
elfstr[nelfstr].off = off;
nelfstr++;
}
uint32
elfwritephdrs(void)
{
int i;
if (elf64) {
for (i = 0; i < hdr.phnum; i++)
elf64phdr(phdr[i]);
return hdr.phnum * ELF64PHDRSIZE;
}
for (i = 0; i < hdr.phnum; i++)
elf32phdr(phdr[i]);
return hdr.phnum * ELF32PHDRSIZE;
}
ElfPhdr*
newElfPhdr(void)
{
ElfPhdr *e;
e = mal(sizeof *e);
if (hdr.phnum >= NSECT)
diag("too many phdrs");
else
phdr[hdr.phnum++] = e;
if (elf64)
hdr.shoff += ELF64PHDRSIZE;
else
hdr.shoff += ELF32PHDRSIZE;
return e;
}
ElfShdr*
newElfShstrtab(vlong name)
{
hdr.shstrndx = hdr.shnum;
return newElfShdr(name);
}
ElfShdr*
newElfShdr(vlong name)
{
ElfShdr *e;
e = mal(sizeof *e);
e->name = name;
if (hdr.shnum >= NSECT) {
diag("too many shdrs");
} else {
shdr[hdr.shnum++] = e;
}
return e;
}
ElfEhdr*
getElfEhdr(void)
{
return &hdr;
}
uint32
elf64writehdr(void)
{
int i;
for (i = 0; i < EI_NIDENT; i++)
cput(hdr.ident[i]);
WPUT(hdr.type);
WPUT(hdr.machine);
LPUT(hdr.version);
VPUT(hdr.entry);
VPUT(hdr.phoff);
VPUT(hdr.shoff);
LPUT(hdr.flags);
WPUT(hdr.ehsize);
WPUT(hdr.phentsize);
WPUT(hdr.phnum);
WPUT(hdr.shentsize);
WPUT(hdr.shnum);
WPUT(hdr.shstrndx);
return ELF64HDRSIZE;
}
uint32
elf32writehdr(void)
{
int i;
for (i = 0; i < EI_NIDENT; i++)
cput(hdr.ident[i]);
WPUT(hdr.type);
WPUT(hdr.machine);
LPUT(hdr.version);
LPUT(hdr.entry);
LPUT(hdr.phoff);
LPUT(hdr.shoff);
LPUT(hdr.flags);
WPUT(hdr.ehsize);
WPUT(hdr.phentsize);
WPUT(hdr.phnum);
WPUT(hdr.shentsize);
WPUT(hdr.shnum);
WPUT(hdr.shstrndx);
return ELF32HDRSIZE;
}
uint32
elfwritehdr(void)
{
if(elf64)
return elf64writehdr();
return elf32writehdr();
}
/* Taken directly from the definition document for ELF64 */
uint32
elfhash(uchar *name)
{
uint32 h = 0, g;
while (*name) {
h = (h << 4) + *name++;
if (g = h & 0xf0000000)
h ^= g >> 24;
h &= 0x0fffffff;
}
return h;
}
void
elfwritedynent(Sym *s, int tag, uint64 val)
{
if(elf64) {
adduint64(s, tag);
adduint64(s, val);
} else {
adduint32(s, tag);
adduint32(s, val);
}
}
void
elfwritedynentsym(Sym *s, int tag, Sym *t)
{
if(elf64)
adduint64(s, tag);
else
adduint32(s, tag);
addaddr(s, t);
}
void
elfwritedynentsymsize(Sym *s, int tag, Sym *t)
{
if(elf64)
adduint64(s, tag);
else
adduint32(s, tag);
addsize(s, t);
}
int
elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p)
{
int n;
interp = p;
n = strlen(interp)+1;
sh->addr = startva + resoff - n;
sh->off = resoff - n;
sh->size = n;
return n;
}
int
elfwriteinterp(vlong stridx)
{
ElfShdr *sh = nil;
int i;
for(i = 0; i < hdr.shnum; i++)
if(shdr[i]->name == stridx)
sh = shdr[i];
if(sh == nil || interp == nil)
return 0;
cseek(sh->off);
cwrite(interp, sh->size);
return sh->size;
}
// Defined in NetBSD's sys/exec_elf.h
#define ELF_NOTE_TYPE_NETBSD_TAG 1
#define ELF_NOTE_NETBSD_NAMESZ 7
#define ELF_NOTE_NETBSD_DESCSZ 4
#define ELF_NOTE_NETBSD_NAME "NetBSD\0\0"
#define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */
int
elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff)
{
int n;
n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1;
n += resoff % 4;
sh->addr = startva + resoff - n;
sh->off = resoff - n;
sh->size = n;
return n;
}
int
elfwritenetbsdsig(vlong stridx) {
ElfShdr *sh = nil;
int i;
for(i = 0; i < hdr.shnum; i++)
if(shdr[i]->name == stridx)
sh = shdr[i];
if(sh == nil)
return 0;
// Write Elf_Note header followed by NetBSD string.
cseek(sh->off);
LPUT(ELF_NOTE_NETBSD_NAMESZ);
LPUT(ELF_NOTE_NETBSD_DESCSZ);
LPUT(ELF_NOTE_TYPE_NETBSD_TAG);
cwrite(ELF_NOTE_NETBSD_NAME, 8);
LPUT(ELF_NOTE_NETBSD_VERSION);
return sh->size;
}
extern int nelfsym;
int elfverneed;
typedef struct Elfaux Elfaux;
typedef struct Elflib Elflib;
struct Elflib
{
Elflib *next;
Elfaux *aux;
char *file;
};
struct Elfaux
{
Elfaux *next;
int num;
char *vers;
};
Elfaux*
addelflib(Elflib **list, char *file, char *vers)
{
Elflib *lib;
Elfaux *aux;
for(lib=*list; lib; lib=lib->next)
if(strcmp(lib->file, file) == 0)
goto havelib;
lib = mal(sizeof *lib);
lib->next = *list;
lib->file = file;
*list = lib;
havelib:
for(aux=lib->aux; aux; aux=aux->next)
if(strcmp(aux->vers, vers) == 0)
goto haveaux;
aux = mal(sizeof *aux);
aux->next = lib->aux;
aux->vers = vers;
lib->aux = aux;
haveaux:
return aux;
}
void
elfdynhash(void)
{
Sym *s, *sy, *dynstr;
int i, j, nbucket, b, nfile;
uint32 hc, *chain, *buckets;
int nsym;
char *name;
Elfaux **need;
Elflib *needlib;
Elflib *l;
Elfaux *x;
if(!iself)
return;
nsym = nelfsym;
s = lookup(".hash", 0);
s->type = SELFROSECT;
s->reachable = 1;
i = nsym;
nbucket = 1;
while(i > 0) {
++nbucket;
i >>= 1;
}
needlib = nil;
need = malloc(nsym * sizeof need[0]);
chain = malloc(nsym * sizeof chain[0]);
buckets = malloc(nbucket * sizeof buckets[0]);
if(need == nil || chain == nil || buckets == nil) {
cursym = nil;
diag("out of memory");
errorexit();
}
memset(need, 0, nsym * sizeof need[0]);
memset(chain, 0, nsym * sizeof chain[0]);
memset(buckets, 0, nbucket * sizeof buckets[0]);
for(sy=allsym; sy!=S; sy=sy->allsym) {
if (sy->dynid <= 0)
continue;
if(sy->dynimpvers)
need[sy->dynid] = addelflib(&needlib, sy->dynimplib, sy->dynimpvers);
name = sy->dynimpname;
if(name == nil)
name = sy->name;
hc = elfhash((uchar*)name);
b = hc % nbucket;
chain[sy->dynid] = buckets[b];
buckets[b] = sy->dynid;
}
adduint32(s, nbucket);
adduint32(s, nsym);
for(i = 0; i<nbucket; i++)
adduint32(s, buckets[i]);
for(i = 0; i<nsym; i++)
adduint32(s, chain[i]);
free(chain);
free(buckets);
// version symbols
dynstr = lookup(".dynstr", 0);
s = lookup(".gnu.version_r", 0);
i = 2;
nfile = 0;
for(l=needlib; l; l=l->next) {
nfile++;
// header
adduint16(s, 1); // table version
j = 0;
for(x=l->aux; x; x=x->next)
j++;
adduint16(s, j); // aux count
adduint32(s, addstring(dynstr, l->file)); // file string offset
adduint32(s, 16); // offset from header to first aux
if(l->next)
adduint32(s, 16+j*16); // offset from this header to next
else
adduint32(s, 0);
for(x=l->aux; x; x=x->next) {
x->num = i++;
// aux struct
adduint32(s, elfhash((uchar*)x->vers)); // hash
adduint16(s, 0); // flags
adduint16(s, x->num); // other - index we refer to this by
adduint32(s, addstring(dynstr, x->vers)); // version string offset
if(x->next)
adduint32(s, 16); // offset from this aux to next
else
adduint32(s, 0);
}
}
// version references
s = lookup(".gnu.version", 0);
for(i=0; i<nsym; i++) {
if(i == 0)
adduint16(s, 0); // first entry - no symbol
else if(need[i] == nil)
adduint16(s, 1); // global
else
adduint16(s, need[i]->num);
}
free(need);
s = lookup(".dynamic", 0);
elfverneed = nfile;
if(elfverneed) {
elfwritedynentsym(s, DT_VERNEED, lookup(".gnu.version_r", 0));
elfwritedynent(s, DT_VERNEEDNUM, nfile);
elfwritedynentsym(s, DT_VERSYM, lookup(".gnu.version", 0));
}
elfwritedynent(s, DT_NULL, 0);
}
ElfPhdr*
elfphload(Segment *seg)
{
ElfPhdr *ph;
ph = newElfPhdr();
ph->type = PT_LOAD;
if(seg->rwx & 4)
ph->flags |= PF_R;
if(seg->rwx & 2)
ph->flags |= PF_W;
if(seg->rwx & 1)
ph->flags |= PF_X;
ph->vaddr = seg->vaddr;
ph->paddr = seg->vaddr;
ph->memsz = seg->len;
ph->off = seg->fileoff;
ph->filesz = seg->filelen;
ph->align = INITRND;
return ph;
}
ElfShdr*
elfshbits(Section *sect)
{
int i, off;
ElfShdr *sh;
for(i=0; i<nelfstr; i++) {
if(strcmp(sect->name, elfstr[i].s) == 0) {
off = elfstr[i].off;
goto found;
}
}
diag("cannot find elf name %s", sect->name);
errorexit();
return nil;
found:
for(i=0; i<hdr.shnum; i++) {
sh = shdr[i];
if(sh->name == off)
return sh;
}
sh = newElfShdr(off);
if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen)
sh->type = SHT_PROGBITS;
else
sh->type = SHT_NOBITS;
sh->flags = SHF_ALLOC;
if(sect->rwx & 1)
sh->flags |= SHF_EXECINSTR;
if(sect->rwx & 2)
sh->flags |= SHF_WRITE;
sh->addr = sect->vaddr;
sh->addralign = PtrSize;
sh->size = sect->len;
sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr;
return sh;
}