aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/ld
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2014-12-16 14:59:59 -0500
committerAustin Clements <austin@google.com>2015-01-07 20:35:54 +0000
commitdb923390a01d4c992116161bed1328bd5bb32a24 (patch)
tree54daaefb538fec7d954849b2ab2c3659b2ad785b /src/cmd/ld
parentac5a1ac318efb7890b25cf614a0cd5b3e52c74e3 (diff)
downloadgo-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.c37
-rw-r--r--src/cmd/ld/elf.c83
-rw-r--r--src/cmd/ld/elf.h16
-rw-r--r--src/cmd/ld/ldelf.c29
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;
}