| // 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 = rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + rnd(ELF_NOTE_NETBSD_DESCSZ, 4); |
| 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) { |
| if(strcmp(goos, "android") != 0) |
| sh->flags |= SHF_TLS; // no TLS on android |
| 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); |
| } |