Commit db923390 authored by Austin Clements's avatar Austin Clements

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/2006Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent ac5a1ac3
...@@ -212,6 +212,7 @@ enum ...@@ -212,6 +212,7 @@ enum
SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHO, /* Mach-O __nl_symbol_ptr */
SMACHOGOT, SMACHOGOT,
SWINDOWS, SWINDOWS,
SELFGOT, /* also .toc in ppc64 ABI */
SNOPTRDATA, SNOPTRDATA,
SINITARR, SINITARR,
SDATA, SDATA,
...@@ -256,12 +257,20 @@ enum ...@@ -256,12 +257,20 @@ enum
R_PLT1, R_PLT1,
R_PLT2, R_PLT2,
R_USEFIELD, R_USEFIELD,
R_POWER_TOC, // ELF R_PPC64_TOC16*
}; };
// Reloc.variant // Reloc.variant
enum enum
{ {
RV_NONE, // identity variant RV_NONE, // identity variant
RV_POWER_LO, // x & 0xFFFF
RV_POWER_HI, // x >> 16
RV_POWER_HA, // (x + 0x8000) >> 16
RV_POWER_DS, // x & 0xFFFC, check x&0x3 == 0
RV_CHECK_OVERFLOW = 1<<8, // check overflow flag
RV_TYPE_MASK = (RV_CHECK_OVERFLOW - 1),
}; };
// Auto.type // Auto.type
......
This diff is collapsed.
...@@ -80,7 +80,8 @@ archinit(void) ...@@ -80,7 +80,8 @@ archinit(void)
INITRND = 4096; INITRND = 4096;
break; break;
case Hlinux: /* ppc64 elf */ case Hlinux: /* ppc64 elf */
debug['d'] = 1; // TODO(minux): dynamic linking is not supported yet. if(strcmp(thestring, "ppc64") == 0)
debug['d'] = 1; // TODO(austin): ELF ABI v1 not supported yet
elfinit(); elfinit();
HEADR = ELFRESERVE; HEADR = ELFRESERVE;
if(INITTEXT == -1) if(INITTEXT == -1)
......
...@@ -49,7 +49,11 @@ datcmp(LSym *s1, LSym *s2) ...@@ -49,7 +49,11 @@ datcmp(LSym *s1, LSym *s2)
{ {
if(s1->type != s2->type) if(s1->type != s2->type)
return (int)s1->type - (int)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) if(s1->size < s2->size)
return -1; return -1;
return +1; return +1;
...@@ -920,7 +924,7 @@ dodata(void) ...@@ -920,7 +924,7 @@ dodata(void)
vlong datsize; vlong datsize;
Section *sect; Section *sect;
Segment *segro; Segment *segro;
LSym *s, *last, **l; LSym *s, *last, **l, *toc;
LSym *gcdata, *gcbss; LSym *gcdata, *gcbss;
ProgGen gen; ProgGen gen;
...@@ -994,7 +998,7 @@ dodata(void) ...@@ -994,7 +998,7 @@ dodata(void)
/* writable ELF sections */ /* writable ELF sections */
datsize = 0; 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 = addsection(&segdata, s->name, 06);
sect->align = symalign(s); sect->align = symalign(s);
datsize = rnd(datsize, sect->align); datsize = rnd(datsize, sect->align);
...@@ -1006,6 +1010,33 @@ dodata(void) ...@@ -1006,6 +1010,33 @@ dodata(void)
sect->len = datsize - sect->vaddr; 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 */ /* pointer-free data */
sect = addsection(&segdata, ".noptrdata", 06); sect = addsection(&segdata, ".noptrdata", 06);
sect->align = maxalign(s, SINITARR-1); sect->align = maxalign(s, SINITARR-1);
......
...@@ -306,12 +306,18 @@ elfwritedynent(LSym *s, int tag, uint64 val) ...@@ -306,12 +306,18 @@ elfwritedynent(LSym *s, int tag, uint64 val)
void void
elfwritedynentsym(LSym *s, int tag, LSym *t) elfwritedynentsym(LSym *s, int tag, LSym *t)
{
elfwritedynentsymplus(s, tag, t, 0);
}
void
elfwritedynentsymplus(LSym *s, int tag, LSym *t, vlong add)
{ {
if(elf64) if(elf64)
adduint64(ctxt, s, tag); adduint64(ctxt, s, tag);
else else
adduint32(ctxt, s, tag); adduint32(ctxt, s, tag);
addaddr(ctxt, s, t); addaddrplus(ctxt, s, t, add);
} }
void void
...@@ -977,6 +983,8 @@ doelf(void) ...@@ -977,6 +983,8 @@ doelf(void)
addstring(shstrtab, ".interp"); addstring(shstrtab, ".interp");
addstring(shstrtab, ".hash"); addstring(shstrtab, ".hash");
addstring(shstrtab, ".got"); addstring(shstrtab, ".got");
if(thechar == '9')
addstring(shstrtab, ".glink");
addstring(shstrtab, ".got.plt"); addstring(shstrtab, ".got.plt");
addstring(shstrtab, ".dynamic"); addstring(shstrtab, ".dynamic");
addstring(shstrtab, ".dynsym"); addstring(shstrtab, ".dynsym");
...@@ -1020,7 +1028,14 @@ doelf(void) ...@@ -1020,7 +1028,14 @@ doelf(void)
/* global offset table */ /* global offset table */
s = linklookup(ctxt, ".got", 0); s = linklookup(ctxt, ".got", 0);
s->reachable = 1; 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 */ /* hash */
s = linklookup(ctxt, ".hash", 0); s = linklookup(ctxt, ".hash", 0);
...@@ -1033,6 +1048,11 @@ doelf(void) ...@@ -1033,6 +1048,11 @@ doelf(void)
s = linklookup(ctxt, ".plt", 0); s = linklookup(ctxt, ".plt", 0);
s->reachable = 1; s->reachable = 1;
if(thechar == '9')
// In the ppc64 ABI, .plt is a data section
// written by the dynamic linker.
s->type = SELFSECT;
else
s->type = SELFRXSECT; s->type = SELFRXSECT;
elfsetupplt(); elfsetupplt();
...@@ -1080,8 +1100,14 @@ doelf(void) ...@@ -1080,8 +1100,14 @@ doelf(void)
if(rpath) if(rpath)
elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath));
if(thechar == '9')
elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".plt", 0));
else
elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); 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 // 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_JMPREL is emitted so we have to defer generation of DT_PLTREL,
// DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the
...@@ -1309,6 +1335,7 @@ asmbelf(vlong symo) ...@@ -1309,6 +1335,7 @@ asmbelf(vlong symo)
switch(eh->machine) { switch(eh->machine) {
case EM_X86_64: case EM_X86_64:
case EM_PPC64:
sh = elfshname(".rela.plt"); sh = elfshname(".rela.plt");
sh->type = SHT_RELA; sh->type = SHT_RELA;
sh->flags = SHF_ALLOC; sh->flags = SHF_ALLOC;
...@@ -1345,16 +1372,33 @@ asmbelf(vlong symo) ...@@ -1345,16 +1372,33 @@ asmbelf(vlong symo)
break; 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 = elfshname(".plt");
sh->type = SHT_PROGBITS; sh->type = SHT_PROGBITS;
sh->flags = SHF_ALLOC+SHF_EXECINSTR; sh->flags = SHF_ALLOC+SHF_EXECINSTR;
if(eh->machine == EM_X86_64) if(eh->machine == EM_X86_64)
sh->entsize = 16; 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->entsize = 4;
sh->addralign = 4; sh->addralign = sh->entsize;
shsym(sh, linklookup(ctxt, ".plt", 0)); shsym(sh, linklookup(ctxt, ".plt", 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 = elfshname(".got");
sh->type = SHT_PROGBITS; sh->type = SHT_PROGBITS;
sh->flags = SHF_ALLOC+SHF_WRITE; sh->flags = SHF_ALLOC+SHF_WRITE;
...@@ -1368,6 +1412,7 @@ asmbelf(vlong symo) ...@@ -1368,6 +1412,7 @@ asmbelf(vlong symo)
sh->entsize = RegSize; sh->entsize = RegSize;
sh->addralign = RegSize; sh->addralign = RegSize;
shsym(sh, linklookup(ctxt, ".got.plt", 0)); shsym(sh, linklookup(ctxt, ".got.plt", 0));
}
sh = elfshname(".hash"); sh = elfshname(".hash");
sh->type = SHT_HASH; sh->type = SHT_HASH;
......
...@@ -317,6 +317,9 @@ typedef struct { ...@@ -317,6 +317,9 @@ typedef struct {
#define DT_VERNEEDNUM 0x6fffffff #define DT_VERNEEDNUM 0x6fffffff
#define DT_VERSYM 0x6ffffff0 #define DT_VERSYM 0x6ffffff0
#define DT_PPC64_GLINK (DT_LOPROC + 0)
#define DT_PPC64_OPT (DT_LOPROC + 3)
/* Values for DT_FLAGS */ /* Values for DT_FLAGS */
/* Indicates that the object being loaded may make reference to /* Indicates that the object being loaded may make reference to
the $ORIGIN substitution string */ the $ORIGIN substitution string */
...@@ -700,6 +703,18 @@ typedef struct { ...@@ -700,6 +703,18 @@ typedef struct {
/* Count of defined relocation types. */ /* Count of defined relocation types. */
#define R_PPC_EMB_COUNT (R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1) #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_NONE 0
#define R_SPARC_8 1 #define R_SPARC_8 1
...@@ -970,6 +985,7 @@ uint32 elfwritephdrs(void); ...@@ -970,6 +985,7 @@ uint32 elfwritephdrs(void);
uint32 elfwriteshdrs(void); uint32 elfwriteshdrs(void);
void elfwritedynent(LSym*, int, uint64); void elfwritedynent(LSym*, int, uint64);
void elfwritedynentsym(LSym*, int, LSym*); void elfwritedynentsym(LSym*, int, LSym*);
void elfwritedynentsymplus(LSym*, int, LSym*, vlong);
void elfwritedynentsymsize(LSym*, int, LSym*); void elfwritedynentsymsize(LSym*, int, LSym*);
uint32 elfhash(uchar*); uint32 elfhash(uchar*);
uint64 startelf(void); uint64 startelf(void);
......
...@@ -554,6 +554,9 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) ...@@ -554,6 +554,9 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
s->type = STEXT; s->type = STEXT;
break; break;
} }
if(strcmp(sect->name, ".got") == 0 ||
strcmp(sect->name, ".toc") == 0)
s->type = SELFGOT;
if(sect->type == ElfSectProgbits) { if(sect->type == ElfSectProgbits) {
s->p = sect->base; s->p = sect->base;
s->np = sect->size; s->np = sect->size;
...@@ -812,6 +815,10 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) ...@@ -812,6 +815,10 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym)
s = nil; s = nil;
if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0)
sym->name = ".got"; 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) { switch(sym->type) {
case ElfSymTypeSection: case ElfSymTypeSection:
s = obj->sect[sym->shndx].sym; s = obj->sect[sym->shndx].sym;
...@@ -842,6 +849,15 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) ...@@ -842,6 +849,15 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym)
// symbols, ignore these // symbols, ignore these
break; 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) { if(needSym) {
// local names and hidden visiblity global names are unique // local names and hidden visiblity global names are unique
// and should only reference by its index, not name, so we // and should only reference by its index, not name, so we
...@@ -892,6 +908,17 @@ reltype(char *pn, int elftype, uchar *siz) ...@@ -892,6 +908,17 @@ reltype(char *pn, int elftype, uchar *siz)
switch(R(thechar, elftype)) { switch(R(thechar, elftype)) {
default: default:
diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); 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_ABS32):
case R('5', R_ARM_GOT32): case R('5', R_ARM_GOT32):
case R('5', R_ARM_PLT32): case R('5', R_ARM_PLT32):
...@@ -913,9 +940,11 @@ reltype(char *pn, int elftype, uchar *siz) ...@@ -913,9 +940,11 @@ reltype(char *pn, int elftype, uchar *siz)
case R('8', R_386_PLT32): case R('8', R_386_PLT32):
case R('8', R_386_GOTOFF): case R('8', R_386_GOTOFF):
case R('8', R_386_GOTPC): case R('8', R_386_GOTPC):
case R('9', R_PPC64_REL24):
*siz = 4; *siz = 4;
break; break;
case R('6', R_X86_64_64): case R('6', R_X86_64_64):
case R('9', R_PPC64_ADDR64):
*siz = 8; *siz = 8;
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment