diff options
| author | Austin Clements <austin@google.com> | 2014-12-16 14:59:59 -0500 |
|---|---|---|
| committer | Austin Clements <austin@google.com> | 2015-01-07 20:35:54 +0000 |
| commit | db923390a01d4c992116161bed1328bd5bb32a24 (patch) | |
| tree | 54daaefb538fec7d954849b2ab2c3659b2ad785b /src/cmd/ld | |
| parent | ac5a1ac318efb7890b25cf614a0cd5b3e52c74e3 (diff) | |
| download | go-db923390a01d4c992116161bed1328bd5bb32a24.tar.xz | |
cmd/9l: support internal linking
This implements the ELF relocations and dynamic linking tables
necessary to support internal linking on ppc64. It also marks ppc64le
ELF files as ABI v2; failing to do this doesn't seem to confuse the
loader, but it does confuse libbfd (and hence gdb, objdump, etc).
Change-Id: I559dddf89b39052e1b6288a4dd5e72693b5355e4
Reviewed-on: https://go-review.googlesource.com/2006
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/cmd/ld')
| -rw-r--r-- | src/cmd/ld/data.c | 37 | ||||
| -rw-r--r-- | src/cmd/ld/elf.c | 83 | ||||
| -rw-r--r-- | src/cmd/ld/elf.h | 16 | ||||
| -rw-r--r-- | src/cmd/ld/ldelf.c | 29 |
4 files changed, 143 insertions, 22 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 48e8a58866..22843b8948 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -49,7 +49,11 @@ datcmp(LSym *s1, LSym *s2) { if(s1->type != s2->type) return (int)s1->type - (int)s2->type; - if(s1->size != s2->size) { + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type SELFGOT, so in that case + // fall through to the name comparison (conveniently, .got + // sorts before .toc). + if(s1->type != SELFGOT && s1->size != s2->size) { if(s1->size < s2->size) return -1; return +1; @@ -920,7 +924,7 @@ dodata(void) vlong datsize; Section *sect; Segment *segro; - LSym *s, *last, **l; + LSym *s, *last, **l, *toc; LSym *gcdata, *gcbss; ProgGen gen; @@ -994,7 +998,7 @@ dodata(void) /* writable ELF sections */ datsize = 0; - for(; s != nil && s->type < SNOPTRDATA; s = s->next) { + for(; s != nil && s->type < SELFGOT; s = s->next) { sect = addsection(&segdata, s->name, 06); sect->align = symalign(s); datsize = rnd(datsize, sect->align); @@ -1006,6 +1010,33 @@ dodata(void) sect->len = datsize - sect->vaddr; } + /* .got (and .toc on ppc64) */ + if(s->type == SELFGOT) { + sect = addsection(&segdata, ".got", 06); + sect->align = maxalign(s, SELFGOT); + datsize = rnd(datsize, sect->align); + sect->vaddr = datsize; + for(; s != nil && s->type == SELFGOT; s = s->next) { + datsize = aligndatsize(datsize, s); + s->sect = sect; + s->type = SDATA; + s->value = datsize - sect->vaddr; + + // Resolve .TOC. symbol for this object file (ppc64) + toc = linkrlookup(ctxt, ".TOC.", s->version); + if(toc != nil) { + toc->sect = sect; + toc->outer = s; + toc->sub = s->sub; + s->sub = toc; + + toc->value = 0x8000; + } + growdatsize(&datsize, s); + } + sect->len = datsize - sect->vaddr; + } + /* pointer-free data */ sect = addsection(&segdata, ".noptrdata", 06); sect->align = maxalign(s, SINITARR-1); diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index b17d66890f..89a0a5e87f 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -307,11 +307,17 @@ elfwritedynent(LSym *s, int tag, uint64 val) void elfwritedynentsym(LSym *s, int tag, LSym *t) { + elfwritedynentsymplus(s, tag, t, 0); +} + +void +elfwritedynentsymplus(LSym *s, int tag, LSym *t, vlong add) +{ if(elf64) adduint64(ctxt, s, tag); else adduint32(ctxt, s, tag); - addaddr(ctxt, s, t); + addaddrplus(ctxt, s, t, add); } void @@ -977,6 +983,8 @@ doelf(void) addstring(shstrtab, ".interp"); addstring(shstrtab, ".hash"); addstring(shstrtab, ".got"); + if(thechar == '9') + addstring(shstrtab, ".glink"); addstring(shstrtab, ".got.plt"); addstring(shstrtab, ".dynamic"); addstring(shstrtab, ".dynsym"); @@ -1020,7 +1028,14 @@ doelf(void) /* global offset table */ s = linklookup(ctxt, ".got", 0); s->reachable = 1; - s->type = SELFSECT; // writable + s->type = SELFGOT; // writable + + /* ppc64 glink resolver */ + if(thechar == '9') { + s = linklookup(ctxt, ".glink", 0); + s->reachable = 1; + s->type = SELFRXSECT; + } /* hash */ s = linklookup(ctxt, ".hash", 0); @@ -1033,7 +1048,12 @@ doelf(void) s = linklookup(ctxt, ".plt", 0); s->reachable = 1; - s->type = SELFRXSECT; + if(thechar == '9') + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + s->type = SELFSECT; + else + s->type = SELFRXSECT; elfsetupplt(); @@ -1079,8 +1099,14 @@ doelf(void) } if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - - elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); + + if(thechar == '9') + elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".plt", 0)); + else + elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); + + if(thechar == '9') + elfwritedynent(s, DT_PPC64_OPT, 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, @@ -1309,6 +1335,7 @@ asmbelf(vlong symo) switch(eh->machine) { case EM_X86_64: + case EM_PPC64: sh = elfshname(".rela.plt"); sh->type = SHT_RELA; sh->flags = SHF_ALLOC; @@ -1345,29 +1372,47 @@ asmbelf(vlong symo) break; } + if(eh->machine == EM_PPC64) { + sh = elfshname(".glink"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->addralign = 4; + shsym(sh, linklookup(ctxt, ".glink", 0)); + } + sh = elfshname(".plt"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_EXECINSTR; if(eh->machine == EM_X86_64) sh->entsize = 16; - else + else if(eh->machine == EM_PPC64) { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh->type = SHT_NOBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 8; + } else sh->entsize = 4; - sh->addralign = 4; + sh->addralign = sh->entsize; 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)); + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if(eh->machine != EM_PPC64) { + 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(".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; diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index e84d996f25..16c052fcb5 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -317,6 +317,9 @@ typedef struct { #define DT_VERNEEDNUM 0x6fffffff #define DT_VERSYM 0x6ffffff0 +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPT (DT_LOPROC + 3) + /* Values for DT_FLAGS */ /* Indicates that the object being loaded may make reference to the $ORIGIN substitution string */ @@ -700,6 +703,18 @@ typedef struct { /* Count of defined relocation types. */ #define R_PPC_EMB_COUNT (R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1) +#define R_PPC64_REL24 R_PPC_REL24 +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_ADDR64 38 +#define R_PPC64_TOC16 47 +#define R_PPC64_TOC16_LO 48 +#define R_PPC64_TOC16_HI 49 +#define R_PPC64_TOC16_HA 50 +#define R_PPC64_TOC16_DS 63 +#define R_PPC64_TOC16_LO_DS 64 +#define R_PPC64_REL16_LO 250 +#define R_PPC64_REL16_HI 251 +#define R_PPC64_REL16_HA 252 #define R_SPARC_NONE 0 #define R_SPARC_8 1 @@ -970,6 +985,7 @@ uint32 elfwritephdrs(void); uint32 elfwriteshdrs(void); void elfwritedynent(LSym*, int, uint64); void elfwritedynentsym(LSym*, int, LSym*); +void elfwritedynentsymplus(LSym*, int, LSym*, vlong); void elfwritedynentsymsize(LSym*, int, LSym*); uint32 elfhash(uchar*); uint64 startelf(void); diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index f289fb38c7..29d32d283d 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -554,6 +554,9 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->type = STEXT; break; } + if(strcmp(sect->name, ".got") == 0 || + strcmp(sect->name, ".toc") == 0) + s->type = SELFGOT; if(sect->type == ElfSectProgbits) { s->p = sect->base; s->np = sect->size; @@ -812,6 +815,10 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) s = nil; if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) sym->name = ".got"; + if(strcmp(sym->name, ".TOC.") == 0) + // Magic symbol on ppc64. Will be set to this object + // file's .got+0x8000. + sym->bind = ElfSymBindLocal; switch(sym->type) { case ElfSymTypeSection: s = obj->sect[sym->shndx].sym; @@ -842,6 +849,15 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) // symbols, ignore these break; } + if(strcmp(sym->name, ".TOC.") == 0) { + // We need to be able to look this up, + // so put it in the hash table. + if(needSym) { + s = linklookup(ctxt, sym->name, ctxt->version); + s->type |= SHIDDEN; + } + break; + } if(needSym) { // local names and hidden visiblity global names are unique // and should only reference by its index, not name, so we @@ -892,6 +908,17 @@ 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('9', R_PPC64_TOC16): + case R('9', R_PPC64_TOC16_LO): + case R('9', R_PPC64_TOC16_HI): + case R('9', R_PPC64_TOC16_HA): + case R('9', R_PPC64_TOC16_DS): + case R('9', R_PPC64_TOC16_LO_DS): + case R('9', R_PPC64_REL16_LO): + case R('9', R_PPC64_REL16_HI): + case R('9', R_PPC64_REL16_HA): + *siz = 2; + break; case R('5', R_ARM_ABS32): case R('5', R_ARM_GOT32): case R('5', R_ARM_PLT32): @@ -913,9 +940,11 @@ reltype(char *pn, int elftype, uchar *siz) case R('8', R_386_PLT32): case R('8', R_386_GOTOFF): case R('8', R_386_GOTPC): + case R('9', R_PPC64_REL24): *siz = 4; break; case R('6', R_X86_64_64): + case R('9', R_PPC64_ADDR64): *siz = 8; break; } |
