| // 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 48 | |
| 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; | |
| static char buildinfo[32]; | |
| /* | |
| 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 | |
| case '5': | |
| // we use EABI on both linux/arm and freebsd/arm. | |
| if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd) | |
| hdr.flags = 0x5000002; // has entry point, Version5 EABI | |
| // fallthrough | |
| 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* | |
| newElfShdr(vlong name) | |
| { | |
| ElfShdr *e; | |
| e = mal(sizeof *e); | |
| e->name = name; | |
| e->shnum = hdr.shnum; | |
| 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(LSym *s, int tag, uint64 val) | |
| { | |
| if(elf64) { | |
| adduint64(ctxt, s, tag); | |
| adduint64(ctxt, s, val); | |
| } else { | |
| adduint32(ctxt, s, tag); | |
| adduint32(ctxt, s, val); | |
| } | |
| } | |
| void | |
| elfwritedynentsym(LSym *s, int tag, LSym *t) | |
| { | |
| if(elf64) | |
| adduint64(ctxt, s, tag); | |
| else | |
| adduint32(ctxt, s, tag); | |
| addaddr(ctxt, s, t); | |
| } | |
| void | |
| elfwritedynentsymsize(LSym *s, int tag, LSym *t) | |
| { | |
| if(elf64) | |
| adduint64(ctxt, s, tag); | |
| else | |
| adduint32(ctxt, s, tag); | |
| addsize(ctxt, 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(void) | |
| { | |
| ElfShdr *sh; | |
| sh = elfshname(".interp"); | |
| cseek(sh->off); | |
| cwrite(interp, sh->size); | |
| return sh->size; | |
| } | |
| int | |
| elfnote(ElfShdr *sh, uint64 startva, uint64 resoff, int sz) | |
| { | |
| uint64 n; | |
| n = sizeof(Elf_Note) + sz + resoff % 4; | |
| sh->type = SHT_NOTE; | |
| sh->flags = SHF_ALLOC; | |
| sh->addralign = 4; | |
| sh->addr = startva + resoff - n; | |
| sh->off = resoff - n; | |
| sh->size = n - resoff % 4; | |
| return n; | |
| } | |
| ElfShdr * | |
| elfwritenotehdr(char *str, uint32 namesz, uint32 descsz, uint32 tag) | |
| { | |
| ElfShdr *sh; | |
| sh = elfshname(str); | |
| // Write Elf_Note header. | |
| cseek(sh->off); | |
| LPUT(namesz); | |
| LPUT(descsz); | |
| LPUT(tag); | |
| return sh; | |
| } | |
| // NetBSD Signature (as per sys/exec_elf.h) | |
| #define ELF_NOTE_NETBSD_NAMESZ 7 | |
| #define ELF_NOTE_NETBSD_DESCSZ 4 | |
| #define ELF_NOTE_NETBSD_TAG 1 | |
| #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 = ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; | |
| return elfnote(sh, startva, resoff, n); | |
| } | |
| int | |
| elfwritenetbsdsig(void) | |
| { | |
| ElfShdr *sh; | |
| // Write Elf_Note header. | |
| sh = elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG); | |
| if(sh == nil) | |
| return 0; | |
| // Followed by NetBSD string and version. | |
| cwrite(ELF_NOTE_NETBSD_NAME, ELF_NOTE_NETBSD_NAMESZ + 1); | |
| LPUT(ELF_NOTE_NETBSD_VERSION); | |
| return sh->size; | |
| } | |
| // OpenBSD Signature | |
| #define ELF_NOTE_OPENBSD_NAMESZ 8 | |
| #define ELF_NOTE_OPENBSD_DESCSZ 4 | |
| #define ELF_NOTE_OPENBSD_TAG 1 | |
| #define ELF_NOTE_OPENBSD_NAME "OpenBSD\0" | |
| #define ELF_NOTE_OPENBSD_VERSION 0 | |
| int | |
| elfopenbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) | |
| { | |
| int n; | |
| n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ; | |
| return elfnote(sh, startva, resoff, n); | |
| } | |
| int | |
| elfwriteopenbsdsig(void) | |
| { | |
| ElfShdr *sh; | |
| // Write Elf_Note header. | |
| sh = elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG); | |
| if(sh == nil) | |
| return 0; | |
| // Followed by OpenBSD string and version. | |
| cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ); | |
| LPUT(ELF_NOTE_OPENBSD_VERSION); | |
| return sh->size; | |
| } | |
| void | |
| addbuildinfo(char *val) | |
| { | |
| char *ov; | |
| int i, b, j; | |
| if(val[0] != '0' || val[1] != 'x') { | |
| fprint(2, "%s: -B argument must start with 0x: %s\n", argv0, val); | |
| exits("usage"); | |
| } | |
| ov = val; | |
| val += 2; | |
| i = 0; | |
| while(*val != '\0') { | |
| if(val[1] == '\0') { | |
| fprint(2, "%s: -B argument must have even number of digits: %s\n", argv0, ov); | |
| exits("usage"); | |
| } | |
| b = 0; | |
| for(j = 0; j < 2; j++, val++) { | |
| b *= 16; | |
| if(*val >= '0' && *val <= '9') | |
| b += *val - '0'; | |
| else if(*val >= 'a' && *val <= 'f') | |
| b += *val - 'a' + 10; | |
| else if(*val >= 'A' && *val <= 'F') | |
| b += *val - 'A' + 10; | |
| else { | |
| fprint(2, "%s: -B argument contains invalid hex digit %c: %s\n", argv0, *val, ov); | |
| exits("usage"); | |
| } | |
| } | |
| if(i >= nelem(buildinfo)) { | |
| fprint(2, "%s: -B option too long (max %d digits): %s\n", argv0, (int)nelem(buildinfo), ov); | |
| exits("usage"); | |
| } | |
| buildinfo[i++] = b; | |
| } | |
| buildinfolen = i; | |
| } | |
| // Build info note | |
| #define ELF_NOTE_BUILDINFO_NAMESZ 4 | |
| #define ELF_NOTE_BUILDINFO_TAG 3 | |
| #define ELF_NOTE_BUILDINFO_NAME "GNU\0" | |
| int | |
| elfbuildinfo(ElfShdr *sh, uint64 startva, uint64 resoff) | |
| { | |
| int n; | |
| n = ELF_NOTE_BUILDINFO_NAMESZ + rnd(buildinfolen, 4); | |
| return elfnote(sh, startva, resoff, n); | |
| } | |
| int | |
| elfwritebuildinfo(void) | |
| { | |
| ElfShdr *sh; | |
| sh = elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, buildinfolen, ELF_NOTE_BUILDINFO_TAG); | |
| if(sh == nil) | |
| return 0; | |
| cwrite(ELF_NOTE_BUILDINFO_NAME, ELF_NOTE_BUILDINFO_NAMESZ); | |
| cwrite(buildinfo, buildinfolen); | |
| cwrite("\0\0\0", rnd(buildinfolen, 4) - buildinfolen); | |
| 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) | |
| { | |
| LSym *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 = linklookup(ctxt, ".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) { | |
| ctxt->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=ctxt->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->extname; | |
| hc = elfhash((uchar*)name); | |
| b = hc % nbucket; | |
| chain[sy->dynid] = buckets[b]; | |
| buckets[b] = sy->dynid; | |
| } | |
| adduint32(ctxt, s, nbucket); | |
| adduint32(ctxt, s, nsym); | |
| for(i = 0; i<nbucket; i++) | |
| adduint32(ctxt, s, buckets[i]); | |
| for(i = 0; i<nsym; i++) | |
| adduint32(ctxt, s, chain[i]); | |
| free(chain); | |
| free(buckets); | |
| // version symbols | |
| dynstr = linklookup(ctxt, ".dynstr", 0); | |
| s = linklookup(ctxt, ".gnu.version_r", 0); | |
| i = 2; | |
| nfile = 0; | |
| for(l=needlib; l; l=l->next) { | |
| nfile++; | |
| // header | |
| adduint16(ctxt, s, 1); // table version | |
| j = 0; | |
| for(x=l->aux; x; x=x->next) | |
| j++; | |
| adduint16(ctxt, s, j); // aux count | |
| adduint32(ctxt, s, addstring(dynstr, l->file)); // file string offset | |
| adduint32(ctxt, s, 16); // offset from header to first aux | |
| if(l->next) | |
| adduint32(ctxt, s, 16+j*16); // offset from this header to next | |
| else | |
| adduint32(ctxt, s, 0); | |
| for(x=l->aux; x; x=x->next) { | |
| x->num = i++; | |
| // aux struct | |
| adduint32(ctxt, s, elfhash((uchar*)x->vers)); // hash | |
| adduint16(ctxt, s, 0); // flags | |
| adduint16(ctxt, s, x->num); // other - index we refer to this by | |
| adduint32(ctxt, s, addstring(dynstr, x->vers)); // version string offset | |
| if(x->next) | |
| adduint32(ctxt, s, 16); // offset from this aux to next | |
| else | |
| adduint32(ctxt, s, 0); | |
| } | |
| } | |
| // version references | |
| s = linklookup(ctxt, ".gnu.version", 0); | |
| for(i=0; i<nsym; i++) { | |
| if(i == 0) | |
| adduint16(ctxt, s, 0); // first entry - no symbol | |
| else if(need[i] == nil) | |
| adduint16(ctxt, s, 1); // global | |
| else | |
| adduint16(ctxt, s, need[i]->num); | |
| } | |
| free(need); | |
| s = linklookup(ctxt, ".dynamic", 0); | |
| elfverneed = nfile; | |
| if(elfverneed) { | |
| elfwritedynentsym(s, DT_VERNEED, linklookup(ctxt, ".gnu.version_r", 0)); | |
| elfwritedynent(s, DT_VERNEEDNUM, nfile); | |
| elfwritedynentsym(s, DT_VERSYM, linklookup(ctxt, ".gnu.version", 0)); | |
| } | |
| if(thechar == '6') { | |
| sy = linklookup(ctxt, ".rela.plt", 0); | |
| if(sy->size > 0) { | |
| elfwritedynent(s, DT_PLTREL, DT_RELA); | |
| elfwritedynentsymsize(s, DT_PLTRELSZ, sy); | |
| elfwritedynentsym(s, DT_JMPREL, sy); | |
| } | |
| } else { | |
| sy = linklookup(ctxt, ".rel.plt", 0); | |
| if(sy->size > 0) { | |
| elfwritedynent(s, DT_PLTREL, DT_REL); | |
| elfwritedynentsymsize(s, DT_PLTRELSZ, sy); | |
| elfwritedynentsym(s, DT_JMPREL, sy); | |
| } | |
| } | |
| 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* | |
| elfshname(char *name) | |
| { | |
| int i, off; | |
| ElfShdr *sh; | |
| for(i=0; i<nelfstr; i++) { | |
| if(strcmp(name, elfstr[i].s) == 0) { | |
| off = elfstr[i].off; | |
| goto found; | |
| } | |
| } | |
| diag("cannot find elf name %s", name); | |
| errorexit(); | |
| return nil; | |
| found: | |
| for(i=0; i<hdr.shnum; i++) { | |
| sh = shdr[i]; | |
| if(sh->name == off) | |
| return sh; | |
| } | |
| sh = newElfShdr(off); | |
| return sh; | |
| } | |
| ElfShdr* | |
| elfshalloc(Section *sect) | |
| { | |
| ElfShdr *sh; | |
| sh = elfshname(sect->name); | |
| sect->elfsect = sh; | |
| return sh; | |
| } | |
| ElfShdr* | |
| elfshbits(Section *sect) | |
| { | |
| ElfShdr *sh; | |
| sh = elfshalloc(sect); | |
| if(sh->type > 0) | |
| return sh; | |
| 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; | |
| if(strcmp(sect->name, ".tbss") == 0) { | |
| sh->flags |= SHF_TLS; | |
| sh->type = SHT_NOBITS; | |
| } | |
| if(linkmode != LinkExternal) | |
| sh->addr = sect->vaddr; | |
| sh->addralign = sect->align; | |
| sh->size = sect->len; | |
| sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; | |
| return sh; | |
| } | |
| ElfShdr* | |
| elfshreloc(Section *sect) | |
| { | |
| int typ; | |
| ElfShdr *sh; | |
| char *prefix; | |
| char buf[100]; | |
| // If main section is SHT_NOBITS, nothing to relocate. | |
| // Also nothing to relocate in .shstrtab. | |
| if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) | |
| return nil; | |
| if(strcmp(sect->name, ".shstrtab") == 0 || strcmp(sect->name, ".tbss") == 0) | |
| return nil; | |
| if(thechar == '6') { | |
| prefix = ".rela"; | |
| typ = SHT_RELA; | |
| } else { | |
| prefix = ".rel"; | |
| typ = SHT_REL; | |
| } | |
| snprint(buf, sizeof buf, "%s%s", prefix, sect->name); | |
| sh = elfshname(buf); | |
| sh->type = typ; | |
| sh->entsize = RegSize*(2+(typ==SHT_RELA)); | |
| sh->link = elfshname(".symtab")->shnum; | |
| sh->info = sect->elfsect->shnum; | |
| sh->off = sect->reloff; | |
| sh->size = sect->rellen; | |
| sh->addralign = RegSize; | |
| return sh; | |
| } | |
| void | |
| elfrelocsect(Section *sect, LSym *first) | |
| { | |
| LSym *sym; | |
| int32 eaddr; | |
| Reloc *r; | |
| // If main section is SHT_NOBITS, nothing to relocate. | |
| // Also nothing to relocate in .shstrtab. | |
| if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) | |
| return; | |
| if(strcmp(sect->name, ".shstrtab") == 0) | |
| return; | |
| sect->reloff = cpos(); | |
| for(sym = first; sym != nil; sym = sym->next) { | |
| if(!sym->reachable) | |
| continue; | |
| if(sym->value >= sect->vaddr) | |
| break; | |
| } | |
| eaddr = sect->vaddr + sect->len; | |
| for(; sym != nil; sym = sym->next) { | |
| if(!sym->reachable) | |
| continue; | |
| if(sym->value >= eaddr) | |
| break; | |
| ctxt->cursym = sym; | |
| for(r = sym->r; r < sym->r+sym->nr; r++) { | |
| if(r->done) | |
| continue; | |
| if(r->xsym == nil) { | |
| diag("missing xsym in relocation"); | |
| continue; | |
| } | |
| if(r->xsym->elfsym == 0) | |
| diag("reloc %d to non-elf symbol %s (outer=%s) %d", r->type, r->sym->name, r->xsym->name, r->sym->type); | |
| if(elfreloc1(r, sym->value+r->off - sect->vaddr) < 0) | |
| diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); | |
| } | |
| } | |
| sect->rellen = cpos() - sect->reloff; | |
| } | |
| void | |
| elfemitreloc(void) | |
| { | |
| Section *sect; | |
| while(cpos()&7) | |
| cput(0); | |
| elfrelocsect(segtext.sect, ctxt->textp); | |
| for(sect=segtext.sect->next; sect!=nil; sect=sect->next) | |
| elfrelocsect(sect, datap); | |
| for(sect=segrodata.sect; sect!=nil; sect=sect->next) | |
| elfrelocsect(sect, datap); | |
| for(sect=segdata.sect; sect!=nil; sect=sect->next) | |
| elfrelocsect(sect, datap); | |
| } | |
| void | |
| doelf(void) | |
| { | |
| LSym *s, *shstrtab, *dynstr; | |
| if(!iself) | |
| return; | |
| /* predefine strings we need for section headers */ | |
| shstrtab = linklookup(ctxt, ".shstrtab", 0); | |
| shstrtab->type = SELFROSECT; | |
| shstrtab->reachable = 1; | |
| addstring(shstrtab, ""); | |
| addstring(shstrtab, ".text"); | |
| addstring(shstrtab, ".noptrdata"); | |
| addstring(shstrtab, ".data"); | |
| addstring(shstrtab, ".bss"); | |
| addstring(shstrtab, ".noptrbss"); | |
| // generate .tbss section (except for OpenBSD where it's not supported) | |
| // for dynamic internal linker or external linking, so that various | |
| // binutils could correctly calculate PT_TLS size. | |
| // see http://golang.org/issue/5200. | |
| if(HEADTYPE != Hopenbsd) | |
| if(!debug['d'] || linkmode == LinkExternal) | |
| addstring(shstrtab, ".tbss"); | |
| if(HEADTYPE == Hnetbsd) | |
| addstring(shstrtab, ".note.netbsd.ident"); | |
| if(HEADTYPE == Hopenbsd) | |
| addstring(shstrtab, ".note.openbsd.ident"); | |
| if(buildinfolen > 0) | |
| addstring(shstrtab, ".note.gnu.build-id"); | |
| addstring(shstrtab, ".elfdata"); | |
| addstring(shstrtab, ".rodata"); | |
| addstring(shstrtab, ".typelink"); | |
| addstring(shstrtab, ".gosymtab"); | |
| addstring(shstrtab, ".gopclntab"); | |
| if(linkmode == LinkExternal) { | |
| debug_s = debug['s']; | |
| debug['s'] = 0; | |
| debug['d'] = 1; | |
| if(thechar == '6') { | |
| addstring(shstrtab, ".rela.text"); | |
| addstring(shstrtab, ".rela.rodata"); | |
| addstring(shstrtab, ".rela.typelink"); | |
| addstring(shstrtab, ".rela.gosymtab"); | |
| addstring(shstrtab, ".rela.gopclntab"); | |
| addstring(shstrtab, ".rela.noptrdata"); | |
| addstring(shstrtab, ".rela.data"); | |
| } else { | |
| addstring(shstrtab, ".rel.text"); | |
| addstring(shstrtab, ".rel.rodata"); | |
| addstring(shstrtab, ".rel.typelink"); | |
| addstring(shstrtab, ".rel.gosymtab"); | |
| addstring(shstrtab, ".rel.gopclntab"); | |
| addstring(shstrtab, ".rel.noptrdata"); | |
| addstring(shstrtab, ".rel.data"); | |
| } | |
| // add a .note.GNU-stack section to mark the stack as non-executable | |
| addstring(shstrtab, ".note.GNU-stack"); | |
| } | |
| if(flag_shared) { | |
| addstring(shstrtab, ".init_array"); | |
| if(thechar == '6') | |
| addstring(shstrtab, ".rela.init_array"); | |
| else | |
| addstring(shstrtab, ".rel.init_array"); | |
| } | |
| if(!debug['s']) { | |
| addstring(shstrtab, ".symtab"); | |
| addstring(shstrtab, ".strtab"); | |
| dwarfaddshstrings(shstrtab); | |
| } | |
| addstring(shstrtab, ".shstrtab"); | |
| if(!debug['d']) { /* -d suppresses dynamic loader format */ | |
| addstring(shstrtab, ".interp"); | |
| addstring(shstrtab, ".hash"); | |
| addstring(shstrtab, ".got"); | |
| addstring(shstrtab, ".got.plt"); | |
| addstring(shstrtab, ".dynamic"); | |
| addstring(shstrtab, ".dynsym"); | |
| addstring(shstrtab, ".dynstr"); | |
| if(thechar == '6') { | |
| addstring(shstrtab, ".rela"); | |
| addstring(shstrtab, ".rela.plt"); | |
| } else { | |
| addstring(shstrtab, ".rel"); | |
| addstring(shstrtab, ".rel.plt"); | |
| } | |
| addstring(shstrtab, ".plt"); | |
| addstring(shstrtab, ".gnu.version"); | |
| addstring(shstrtab, ".gnu.version_r"); | |
| /* dynamic symbol table - first entry all zeros */ | |
| s = linklookup(ctxt, ".dynsym", 0); | |
| s->type = SELFROSECT; | |
| s->reachable = 1; | |
| if(thechar == '6') | |
| s->size += ELF64SYMSIZE; | |
| else | |
| s->size += ELF32SYMSIZE; | |
| /* dynamic string table */ | |
| s = linklookup(ctxt, ".dynstr", 0); | |
| s->type = SELFROSECT; | |
| s->reachable = 1; | |
| if(s->size == 0) | |
| addstring(s, ""); | |
| dynstr = s; | |
| /* relocation table */ | |
| if(thechar == '6') | |
| s = linklookup(ctxt, ".rela", 0); | |
| else | |
| s = linklookup(ctxt, ".rel", 0); | |
| s->reachable = 1; | |
| s->type = SELFROSECT; | |
| /* global offset table */ | |
| s = linklookup(ctxt, ".got", 0); | |
| s->reachable = 1; | |
| s->type = SELFSECT; // writable | |
| /* hash */ | |
| s = linklookup(ctxt, ".hash", 0); | |
| s->reachable = 1; | |
| s->type = SELFROSECT; | |
| s = linklookup(ctxt, ".got.plt", 0); | |
| s->reachable = 1; | |
| s->type = SELFSECT; // writable | |
| s = linklookup(ctxt, ".plt", 0); | |
| s->reachable = 1; | |
| s->type = SELFRXSECT; | |
| elfsetupplt(); | |
| if(thechar == '6') | |
| s = linklookup(ctxt, ".rela.plt", 0); | |
| else | |
| s = linklookup(ctxt, ".rel.plt", 0); | |
| s->reachable = 1; | |
| s->type = SELFROSECT; | |
| s = linklookup(ctxt, ".gnu.version", 0); | |
| s->reachable = 1; | |
| s->type = SELFROSECT; | |
| s = linklookup(ctxt, ".gnu.version_r", 0); | |
| s->reachable = 1; | |
| s->type = SELFROSECT; | |
| /* define dynamic elf table */ | |
| s = linklookup(ctxt, ".dynamic", 0); | |
| s->reachable = 1; | |
| s->type = SELFSECT; // writable | |
| /* | |
| * .dynamic table | |
| */ | |
| elfwritedynentsym(s, DT_HASH, linklookup(ctxt, ".hash", 0)); | |
| elfwritedynentsym(s, DT_SYMTAB, linklookup(ctxt, ".dynsym", 0)); | |
| if(thechar == '6') | |
| elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); | |
| else | |
| elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); | |
| elfwritedynentsym(s, DT_STRTAB, linklookup(ctxt, ".dynstr", 0)); | |
| elfwritedynentsymsize(s, DT_STRSZ, linklookup(ctxt, ".dynstr", 0)); | |
| if(thechar == '6') { | |
| elfwritedynentsym(s, DT_RELA, linklookup(ctxt, ".rela", 0)); | |
| elfwritedynentsymsize(s, DT_RELASZ, linklookup(ctxt, ".rela", 0)); | |
| elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); | |
| } else { | |
| elfwritedynentsym(s, DT_REL, linklookup(ctxt, ".rel", 0)); | |
| elfwritedynentsymsize(s, DT_RELSZ, linklookup(ctxt, ".rel", 0)); | |
| elfwritedynent(s, DT_RELENT, ELF32RELSIZE); | |
| } | |
| if(rpath) | |
| elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); | |
| elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); | |
| // Solaris dynamic linker can't handle an empty .rela.plt if | |
| // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, | |
| // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the | |
| // size of .rel(a).plt section. | |
| elfwritedynent(s, DT_DEBUG, 0); | |
| // Do not write DT_NULL. elfdynhash will finish it. | |
| } | |
| } | |
| void | |
| shsym(ElfShdr *sh, LSym *s) | |
| { | |
| vlong addr; | |
| addr = symaddr(s); | |
| if(sh->flags&SHF_ALLOC) | |
| sh->addr = addr; | |
| sh->off = datoff(addr); | |
| sh->size = s->size; | |
| } | |
| void | |
| phsh(ElfPhdr *ph, ElfShdr *sh) | |
| { | |
| ph->vaddr = sh->addr; | |
| ph->paddr = ph->vaddr; | |
| ph->off = sh->off; | |
| ph->filesz = sh->size; | |
| ph->memsz = sh->size; | |
| ph->align = sh->addralign; | |
| } | |
| void | |
| asmbelfsetup(void) | |
| { | |
| Section *sect; | |
| /* This null SHdr must appear before all others */ | |
| elfshname(""); | |
| for(sect=segtext.sect; sect!=nil; sect=sect->next) | |
| elfshalloc(sect); | |
| for(sect=segrodata.sect; sect!=nil; sect=sect->next) | |
| elfshalloc(sect); | |
| for(sect=segdata.sect; sect!=nil; sect=sect->next) | |
| elfshalloc(sect); | |
| } | |
| void | |
| asmbelf(vlong symo) | |
| { | |
| vlong a, o; | |
| vlong startva, resoff; | |
| ElfEhdr *eh; | |
| ElfPhdr *ph, *pph, *pnote; | |
| ElfShdr *sh; | |
| Section *sect; | |
| eh = getElfEhdr(); | |
| switch(thechar) { | |
| default: | |
| diag("unknown architecture in asmbelf"); | |
| errorexit(); | |
| case '5': | |
| eh->machine = EM_ARM; | |
| break; | |
| case '6': | |
| eh->machine = EM_X86_64; | |
| break; | |
| case '8': | |
| eh->machine = EM_386; | |
| break; | |
| } | |
| startva = INITTEXT - HEADR; | |
| resoff = ELFRESERVE; | |
| pph = nil; | |
| if(linkmode == LinkExternal) { | |
| /* skip program headers */ | |
| eh->phoff = 0; | |
| eh->phentsize = 0; | |
| goto elfobj; | |
| } | |
| /* program header info */ | |
| pph = newElfPhdr(); | |
| pph->type = PT_PHDR; | |
| pph->flags = PF_R; | |
| pph->off = eh->ehsize; | |
| pph->vaddr = INITTEXT - HEADR + pph->off; | |
| pph->paddr = INITTEXT - HEADR + pph->off; | |
| pph->align = INITRND; | |
| /* | |
| * PHDR must be in a loaded segment. Adjust the text | |
| * segment boundaries downwards to include it. | |
| * Except on NaCl where it must not be loaded. | |
| */ | |
| if(HEADTYPE != Hnacl) { | |
| o = segtext.vaddr - pph->vaddr; | |
| segtext.vaddr -= o; | |
| segtext.len += o; | |
| o = segtext.fileoff - pph->off; | |
| segtext.fileoff -= o; | |
| segtext.filelen += o; | |
| } | |
| if(!debug['d']) { | |
| /* interpreter */ | |
| sh = elfshname(".interp"); | |
| sh->type = SHT_PROGBITS; | |
| sh->flags = SHF_ALLOC; | |
| sh->addralign = 1; | |
| if(interpreter == nil) { | |
| switch(HEADTYPE) { | |
| case Hlinux: | |
| interpreter = linuxdynld; | |
| break; | |
| case Hfreebsd: | |
| interpreter = freebsddynld; | |
| break; | |
| case Hnetbsd: | |
| interpreter = netbsddynld; | |
| break; | |
| case Hopenbsd: | |
| interpreter = openbsddynld; | |
| break; | |
| case Hdragonfly: | |
| interpreter = dragonflydynld; | |
| break; | |
| case Hsolaris: | |
| interpreter = solarisdynld; | |
| break; | |
| } | |
| } | |
| resoff -= elfinterp(sh, startva, resoff, interpreter); | |
| ph = newElfPhdr(); | |
| ph->type = PT_INTERP; | |
| ph->flags = PF_R; | |
| phsh(ph, sh); | |
| } | |
| pnote = nil; | |
| if(HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd) { | |
| sh = nil; | |
| switch(HEADTYPE) { | |
| case Hnetbsd: | |
| sh = elfshname(".note.netbsd.ident"); | |
| resoff -= elfnetbsdsig(sh, startva, resoff); | |
| break; | |
| case Hopenbsd: | |
| sh = elfshname(".note.openbsd.ident"); | |
| resoff -= elfopenbsdsig(sh, startva, resoff); | |
| break; | |
| } | |
| pnote = newElfPhdr(); | |
| pnote->type = PT_NOTE; | |
| pnote->flags = PF_R; | |
| phsh(pnote, sh); | |
| } | |
| if(buildinfolen > 0) { | |
| sh = elfshname(".note.gnu.build-id"); | |
| resoff -= elfbuildinfo(sh, startva, resoff); | |
| if(pnote == nil) { | |
| pnote = newElfPhdr(); | |
| pnote->type = PT_NOTE; | |
| pnote->flags = PF_R; | |
| } | |
| phsh(pnote, sh); | |
| } | |
| // Additions to the reserved area must be above this line. | |
| USED(resoff); | |
| elfphload(&segtext); | |
| if(segrodata.sect != nil) | |
| elfphload(&segrodata); | |
| elfphload(&segdata); | |
| /* Dynamic linking sections */ | |
| if(!debug['d']) { /* -d suppresses dynamic loader format */ | |
| sh = elfshname(".dynsym"); | |
| sh->type = SHT_DYNSYM; | |
| sh->flags = SHF_ALLOC; | |
| if(elf64) | |
| sh->entsize = ELF64SYMSIZE; | |
| else | |
| sh->entsize = ELF32SYMSIZE; | |
| sh->addralign = RegSize; | |
| sh->link = elfshname(".dynstr")->shnum; | |
| // sh->info = index of first non-local symbol (number of local symbols) | |
| shsym(sh, linklookup(ctxt, ".dynsym", 0)); | |
| sh = elfshname(".dynstr"); | |
| sh->type = SHT_STRTAB; | |
| sh->flags = SHF_ALLOC; | |
| sh->addralign = 1; | |
| shsym(sh, linklookup(ctxt, ".dynstr", 0)); | |
| if(elfverneed) { | |
| sh = elfshname(".gnu.version"); | |
| sh->type = SHT_GNU_VERSYM; | |
| sh->flags = SHF_ALLOC; | |
| sh->addralign = 2; | |
| sh->link = elfshname(".dynsym")->shnum; | |
| sh->entsize = 2; | |
| shsym(sh, linklookup(ctxt, ".gnu.version", 0)); | |
| sh = elfshname(".gnu.version_r"); | |
| sh->type = SHT_GNU_VERNEED; | |
| sh->flags = SHF_ALLOC; | |
| sh->addralign = RegSize; | |
| sh->info = elfverneed; | |
| sh->link = elfshname(".dynstr")->shnum; | |
| shsym(sh, linklookup(ctxt, ".gnu.version_r", 0)); | |
| } | |
| switch(eh->machine) { | |
| case EM_X86_64: | |
| sh = elfshname(".rela.plt"); | |
| sh->type = SHT_RELA; | |
| sh->flags = SHF_ALLOC; | |
| sh->entsize = ELF64RELASIZE; | |
| sh->addralign = RegSize; | |
| sh->link = elfshname(".dynsym")->shnum; | |
| sh->info = elfshname(".plt")->shnum; | |
| shsym(sh, linklookup(ctxt, ".rela.plt", 0)); | |
| sh = elfshname(".rela"); | |
| sh->type = SHT_RELA; | |
| sh->flags = SHF_ALLOC; | |
| sh->entsize = ELF64RELASIZE; | |
| sh->addralign = 8; | |
| sh->link = elfshname(".dynsym")->shnum; | |
| shsym(sh, linklookup(ctxt, ".rela", 0)); | |
| break; | |
| default: | |
| sh = elfshname(".rel.plt"); | |
| sh->type = SHT_REL; | |
| sh->flags = SHF_ALLOC; | |
| sh->entsize = ELF32RELSIZE; | |
| sh->link = elfshname(".dynsym")->shnum; | |
| shsym(sh, linklookup(ctxt, ".rel.plt", 0)); | |
| sh = elfshname(".rel"); | |
| sh->type = SHT_REL; | |
| sh->flags = SHF_ALLOC; | |
| sh->entsize = ELF32RELSIZE; | |
| sh->addralign = 4; | |
| sh->link = elfshname(".dynsym")->shnum; | |
| shsym(sh, linklookup(ctxt, ".rel", 0)); | |
| break; | |
| } | |
| sh = elfshname(".plt"); | |
| sh->type = SHT_PROGBITS; | |
| sh->flags = SHF_ALLOC+SHF_EXECINSTR; | |
| if(eh->machine == EM_X86_64) | |
| sh->entsize = 16; | |
| else | |
| sh->entsize = 4; | |
| sh->addralign = 4; | |
| shsym(sh, linklookup(ctxt, ".plt", 0)); | |
| sh = elfshname(".got"); | |
| sh->type = SHT_PROGBITS; | |
| sh->flags = SHF_ALLOC+SHF_WRITE; | |
| sh->entsize = RegSize; | |
| sh->addralign = RegSize; | |
| shsym(sh, linklookup(ctxt, ".got", 0)); | |
| sh = elfshname(".got.plt"); | |
| sh->type = SHT_PROGBITS; | |
| sh->flags = SHF_ALLOC+SHF_WRITE; | |
| sh->entsize = RegSize; | |
| sh->addralign = RegSize; | |
| shsym(sh, linklookup(ctxt, ".got.plt", 0)); | |
| sh = elfshname(".hash"); | |
| sh->type = SHT_HASH; | |
| sh->flags = SHF_ALLOC; | |
| sh->entsize = 4; | |
| sh->addralign = RegSize; | |
| sh->link = elfshname(".dynsym")->shnum; | |
| shsym(sh, linklookup(ctxt, ".hash", 0)); | |
| /* sh and PT_DYNAMIC for .dynamic section */ | |
| sh = elfshname(".dynamic"); | |
| sh->type = SHT_DYNAMIC; | |
| sh->flags = SHF_ALLOC+SHF_WRITE; | |
| sh->entsize = 2*RegSize; | |
| sh->addralign = RegSize; | |
| sh->link = elfshname(".dynstr")->shnum; | |
| shsym(sh, linklookup(ctxt, ".dynamic", 0)); | |
| ph = newElfPhdr(); | |
| ph->type = PT_DYNAMIC; | |
| ph->flags = PF_R + PF_W; | |
| phsh(ph, sh); | |
| /* | |
| * Thread-local storage segment (really just size). | |
| */ | |
| // Do not emit PT_TLS for OpenBSD since ld.so(1) does | |
| // not currently support it. This is handled | |
| // appropriately in runtime/cgo. | |
| if(ctxt->tlsoffset != 0 && HEADTYPE != Hopenbsd) { | |
| ph = newElfPhdr(); | |
| ph->type = PT_TLS; | |
| ph->flags = PF_R; | |
| ph->memsz = -ctxt->tlsoffset; | |
| ph->align = RegSize; | |
| } | |
| } | |
| if(HEADTYPE == Hlinux) { | |
| ph = newElfPhdr(); | |
| ph->type = PT_GNU_STACK; | |
| ph->flags = PF_W+PF_R; | |
| ph->align = RegSize; | |
| ph = newElfPhdr(); | |
| ph->type = PT_PAX_FLAGS; | |
| ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled | |
| ph->align = RegSize; | |
| } | |
| elfobj: | |
| sh = elfshname(".shstrtab"); | |
| sh->type = SHT_STRTAB; | |
| sh->addralign = 1; | |
| shsym(sh, linklookup(ctxt, ".shstrtab", 0)); | |
| eh->shstrndx = sh->shnum; | |
| // put these sections early in the list | |
| if(!debug['s']) { | |
| elfshname(".symtab"); | |
| elfshname(".strtab"); | |
| } | |
| for(sect=segtext.sect; sect!=nil; sect=sect->next) | |
| elfshbits(sect); | |
| for(sect=segrodata.sect; sect!=nil; sect=sect->next) | |
| elfshbits(sect); | |
| for(sect=segdata.sect; sect!=nil; sect=sect->next) | |
| elfshbits(sect); | |
| if(linkmode == LinkExternal) { | |
| for(sect=segtext.sect; sect!=nil; sect=sect->next) | |
| elfshreloc(sect); | |
| for(sect=segrodata.sect; sect!=nil; sect=sect->next) | |
| elfshreloc(sect); | |
| for(sect=segdata.sect; sect!=nil; sect=sect->next) | |
| elfshreloc(sect); | |
| // add a .note.GNU-stack section to mark the stack as non-executable | |
| sh = elfshname(".note.GNU-stack"); | |
| sh->type = SHT_PROGBITS; | |
| sh->addralign = 1; | |
| sh->flags = 0; | |
| } | |
| // generate .tbss section for dynamic internal linking (except for OpenBSD) | |
| // external linking generates .tbss in data.c | |
| if(linkmode == LinkInternal && !debug['d'] && HEADTYPE != Hopenbsd) { | |
| sh = elfshname(".tbss"); | |
| sh->type = SHT_NOBITS; | |
| sh->addralign = RegSize; | |
| sh->size = -ctxt->tlsoffset; | |
| sh->flags = SHF_ALLOC | SHF_TLS | SHF_WRITE; | |
| } | |
| if(!debug['s']) { | |
| sh = elfshname(".symtab"); | |
| sh->type = SHT_SYMTAB; | |
| sh->off = symo; | |
| sh->size = symsize; | |
| sh->addralign = RegSize; | |
| sh->entsize = 8+2*RegSize; | |
| sh->link = elfshname(".strtab")->shnum; | |
| sh->info = elfglobalsymndx; | |
| sh = elfshname(".strtab"); | |
| sh->type = SHT_STRTAB; | |
| sh->off = symo+symsize; | |
| sh->size = elfstrsize; | |
| sh->addralign = 1; | |
| dwarfaddelfheaders(); | |
| } | |
| /* Main header */ | |
| eh->ident[EI_MAG0] = '\177'; | |
| eh->ident[EI_MAG1] = 'E'; | |
| eh->ident[EI_MAG2] = 'L'; | |
| eh->ident[EI_MAG3] = 'F'; | |
| if(HEADTYPE == Hfreebsd) | |
| eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; | |
| else if(HEADTYPE == Hnetbsd) | |
| eh->ident[EI_OSABI] = ELFOSABI_NETBSD; | |
| else if(HEADTYPE == Hopenbsd) | |
| eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; | |
| else if(HEADTYPE == Hdragonfly) | |
| eh->ident[EI_OSABI] = ELFOSABI_NONE; | |
| if(elf64) | |
| eh->ident[EI_CLASS] = ELFCLASS64; | |
| else | |
| eh->ident[EI_CLASS] = ELFCLASS32; | |
| eh->ident[EI_DATA] = ELFDATA2LSB; | |
| eh->ident[EI_VERSION] = EV_CURRENT; | |
| if(linkmode == LinkExternal) | |
| eh->type = ET_REL; | |
| else | |
| eh->type = ET_EXEC; | |
| if(linkmode != LinkExternal) | |
| eh->entry = entryvalue(); | |
| eh->version = EV_CURRENT; | |
| if(pph != nil) { | |
| pph->filesz = eh->phnum * eh->phentsize; | |
| pph->memsz = pph->filesz; | |
| } | |
| cseek(0); | |
| a = 0; | |
| a += elfwritehdr(); | |
| a += elfwritephdrs(); | |
| a += elfwriteshdrs(); | |
| if(!debug['d']) | |
| a += elfwriteinterp(); | |
| if(linkmode != LinkExternal) { | |
| if(HEADTYPE == Hnetbsd) | |
| a += elfwritenetbsdsig(); | |
| if(HEADTYPE == Hopenbsd) | |
| a += elfwriteopenbsdsig(); | |
| if(buildinfolen > 0) | |
| a += elfwritebuildinfo(); | |
| } | |
| if(a > ELFRESERVE) | |
| diag("ELFRESERVE too small: %lld > %d", a, ELFRESERVE); | |
| } |