elf.$O\
enam.$O\
go.$O\
+ ldelf.$O\
+ ldmacho.$O\
lib.$O\
list.$O\
macho.$O\
ElfStrShstrtab,
ElfStrSymtab,
ElfStrStrtab,
+ ElfStrRelaPlt,
+ ElfStrPlt,
NElfStr
};
return 0;
}
+int nelfsym = 1;
+
+static void adddynsym(Sym*);
+static void addpltsym(Sym*);
+static void addgotsym(Sym*);
+
+void
+adddynrel(Sym *s, Reloc *r)
+{
+ Sym *targ, *rela, *got;
+
+ targ = r->sym;
+ cursym = s;
+
+ switch(r->type) {
+ default:
+ if(r->type >= 256) {
+ diag("unexpected relocation type %d", r->type);
+ return;
+ }
+ break;
+
+ // Handle relocations found in ELF object files.
+ case 256 + R_X86_64_PC32:
+ if(targ->dynimpname)
+ diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name);
+ if(targ->type == 0 || targ->type == SXREF)
+ diag("unknown symbol %s in pcrel", targ->name);
+ r->type = D_PCREL;
+ r->add += 4;
+ return;
+
+ case 256 + R_X86_64_PLT32:
+ addpltsym(targ);
+ r->type = D_PCREL;
+ r->sym = lookup(".plt", 0);
+ r->add += 4;
+ r->add += targ->plt;
+ return;
+
+ case 256 + R_X86_64_GOTPCREL:
+ addgotsym(targ);
+ r->type = D_PCREL;
+ r->sym = lookup(".got", 0);
+ r->add += 4;
+ r->add += targ->got;
+ return;
+
+ case 256 + R_X86_64_64:
+ if(targ->dynimpname)
+ diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name);
+ r->type = D_ADDR;
+ return;
+
+ // Handle relocations found in Mach-O object files.
+ case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0:
+ case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0:
+ case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0:
+ // TODO: What is the difference between all these?
+ r->type = D_ADDR;
+ if(targ->dynimpname)
+ diag("unexpected reloc for dynamic symbol %s", targ->name);
+ return;
+
+ case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1:
+ if(targ->dynimpname) {
+ addpltsym(targ);
+ r->sym = lookup(".plt", 0);
+ r->add = targ->plt;
+ r->type = D_PCREL;
+ return;
+ }
+ // fall through
+ case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1:
+ case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1:
+ case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1:
+ case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1:
+ case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1:
+ r->type = D_PCREL;
+ if(targ->dynimpname)
+ diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name);
+ return;
+
+ case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
+ case 512 + MACHO_X86_64_RELOC_GOT*2 + 1:
+ // TODO: What is the difference between these two?
+ addgotsym(targ);
+ r->type = D_PCREL;
+ r->sym = lookup(".got", 0);
+ r->add += targ->got;
+ return;
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if(targ->dynimpname == nil)
+ return;
+
+ switch(r->type) {
+ case D_PCREL:
+ addpltsym(targ);
+ r->sym = lookup(".plt", 0);
+ r->add = targ->plt;
+ return;
+
+ case D_ADDR:
+ if(s->type != SDATA)
+ break;
+ if(iself) {
+ adddynsym(targ);
+ rela = lookup(".rela", 0);
+ addaddrplus(rela, s, r->off);
+ if(r->siz == 8)
+ adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_64));
+ else
+ adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_32));
+ adduint64(rela, r->add);
+ r->type = 256; // ignore during relocsym
+ return;
+ }
+ if(HEADTYPE == 6 && s->size == PtrSize && r->off == 0) {
+ // Mach-O relocations are a royal pain to lay out.
+ // They use a compact stateful bytecode representation
+ // that is too much bother to deal with.
+ // Instead, interpret the C declaration
+ // void *_Cvar_stderr = &stderr;
+ // as making _Cvar_stderr the name of a GOT entry
+ // for stderr. This is separate from the usual GOT entry,
+ // just in case the C code assigns to the variable,
+ // and of course it only works for single pointers,
+ // but we only need to support cgo and that's all it needs.
+ adddynsym(targ);
+ got = lookup(".got", 0);
+ s->type = got->type | SSUB;
+ s->outer = got;
+ s->sub = got->sub;
+ got->sub = s;
+ s->value = got->size;
+ adduint64(got, 0);
+ adduint32(lookup(".linkedit.got", 0), targ->dynid);
+ r->type = 256; // ignore during relocsym
+ return;
+ }
+ break;
+ }
+
+ cursym = s;
+ diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type);
+}
+
+int
+archreloc(Reloc *r, Sym *s, vlong *val)
+{
+ return -1;
+}
+
+static void
+elfsetupplt(void)
+{
+ Sym *plt, *got;
+
+ plt = lookup(".plt", 0);
+ got = lookup(".got.plt", 0);
+ if(plt->size == 0) {
+ // pushq got+8(IP)
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x35);
+ addpcrelplus(plt, got, 8);
+
+ // jmpq got+16(IP)
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x25);
+ addpcrelplus(plt, got, 16);
+
+ // nopl 0(AX)
+ adduint32(plt, 0x00401f0f);
+
+ // assume got->size == 0 too
+ addaddrplus(got, lookup(".dynamic", 0), 0);
+ adduint64(got, 0);
+ adduint64(got, 0);
+ }
+}
+
+static void
+addpltsym(Sym *s)
+{
+ if(s->plt >= 0)
+ return;
+
+ adddynsym(s);
+
+ if(iself) {
+ Sym *plt, *got, *rela;
+
+ plt = lookup(".plt", 0);
+ got = lookup(".got.plt", 0);
+ rela = lookup(".rela.plt", 0);
+ if(plt->size == 0)
+ elfsetupplt();
+
+ // jmpq *got+size(IP)
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x25);
+ addpcrelplus(plt, got, got->size);
+
+ // add to got: pointer to current pos in plt
+ addaddrplus(got, plt, plt->size);
+
+ // pushq $x
+ adduint8(plt, 0x68);
+ adduint32(plt, (got->size-24-8)/8);
+
+ // jmpq .plt
+ adduint8(plt, 0xe9);
+ adduint32(plt, -(plt->size+4));
+
+ // rela
+ addaddrplus(rela, got, got->size-8);
+ adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT));
+ adduint64(rela, 0);
+
+ s->plt = plt->size - 16;
+ } else if(HEADTYPE == 6) { // Mach-O
+ // To do lazy symbol lookup right, we're supposed
+ // to tell the dynamic loader which library each
+ // symbol comes from and format the link info
+ // section just so. I'm too lazy (ha!) to do that
+ // so for now we'll just use non-lazy pointers,
+ // which don't need to be told which library to use.
+ //
+ // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
+ // has details about what we're avoiding.
+
+ Sym *plt;
+
+ addgotsym(s);
+ plt = lookup(".plt", 0);
+
+ adduint32(lookup(".linkedit.plt", 0), s->dynid);
+
+ // jmpq *got+size(IP)
+ s->plt = plt->size;
+
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x25);
+ addpcrelplus(plt, lookup(".got", 0), s->got);
+ } else {
+ diag("addpltsym: unsupported binary format");
+ }
+}
+
+static void
+addgotsym(Sym *s)
+{
+ Sym *got, *rela, *indir;
+
+ if(s->got >= 0)
+ return;
+
+ adddynsym(s);
+ got = lookup(".got", 0);
+ s->got = got->size;
+ adduint64(got, 0);
+
+ if(iself) {
+ rela = lookup(".rela", 0);
+ addaddrplus(rela, got, s->got);
+ adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT));
+ adduint64(rela, 0);
+ } else if(HEADTYPE == 6) { // Mach-O
+ adduint32(lookup(".linkedit.got", 0), s->dynid);
+ } else {
+ diag("addgotsym: unsupported binary format");
+ }
+}
+
+static void
+adddynsym(Sym *s)
+{
+ Sym *d, *str;
+ int t;
+ char *name;
+
+ if(s->dynid >= 0)
+ return;
+
+ if(iself) {
+ s->dynid = nelfsym++;
+
+ d = lookup(".dynsym", 0);
+ name = s->dynimpname;
+ if(name == nil)
+ name = s->name;
+ adduint32(d, addstring(lookup(".dynstr", 0), name));
+ /* type */
+ t = STB_GLOBAL << 4;
+ if(s->dynexport && s->type == STEXT)
+ t |= STT_FUNC;
+ else
+ t |= STT_OBJECT;
+ adduint8(d, t);
+
+ /* reserved */
+ adduint8(d, 0);
+
+ /* section where symbol is defined */
+ if(!s->dynexport && s->dynimpname != nil)
+ adduint16(d, SHN_UNDEF);
+ else {
+ switch(s->type) {
+ default:
+ case STEXT:
+ t = 11;
+ break;
+ case SRODATA:
+ t = 12;
+ break;
+ case SDATA:
+ t = 13;
+ break;
+ case SBSS:
+ t = 14;
+ break;
+ }
+ adduint16(d, t);
+ }
+
+ /* value */
+ if(s->type == SDYNIMPORT)
+ adduint64(d, 0);
+ else
+ addaddr(d, s);
+
+ /* size of object */
+ adduint64(d, 0);
+
+ if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) {
+ elfwritedynent(lookup(".dynamic", 0), DT_NEEDED,
+ addstring(lookup(".dynstr", 0), s->dynimplib));
+ }
+ } else if(HEADTYPE == 6) {
+ // Mach-o symbol nlist64
+ d = lookup(".dynsym", 0);
+ name = s->dynimpname;
+ if(name == nil)
+ name = s->name;
+ s->dynid = d->size/16;
+ // darwin still puts _ prefixes on all C symbols
+ str = lookup(".dynstr", 0);
+ adduint32(d, str->size);
+ adduint8(str, '_');
+ addstring(str, name);
+ adduint8(d, 0x01); // type - N_EXT - external symbol
+ adduint8(d, 0); // section
+ adduint16(d, 0); // desc
+ adduint64(d, 0); // value
+ } else {
+ diag("adddynsym: unsupported binary format");
+ }
+}
+
+void
+adddynlib(char *lib)
+{
+ Sym *s;
+
+ if(!needlib(lib))
+ return;
+
+ if(iself) {
+ s = lookup(".dynstr", 0);
+ if(s->size == 0)
+ addstring(s, "");
+ elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib));
+ } else if(HEADTYPE == 6) { // Mach-O
+ machoadddynlib(lib);
+ } else {
+ diag("adddynlib: unsupported binary format");
+ }
+}
+
void
doelf(void)
{
- Sym *s, *shstrtab, *dynamic, *dynstr, *d;
- int h, nsym, t;
+ Sym *s, *shstrtab, *dynstr;
if(HEADTYPE != 7 && HEADTYPE != 9)
return;
elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym");
elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr");
elfstr[ElfStrRela] = addstring(shstrtab, ".rela");
+ elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt");
+ elfstr[ElfStrPlt] = addstring(shstrtab, ".plt");
/* dynamic symbol table - first entry all zeros */
s = lookup(".dynsym", 0);
s = lookup(".dynstr", 0);
s->type = SELFDATA;
s->reachable = 1;
- addstring(s, "");
+ if(s->size == 0)
+ addstring(s, "");
dynstr = s;
/* relocation table */
/* global offset table */
s = lookup(".got", 0);
s->reachable = 1;
+ s->type = SDATA; // writable, so not SELFDATA
+
+ /* hash */
+ s = lookup(".hash", 0);
+ s->reachable = 1;
s->type = SELFDATA;
- /* got.plt - ??? */
s = lookup(".got.plt", 0);
s->reachable = 1;
+ s->type = SDATA; // writable, not SELFDATA
+
+ s = lookup(".plt", 0);
+ s->reachable = 1;
s->type = SELFDATA;
- /* hash */
- s = lookup(".hash", 0);
+ elfsetupplt();
+
+ s = lookup(".rela.plt", 0);
s->reachable = 1;
s->type = SELFDATA;
s = lookup(".dynamic", 0);
s->reachable = 1;
s->type = SELFDATA;
- dynamic = s;
-
- /*
- * relocation entries for dynimport symbols
- */
- nsym = 1; // sym 0 is reserved
- for(h=0; h<NHASH; h++) {
- for(s=hash[h]; s!=S; s=s->hash) {
- if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
- continue;
-
- if(!s->dynexport) {
- d = lookup(".rela", 0);
- addaddr(d, s);
- adduint64(d, ELF64_R_INFO(nsym, R_X86_64_64));
- adduint64(d, 0);
- }
-
- nsym++;
-
- d = lookup(".dynsym", 0);
- adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname));
- /* type */
- t = STB_GLOBAL << 4;
- if(s->dynexport && s->type == STEXT)
- t |= STT_FUNC;
- else
- t |= STT_OBJECT;
- adduint8(d, t);
-
- /* reserved */
- adduint8(d, 0);
-
- /* section where symbol is defined */
- if(!s->dynexport)
- adduint16(d, SHN_UNDEF);
- else {
- switch(s->type) {
- default:
- case STEXT:
- t = 9;
- break;
- case SDATA:
- t = 10;
- break;
- case SBSS:
- t = 11;
- break;
- }
- adduint16(d, t);
- }
-
- /* value */
- if(!s->dynexport)
- adduint64(d, 0);
- else
- addaddr(d, s);
-
- /* size of object */
- adduint64(d, 0);
-
- if(!s->dynexport && needlib(s->dynimplib))
- elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib));
- }
- }
-
- elfdynhash(nsym);
/*
* .dynamic table
*/
- s = dynamic;
elfwritedynentsym(s, DT_HASH, lookup(".hash", 0));
elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0));
elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE);
elfwritedynent(s, DT_RELAENT, ELF64RELASIZE);
if(rpath)
elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath));
+
+ elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0));
+ elfwritedynent(s, DT_PLTREL, DT_RELA);
+ elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0));
+ elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0));
elfwritedynent(s, DT_NULL, 0);
}
}
case 9:
debug['8'] = 1; /* 64-bit addresses */
/* index of elf text section; needed by asmelfsym, double-checked below */
- /* !debug['d'] causes 8 extra sections before the .text section */
+ /* !debug['d'] causes extra sections before the .text section */
elftextsh = 1;
if(!debug['d'])
- elftextsh += 8;
+ elftextsh += 10;
break;
}
sh->entsize = 8;
sh->addralign = 8;
shsym(sh, lookup(".got.plt", 0));
-
+
dynsym = eh->shnum;
sh = newElfShdr(elfstr[ElfStrDynsym]);
sh->type = SHT_DYNSYM;
sh->addralign = 1;
shsym(sh, lookup(".dynstr", 0));
+ sh = newElfShdr(elfstr[ElfStrRelaPlt]);
+ sh->type = SHT_RELA;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = ELF64RELASIZE;
+ sh->addralign = 8;
+ sh->link = dynsym;
+ sh->info = eh->shnum; // .plt
+ shsym(sh, lookup(".rela.plt", 0));
+
+ sh = newElfShdr(elfstr[ElfStrPlt]);
+ sh->type = SHT_PROGBITS;
+ sh->flags = SHF_ALLOC+SHF_EXECINSTR;
+ sh->entsize = 16;
+ sh->addralign = 4;
+ shsym(sh, lookup(".plt", 0));
+
sh = newElfShdr(elfstr[ElfStrHash]);
sh->type = SHT_HASH;
sh->flags = SHF_ALLOC;
for(h=0; h<NHASH; h++) {
for(s=hash[h]; s!=S; s=s->hash) {
- switch(s->type) {
+ switch(s->type&~SSUB) {
case SCONST:
case SRODATA:
case SDATA:
case SELFDATA:
- case SMACHO:
+ case SMACHOGOT:
if(!s->reachable)
continue;
put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype);
}
for(s = textp; s != nil; s = s->next) {
+ if(s->text == nil)
+ continue;
+
/* filenames first */
for(a=s->autom; a; a=a->link)
if(a->type == D_FILE)
if(a->type == D_FILE1)
put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0);
- put(s, s->name, 'T', 0, s->size, s->version, s->gotype);
+ put(s, s->name, 'T', s->value, s->size, s->version, s->gotype);
/* frame, auto and param after */
put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0);
{
int32 off;
uchar siz;
- uchar type;
+ int32 type;
int64 add;
Sym* sym;
};
uchar reachable;
uchar dynexport;
uchar special;
+ int32 dynid;
int32 sig;
+ int32 plt;
+ int32 got;
Sym* hash; // in hash table
Sym* next; // in text or data list
Sym* sub; // in SSUB list
+ Sym* outer; // container of sub
vlong value;
vlong size;
Sym* gotype;
/* order here is order in output file */
STEXT = 1,
SELFDATA,
+ SMACHOPLT,
SRODATA,
SDATA,
- SMACHO,
+ SMACHOGOT,
SBSS,
SXREF,
+ SMACHODYNSTR,
+ SMACHODYNSYM,
+ SMACHOINDIRECTPLT,
+ SMACHOINDIRECTGOT,
SFILE,
SCONST,
+ SDYNIMPORT,
SSUB = 1<<8,
NHASH = 10007,
D_CONST2 = D_INDIR+D_INDIR,
D_SIZE, /* 8l internal */
D_PCREL,
+ D_GOTOFF,
+ D_GOTPCREL,
T_TYPE = 1<<0,
T_INDEX = 1<<1,
elf.$O\
enam.$O\
go.$O\
+ ldelf.$O\
+ ldmacho.$O\
lib.$O\
list.$O\
macho.$O\
ElfStrGosymtab,
ElfStrGopclntab,
ElfStrShstrtab,
+ ElfStrSymtab,
+ ElfStrStrtab,
+ ElfStrRelPlt,
+ ElfStrPlt,
NElfStr
};
return 0;
}
+int nelfsym = 1;
+
+static void adddynsym(Sym*);
+static void addpltsym(Sym*);
+static void addgotsym(Sym*);
+
+void
+adddynrel(Sym *s, Reloc *r)
+{
+ Sym *targ, *rel, *got;
+
+ targ = r->sym;
+ cursym = s;
+
+ switch(r->type) {
+ default:
+ if(r->type >= 256) {
+ diag("unexpected relocation type %d", r->type);
+ return;
+ }
+ break;
+
+ // Handle relocations found in ELF object files.
+ case 256 + R_386_PC32:
+ if(targ->dynimpname)
+ diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ->name);
+ if(targ->type == 0 || targ->type == SXREF)
+ diag("unknown symbol %s in pcrel", targ->name);
+ r->type = D_PCREL;
+ r->add += 4;
+ return;
+
+ case 256 + R_386_PLT32:
+ addpltsym(targ);
+ r->type = D_PCREL;
+ r->sym = lookup(".plt", 0);
+ r->add += 4;
+ r->add += targ->plt;
+ return;
+
+ case 256 + R_386_GOT32:
+ addgotsym(targ);
+ r->type = D_CONST; // write r->add during relocsym
+ r->sym = S;
+ r->add += targ->got;
+ return;
+
+ case 256 + R_386_GOTOFF:
+ r->type = D_GOTOFF;
+ return;
+
+ case 256 + R_386_GOTPC:
+ r->type = D_PCREL;
+ r->sym = lookup(".got", 0);
+ r->add += 4;
+ return;
+
+ case 256 + R_386_32:
+ if(targ->dynimpname)
+ diag("unexpected R_386_32 relocation for dynamic symbol %s", targ->name);
+ r->type = D_ADDR;
+ return;
+
+ case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 0:
+ r->type = D_ADDR;
+ if(targ->dynimpname)
+ diag("unexpected reloc for dynamic symbol %s", targ->name);
+ return;
+
+ case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 1:
+ if(targ->dynimpname) {
+ addpltsym(targ);
+ r->sym = lookup(".plt", 0);
+ r->add = targ->plt;
+ r->type = D_PCREL;
+ return;
+ }
+ r->type = D_PCREL;
+ if(targ->dynimpname)
+ diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name);
+ return;
+
+ case 512 + MACHO_FAKE_GOTPCREL:
+ addgotsym(targ);
+ r->sym = lookup(".got", 0);
+ r->add += targ->got;
+ r->type = D_PCREL;
+ return;
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if(targ->dynimpname == nil)
+ return;
+
+ switch(r->type) {
+ case D_PCREL:
+ addpltsym(targ);
+ r->sym = lookup(".plt", 0);
+ r->add = targ->plt;
+ return;
+
+ case D_ADDR:
+ if(s->type != SDATA)
+ break;
+ if(iself) {
+ adddynsym(targ);
+ rel = lookup(".rel", 0);
+ addaddrplus(rel, s, r->off);
+ adduint32(rel, ELF32_R_INFO(targ->dynid, R_386_32));
+ r->type = D_CONST; // write r->add during relocsym
+ r->sym = S;
+ return;
+ }
+ if(HEADTYPE == 6 && s->size == PtrSize && r->off == 0) {
+ // Mach-O relocations are a royal pain to lay out.
+ // They use a compact stateful bytecode representation
+ // that is too much bother to deal with.
+ // Instead, interpret the C declaration
+ // void *_Cvar_stderr = &stderr;
+ // as making _Cvar_stderr the name of a GOT entry
+ // for stderr. This is separate from the usual GOT entry,
+ // just in case the C code assigns to the variable,
+ // and of course it only works for single pointers,
+ // but we only need to support cgo and that's all it needs.
+ adddynsym(targ);
+ got = lookup(".got", 0);
+ s->type = got->type | SSUB;
+ s->outer = got;
+ s->sub = got->sub;
+ got->sub = s;
+ s->value = got->size;
+ adduint32(got, 0);
+ adduint32(lookup(".linkedit.got", 0), targ->dynid);
+ r->type = 256; // ignore during relocsym
+ return;
+ }
+ break;
+ }
+
+ cursym = s;
+ diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type);
+}
+
+static void
+elfsetupplt(void)
+{
+ Sym *plt, *got;
+
+ plt = lookup(".plt", 0);
+ got = lookup(".got.plt", 0);
+ if(plt->size == 0) {
+ // pushl got+4
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x35);
+ addaddrplus(plt, got, 4);
+
+ // jmp *got+8
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x25);
+ addaddrplus(plt, got, 8);
+
+ // zero pad
+ adduint32(plt, 0);
+
+ // assume got->size == 0 too
+ addaddrplus(got, lookup(".dynamic", 0), 0);
+ adduint32(got, 0);
+ adduint32(got, 0);
+ }
+}
+
+int
+archreloc(Reloc *r, Sym *s, vlong *val)
+{
+ switch(r->type) {
+ case D_CONST:
+ *val = r->add;
+ return 0;
+ case D_GOTOFF:
+ *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0));
+ return 0;
+ }
+ return -1;
+}
+
+static void
+addpltsym(Sym *s)
+{
+ Sym *plt, *got, *rel;
+
+ if(s->plt >= 0)
+ return;
+
+ adddynsym(s);
+
+ if(iself) {
+ plt = lookup(".plt", 0);
+ got = lookup(".got.plt", 0);
+ rel = lookup(".rel.plt", 0);
+ if(plt->size == 0)
+ elfsetupplt();
+
+ // jmpq *got+size
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x25);
+ addaddrplus(plt, got, got->size);
+
+ // add to got: pointer to current pos in plt
+ addaddrplus(got, plt, plt->size);
+
+ // pushl $x
+ adduint8(plt, 0x68);
+ adduint32(plt, rel->size);
+
+ // jmp .plt
+ adduint8(plt, 0xe9);
+ adduint32(plt, -(plt->size+4));
+
+ // rel
+ addaddrplus(rel, got, got->size-4);
+ adduint32(rel, ELF32_R_INFO(s->dynid, R_386_JMP_SLOT));
+
+ s->plt = plt->size - 16;
+ } else if(HEADTYPE == 6) { // Mach-O
+ // Same laziness as in 6l.
+
+ Sym *plt;
+
+ plt = lookup(".plt", 0);
+
+ addgotsym(s);
+
+ adduint32(lookup(".linkedit.plt", 0), s->dynid);
+
+ // jmpq *got+size(IP)
+ s->plt = plt->size;
+
+ adduint8(plt, 0xff);
+ adduint8(plt, 0x25);
+ addaddrplus(plt, lookup(".got", 0), s->got);
+ } else {
+ diag("addpltsym: unsupported binary format");
+ }
+}
+
+static void
+addgotsym(Sym *s)
+{
+ Sym *got, *rel;
+
+ if(s->got >= 0)
+ return;
+
+ adddynsym(s);
+ got = lookup(".got", 0);
+ s->got = got->size;
+ adduint32(got, 0);
+
+ if(iself) {
+ rel = lookup(".rel", 0);
+ addaddrplus(rel, got, s->got);
+ adduint32(rel, ELF32_R_INFO(s->dynid, R_386_GLOB_DAT));
+ } else if(HEADTYPE == 6) { // Mach-O
+ adduint32(lookup(".linkedit.got", 0), s->dynid);
+ } else {
+ diag("addgotsym: unsupported binary format");
+ }
+}
+
+static void
+adddynsym(Sym *s)
+{
+ Sym *d, *str;
+ int t;
+ char *name;
+
+ if(s->dynid >= 0)
+ return;
+
+ if(iself) {
+ s->dynid = nelfsym++;
+
+ d = lookup(".dynsym", 0);
+
+ /* name */
+ name = s->dynimpname;
+ if(name == nil)
+ name = s->name;
+ adduint32(d, addstring(lookup(".dynstr", 0), name));
+
+ /* value */
+ if(s->type == SDYNIMPORT)
+ adduint32(d, 0);
+ else
+ addaddr(d, s);
+
+ /* size */
+ adduint32(d, 0);
+
+ /* type */
+ t = STB_GLOBAL << 4;
+ if(s->dynexport && s->type == STEXT)
+ t |= STT_FUNC;
+ else
+ t |= STT_OBJECT;
+ adduint8(d, t);
+ adduint8(d, 0);
+
+ /* shndx */
+ if(!s->dynexport && s->dynimpname != nil)
+ adduint16(d, SHN_UNDEF);
+ else {
+ switch(s->type) {
+ default:
+ case STEXT:
+ t = 11;
+ break;
+ case SRODATA:
+ t = 12;
+ break;
+ case SDATA:
+ t = 13;
+ break;
+ case SBSS:
+ t = 14;
+ break;
+ }
+ adduint16(d, t);
+ }
+ } else if(HEADTYPE == 6) {
+ // Mach-O symbol nlist32
+ d = lookup(".dynsym", 0);
+ name = s->dynimpname;
+ if(name == nil)
+ name = s->name;
+ s->dynid = d->size/12;
+ // darwin still puts _ prefixes on all C symbols
+ str = lookup(".dynstr", 0);
+ adduint32(d, str->size);
+ adduint8(str, '_');
+ addstring(str, name);
+ adduint8(d, 0x01); // type - N_EXT - external symbol
+ adduint8(d, 0); // section
+ adduint16(d, 0); // desc
+ adduint32(d, 0); // value
+ } else {
+ diag("adddynsym: unsupported binary format");
+ }
+}
+
+void
+adddynlib(char *lib)
+{
+ Sym *s;
+
+ if(!needlib(lib))
+ return;
+
+ if(iself) {
+ s = lookup(".dynstr", 0);
+ if(s->size == 0)
+ addstring(s, "");
+ elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib));
+ } else if(HEADTYPE == 6) { // Mach-O
+ machoadddynlib(lib);
+ } else {
+ diag("adddynlib: unsupported binary format");
+ }
+}
+
void
doelf(void)
{
- Sym *s, *shstrtab, *dynamic, *dynstr, *d;
- int h, nsym, t;
+ Sym *s, *shstrtab, *dynstr;
if(!iself)
return;
elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym");
elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr");
elfstr[ElfStrRel] = addstring(shstrtab, ".rel");
+ elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt");
+ elfstr[ElfStrPlt] = addstring(shstrtab, ".plt");
/* interpreter string */
s = lookup(".interp", 0);
s = lookup(".dynstr", 0);
s->reachable = 1;
s->type = SELFDATA;
- addstring(s, "");
+ if(s->size == 0)
+ addstring(s, "");
dynstr = s;
/* relocation table */
/* global offset table */
s = lookup(".got", 0);
s->reachable = 1;
+ s->type = SDATA; // writable, so not SELFDATA
+
+ /* hash */
+ s = lookup(".hash", 0);
+ s->reachable = 1;
s->type = SELFDATA;
- /* got.plt - ??? */
+ /* got.plt */
s = lookup(".got.plt", 0);
s->reachable = 1;
+ s->type = SDATA; // writable, so not SELFDATA
+
+ s = lookup(".plt", 0);
+ s->reachable = 1;
s->type = SELFDATA;
- /* define dynamic elf table */
- s = lookup(".dynamic", 0);
+ s = lookup(".rel.plt", 0);
s->reachable = 1;
s->type = SELFDATA;
- dynamic = s;
- /*
- * relocation entries for dynimport symbols
- */
- nsym = 1; // sym 0 is reserved
- for(h=0; h<NHASH; h++) {
- for(s=hash[h]; s!=S; s=s->hash) {
- if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
- continue;
+ elfsetupplt();
- if(!s->dynexport) {
- d = lookup(".rel", 0);
- addaddr(d, s);
- adduint32(d, ELF32_R_INFO(nsym, R_386_32));
- }
-
- nsym++;
-
- d = lookup(".dynsym", 0);
- adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname));
- /* value */
- if(!s->dynexport)
- adduint32(d, 0);
- else
- addaddr(d, s);
-
- /* size of object */
- adduint32(d, 0);
-
- /* type */
- t = STB_GLOBAL << 4;
- if(s->dynexport && s->type == STEXT)
- t |= STT_FUNC;
- else
- t |= STT_OBJECT;
- adduint8(d, t);
-
- /* reserved */
- adduint8(d, 0);
-
- /* section where symbol is defined */
- if(!s->dynexport)
- adduint16(d, SHN_UNDEF);
- else {
- switch(s->type) {
- default:
- case STEXT:
- t = 9;
- break;
- case SDATA:
- t = 10;
- break;
- case SBSS:
- t = 11;
- break;
- }
- adduint16(d, t);
- }
-
- if(!s->dynexport && needlib(s->dynimplib))
- elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib));
- }
- }
-
- elfdynhash(nsym);
+ /* define dynamic elf table */
+ s = lookup(".dynamic", 0);
+ s->reachable = 1;
+ s->type = SELFDATA;
/*
* .dynamic table
*/
- s = dynamic;
elfwritedynentsym(s, DT_HASH, lookup(".hash", 0));
elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0));
elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE);
elfwritedynent(s, DT_RELENT, ELF32RELSIZE);
if(rpath)
elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath));
+ elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0));
+ elfwritedynent(s, DT_PLTREL, DT_REL);
+ elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0));
+ elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0));
elfwritedynent(s, DT_NULL, 0);
}
}
if(HEADTYPE == 6)
machlink = domacholink();
+ if(iself) {
+ /* index of elf text section; needed by asmelfsym, double-checked below */
+ /* !debug['d'] causes extra sections before the .text section */
+ elftextsh = 1;
+ if(!debug['d'])
+ elftextsh += 10;
+ }
+
symsize = 0;
spsize = 0;
lcsize = 0;
sh->flags = SHF_ALLOC;
sh->addralign = 1;
shsym(sh, lookup(".dynstr", 0));
+
+ sh = newElfShdr(elfstr[ElfStrRelPlt]);
+ sh->type = SHT_REL;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = ELF32RELSIZE;
+ sh->addralign = 4;
+ sh->link = dynsym;
+ sh->info = eh->shnum; // .plt
+ shsym(sh, lookup(".rel.plt", 0));
+
+ sh = newElfShdr(elfstr[ElfStrPlt]);
+ sh->type = SHT_PROGBITS;
+ sh->flags = SHF_ALLOC+SHF_EXECINSTR;
+ sh->entsize = 4;
+ sh->addralign = 4;
+ shsym(sh, lookup(".plt", 0));
sh = newElfShdr(elfstr[ElfStrHash]);
sh->type = SHT_HASH;
ph->flags = PF_W+PF_R;
ph->align = 4;
+ if(elftextsh != eh->shnum)
+ diag("elftextsh = %d, want %d", elftextsh, eh->shnum);
for(sect=segtext.sect; sect!=nil; sect=sect->next)
elfshbits(sect);
for(sect=segrodata.sect; sect!=nil; sect=sect->next)
for(h=0; h<NHASH; h++) {
for(s=hash[h]; s!=S; s=s->hash) {
- switch(s->type) {
+ switch(s->type&~SSUB) {
case SCONST:
case SRODATA:
case SDATA:
case SELFDATA:
case SMACHO:
+ case SMACHOGOT:
case SWINDOWS:
if(!s->reachable)
continue;
}
for(s = textp; s != nil; s = s->next) {
+ if(s->text == nil)
+ continue;
+
/* filenames first */
for(a=s->autom; a; a=a->link)
if(a->type == D_FILE)
{
int32 off;
uchar siz;
- uchar type;
+ int32 type;
int32 add;
Sym* sym;
};
int32 value;
int32 size;
int32 sig;
+ int32 dynid;
+ int32 plt;
+ int32 got;
Sym* hash; // in hash table
Sym* next; // in text or data list
Sym* sub; // in sub list
+ Sym* outer; // container of sub
Sym* gotype;
char* file;
char* dynimpname;
/* order here is order in output file */
STEXT,
SELFDATA,
+ SMACHOPLT,
SRODATA,
SDATA,
SMACHO, /* Mach-O __nl_symbol_ptr */
+ SMACHOGOT,
SWINDOWS,
SBSS,
SXREF,
+ SMACHODYNSTR,
+ SMACHODYNSYM,
+ SMACHOINDIRECTPLT,
+ SMACHOINDIRECTGOT,
SFILE,
SCONST,
-
+ SDYNIMPORT,
+
SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */
NHASH = 10007,
EXTERN int tlsoffset;
EXTERN Sym* adrgotype; // type symbol on last Adr read
EXTERN Sym* fromgotype; // type symbol on last p->from read
-
+EXTERN int elftextsh;
extern Optab optab[];
extern char* anames[];
#include "../ld/elf.h"
#include "../ld/pe.h"
+void dynreloc(void);
+
/*
* divide-and-conquer list-link
* sort of Sym* structures.
vlong o;
uchar *cast;
+ cursym = s;
memset(&p, 0, sizeof p);
for(r=s->r; r<s->r+s->nr; r++) {
off = r->off;
siz = r->siz;
+ if(off < 0 || off+(siz&~Rbig) > s->np) {
+ diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np);
+ continue;
+ }
+ if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) {
+ diag("%s: not defined", r->sym->name);
+ continue;
+ }
+ if(r->type >= 256)
+ continue;
+
+ if(r->sym != S && (r->sym->type == SDYNIMPORT || r->sym->dynimpname != nil))
+ diag("unhandled relocation for %s (rtype %d)", r->sym->name, r->type);
+
+ if(r->sym != S && !r->sym->reachable)
+ diag("unreachable sym in relocation: %s %s", s->name, r->sym->name);
+
switch(r->type) {
default:
- diag("unknown reloc %d", r->type);
+ o = 0;
+ if(archreloc(r, s, &o) < 0)
+ diag("unknown reloc %d", r->type);
+ break;
case D_ADDR:
- o = symaddr(r->sym);
+ o = symaddr(r->sym) + r->add;
break;
case D_PCREL:
- o = symaddr(r->sym) - (s->value + r->off + r->siz);
+ o = symaddr(r->sym) + r->add - (s->value + r->off + r->siz);
break;
case D_SIZE:
- o = r->sym->size;
+ o = r->sym->size + r->add;
break;
}
- o += r->add;
+//print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "<nil>", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o);
switch(siz) {
default:
- diag("bad reloc size %#ux", siz);
+ cursym = s;
+ diag("bad reloc size %#ux for %s", siz, r->sym->name);
case 4 + Rbig:
fl = o;
s->p[off] = fl>>24;
relocsym(s);
}
+void
+dynrelocsym(Sym *s)
+{
+ Reloc *r;
+
+ for(r=s->r; r<s->r+s->nr; r++)
+ if(r->sym->type == SDYNIMPORT || r->type >= 256)
+ adddynrel(s, r);
+}
+
+void
+dynreloc(void)
+{
+ Sym *s;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f reloc\n", cputime());
+ Bflush(&bso);
+
+ for(s=textp; s!=S; s=s->next)
+ dynrelocsym(s);
+ for(s=datap; s!=S; s=s->next)
+ dynrelocsym(s);
+ if(iself)
+ elfdynhash();
+}
+
void
symgrow(Sym *s, int32 siz)
{
uchar *cast;
vlong o;
Reloc *r;
-
+
off = p->from.offset;
siz = p->datasize;
symgrow(s, off+siz);
uchar *p, *ep;
for(sym = allsym; sym != nil; sym = sym->next)
- if(sym->value >= addr)
+ if(!(sym->type&SSUB) && sym->value >= addr)
break;
eaddr = addr+size;
for(; sym != nil; sym = sym->next) {
+ if(sym->type&SSUB)
+ continue;
if(sym->value >= eaddr)
break;
if(sym->value < addr) {
continue;
}
- Bprint(&bso, "%.6llux\t%-20s | %P\n", (vlong)addr, sym->name, p);
+ Bprint(&bso, "%.6llux\t%-20s | %P\n", sym->value, sym->name, p);
for(p = p->link; p != P; p = p->link) {
if(p->link != P)
epc = p->link->pc;
r = s->size;
s->size += wid;
symgrow(s, s->size);
+ assert(r+wid <= s->size);
fl = v;
cast = (uchar*)&fl;
switch(wid) {
}
vlong
-addaddr(Sym *s, Sym *t)
+addaddrplus(Sym *s, Sym *t, int32 add)
{
vlong i;
Reloc *r;
r->off = i;
r->siz = PtrSize;
r->type = D_ADDR;
+ r->add = add;
+ return i;
+}
+
+vlong
+addpcrelplus(Sym *s, Sym *t, int32 add)
+{
+ vlong i;
+ Reloc *r;
+
+ if(s->type == 0)
+ s->type = SDATA;
+ s->reachable = 1;
+ i = s->size;
+ s->size += 4;
+ symgrow(s, s->size);
+ r = addrel(s);
+ r->sym = t;
+ r->off = i;
+ r->add = add;
+ r->type = D_PCREL;
+ r->siz = 4;
return i;
}
+vlong
+addaddr(Sym *s, Sym *t)
+{
+ return addaddrplus(s, t, 0);
+}
+
vlong
addsize(Sym *s, Sym *t)
{
{
int32 h, t, datsize;
Section *sect;
- Sym *s, *last;
+ Sym *s, *last, **l;
if(debug['v'])
Bprint(&bso, "%5.2f dodata\n", cputime());
diag("%s: initialize bounds (%lld < %d)",
s->name, s->size, s->np);
}
+
+ /*
+ * now that we have the datap list, but before we start
+ * to assign addresses, record all the necessary
+ * dynamic relocations. these will grow the relocation
+ * symbol, which is itself data.
+ */
+ dynreloc();
+
+ /* some symbols may no longer belong in datap (Mach-O) */
+ for(l=&datap; (s=*l) != nil; ) {
+ if(s->type <= STEXT || SXREF <= s->type)
+ *l = s->next;
+ else
+ l = &s->next;
+ }
+ *l = nil;
+
datap = datsort(datap);
/*
sect->len = datsize - sect->vaddr;
}
+// assign addresses to text
+void
+textaddress(void)
+{
+ uvlong va;
+ Prog *p;
+ Section *sect;
+ Sym *sym, *sub;
+
+ addsection(&segtext, ".text", 05);
+
+ // Assign PCs in text segment.
+ // Could parallelize, by assigning to text
+ // and then letting threads copy down, but probably not worth it.
+ sect = segtext.sect;
+ va = INITTEXT;
+ sect->vaddr = va;
+ for(sym = textp; sym != nil; sym = sym->next) {
+ if(sym->type & SSUB)
+ continue;
+ sym->value = 0;
+ for(sub = sym; sub != S; sub = sub->sub) {
+ sub->value += va;
+ for(p = sub->text; p != P; p = p->link)
+ p->pc += sub->value;
+ }
+ if(sym->size == 0 && sym->sub != S) {
+ cursym = sym;
+ }
+ va += sym->size;
+ }
+ sect->len = va - sect->vaddr;
+}
+
// assign addresses
void
address(void)
uvlong va;
va = INITTEXT;
- segtext.rwx = 05;
+ segtext.rwx = 07;
segtext.vaddr = va;
segtext.fileoff = HEADR;
for(s=segtext.sect; s != nil; s=s->next) {
{
ElfPhdr *e;
- e = malloc(sizeof *e);
- memset(e, 0, sizeof *e);
+ e = mal(sizeof *e);
if (hdr.phnum >= NSECT)
diag("too many phdrs");
else
{
ElfShdr *e;
- e = malloc(sizeof *e);
- memset(e, 0, sizeof *e);
+ e = mal(sizeof *e);
e->name = name;
if (hdr.shnum >= NSECT) {
diag("too many shdrs");
sh->size = n;
}
+extern int nelfsym;
+
void
-elfdynhash(int nsym)
+elfdynhash(void)
{
Sym *s, *sy;
int i, h, nbucket, b;
uchar *pc;
uint32 hc, g;
uint32 *chain, *buckets;
+ int nsym;
+ char *name;
+
+ if(!iself)
+ return;
+ nsym = nelfsym;
s = lookup(".hash", 0);
- s->type = SELFDATA; // TODO: rodata
+ s->type = SELFDATA;
s->reachable = 1;
i = nsym;
}
chain = malloc(nsym * sizeof(uint32));
- memset(chain, 0, nsym * sizeof(uint32));
buckets = malloc(nbucket * sizeof(uint32));
+ if(chain == nil || buckets == nil) {
+ cursym = nil;
+ diag("out of memory");
+ errorexit();
+ }
+ memset(chain, 0, nsym * sizeof(uint32));
memset(buckets, 0, nbucket * sizeof(uint32));
- i = 1;
for(h = 0; h<NHASH; h++) {
for(sy=hash[h]; sy!=S; sy=sy->hash) {
- if (!sy->reachable || (sy->type != STEXT && sy->type != SDATA && sy->type != SBSS) || sy->dynimpname == nil)
+ if (sy->dynid <= 0)
continue;
hc = 0;
- for(pc = (uchar*)sy->dynimpname; *pc; pc++) {
+ name = sy->dynimpname;
+ if(name == nil)
+ name = sy->name;
+ for(pc = (uchar*)name; *pc; pc++) {
hc = (hc<<4) + *pc;
g = hc & 0xf0000000;
hc ^= g >> 24;
}
b = hc % nbucket;
- chain[i] = buckets[b];
- buckets[b] = i;
- i++;
+ chain[sy->dynid] = buckets[b];
+ buckets[b] = sy->dynid;
}
}
free(chain);
free(buckets);
+
+ elfwritedynent(lookup(".dynamic", 0), DT_NULL, 0);
}
ElfPhdr*
// use malloc, not mal, so that caller can free
w0 = malloc(strlen(t0) + strlen(pkg)*n);
+ if(w0 == nil) {
+ diag("out of memory");
+ errorexit();
+ }
w = w0;
for(p=t=t0; (p=strstr(p, "\"\".")) != nil; p=t) {
memmove(w, t, p - t);
// successful parse: now can edit the line
*strchr(name, ' ') = 0;
*strchr(def, ' ') = 0;
+
+ if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) {
+ // allow #pragma dynimport _ _ "foo.so"
+ // to force a link of foo.so.
+ adddynlib(lib);
+ continue;
+ }
name = expandpkg(name, pkg);
-
s = lookup(name, 0);
- s->dynimplib = lib;
- s->dynimpname = def;
+ if(s->type == 0 || s->type == SXREF) {
+ s->dynimplib = lib;
+ s->dynimpname = def;
+ s->type = SDYNIMPORT;
+ }
}
return;
mark(s->r[i].sym);
if(s->gotype)
mark(s->gotype);
+ if(s->sub)
+ mark(s->sub);
+ if(s->outer)
+ mark(s->outer);
}
static char*
void
ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence)
{
- static int files;
- static char **filen;
- char **nfilen, *line;
- int i, n, c1, c2, c3;
+ char *line;
+ int n, c1, c2, c3, c4;
+ uint32 magic;
vlong import0, import1, eof;
char src[1024];
eof = Boffset(f) + len;
src[0] = '\0';
- // don't load individual object more than once.
- // happens with import of .6 files because of loop in xresolv.
- // doesn't happen with .a because SYMDEF is consulted
- // first to decide whether each individual object file is needed.
- for(i=0; i<files; i++)
- if(strcmp(filen[i], pn) == 0)
- return;
+ pn = strdup(pn);
+
+ USED(c4);
+ USED(magic);
- if((files&15) == 0){
- nfilen = malloc((files+16)*sizeof(char*));
- memmove(nfilen, filen, files*sizeof(char*));
- free(filen);
- filen = nfilen;
+ c1 = Bgetc(f);
+ c2 = Bgetc(f);
+ c3 = Bgetc(f);
+ c4 = Bgetc(f);
+ Bungetc(f);
+ Bungetc(f);
+ Bungetc(f);
+ Bungetc(f);
+
+ magic = c1<<24 | c2<<16 | c3<<8 | c4;
+ if(magic == 0x7f454c46) { // \x7F E L F
+ ldelf(f, pkg, len, pn);
+ return;
+ }
+ if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) {
+ ldmacho(f, pkg, len, pn);
+ return;
}
- pn = strdup(pn);
- filen[files++] = pn;
/* check the header */
line = Brdline(f, '\n');
if(debug['v'] > 1)
Bprint(&bso, "lookup %s\n", symb);
+ s->dynid = -1;
+ s->plt = -1;
+ s->got = -1;
s->name = mal(l + 1);
memmove(s->name, symb, l);
n = (n+7)&~7;
if(n > NHUNK) {
v = malloc(n);
+ if(v == nil) {
+ diag("out of memory");
+ errorexit();
+ }
memset(v, 0, n);
return v;
}
if(n > nhunk) {
hunk = malloc(NHUNK);
+ if(hunk == nil) {
+ diag("out of memory");
+ errorexit();
+ }
nhunk = NHUNK;
}
}
}
}
+
+uint16
+le16(uchar *b)
+{
+ return b[0] | b[1]<<8;
+}
+
+uint32
+le32(uchar *b)
+{
+ return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+}
+
+uint64
+le64(uchar *b)
+{
+ return le32(b) | (uint64)le32(b+4)<<32;
+}
+
+uint16
+be16(uchar *b)
+{
+ return b[0]<<8 | b[1];
+}
+
+uint32
+be32(uchar *b)
+{
+ return b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
+}
+
+uint64
+be64(uchar *b)
+{
+ return (uvlong)be32(b)<<32 | be32(b+4);
+}
+
+Endian be = { be16, be32, be64 };
+Endian le = { le16, le32, le64 };
void symtab(void);
void Lflag(char *arg);
void usage(void);
+void adddynrel(Sym*, Reloc*);
void ldobj1(Biobuf *f, char*, int64 len, char *pn);
void ldobj(Biobuf*, char*, int64, char*, int);
+void ldelf(Biobuf*, char*, int64, char*);
+void ldmacho(Biobuf*, char*, int64, char*);
void ldpkg(Biobuf*, char*, int64, char*, int);
void mark(Sym *s);
void mkfwd(void);
vlong adduint32(Sym*, uint32);
vlong adduint64(Sym*, uint64);
vlong addaddr(Sym*, Sym*);
+vlong addaddrplus(Sym*, Sym*, int32);
+vlong addpcrelplus(Sym*, Sym*, int32);
vlong addsize(Sym*, Sym*);
vlong adduint8(Sym*, uint8);
vlong adduint16(Sym*, uint16);
void strnput(char*, int);
void dodata(void);
void address(void);
+void textaddress(void);
void genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*));
vlong datoff(vlong);
+void adddynlib(char*);
+int archreloc(Reloc*, Sym*, vlong*);
int pathchar(void);
void* mal(uint32);
void unmal(void*, uint32);
void mywhatsys(void);
+int rbyoff(const void*, const void*);
+
+uint16 le16(uchar*);
+uint32 le32(uchar*);
+uint64 le64(uchar*);
+uint16 be16(uchar*);
+uint32 be32(uchar*);
+uint64 be64(uchar*);
+
+typedef struct Endian Endian;
+struct Endian
+{
+ uint16 (*e16)(uchar*);
+ uint32 (*e32)(uchar*);
+ uint64 (*e64)(uchar*);
+};
+
+extern Endian be, le;
// relocation size bits
enum {
// Generic linking code.
-static uchar *linkdata;
-static uint32 nlinkdata;
-static uint32 mlinkdata;
-
-static uchar *strtab;
-static uint32 nstrtab;
-static uint32 mstrtab;
-
struct Expsym
{
int off;
Sym* s;
} *expsym;
static int nexpsym;
-static int nimpsym;
static char **dylib;
static int ndylib;
LPUT(t->reloc);
LPUT(t->nreloc);
LPUT(t->flag);
- LPUT(0); /* reserved */
- LPUT(0); /* reserved */
+ LPUT(t->res1); /* reserved */
+ LPUT(t->res2); /* reserved */
LPUT(0); /* reserved */
} else {
strnput(t->name, 16);
LPUT(t->reloc);
LPUT(t->nreloc);
LPUT(t->flag);
- LPUT(0); /* reserved */
- LPUT(0); /* reserved */
+ LPUT(t->res1); /* reserved */
+ LPUT(t->res2); /* reserved */
}
}
}
return cpos() - o1;
}
-static void*
-grow(uchar **dat, uint32 *ndat, uint32 *mdat, uint32 n)
-{
- uchar *p;
- uint32 old;
-
- if(*ndat+n > *mdat) {
- old = *mdat;
- *mdat = (*ndat+n)*2 + 128;
- *dat = realloc(*dat, *mdat);
- if(*dat == 0) {
- diag("out of memory");
- errorexit();
- }
- memset(*dat+old, 0, *mdat-old);
- }
- p = *dat + *ndat;
- *ndat += n;
- return p;
-}
-
-static int
-needlib(char *name)
-{
- char *p;
- Sym *s;
-
- /* reuse hash code in symbol table */
- p = smprint(".machoload.%s", name);
- s = lookup(p, 0);
- if(s->type == 0) {
- s->type = 100; // avoid SDATA, etc.
- return 1;
- }
- return 0;
-}
-
void
domacho(void)
{
- int h, ptrsize, t;
- char *p;
- uchar *dat;
- uint32 x;
- Sym *s, *smacho;
- Sym **impsym;
-
- ptrsize = 4;
- if(macho64)
- ptrsize = 8;
-
- // empirically, string table must begin with " \x00".
- if(!debug['d'])
- *(char*)grow(&strtab, &nstrtab, &mstrtab, 2) = ' ';
-
- impsym = nil;
- for(h=0; h<NHASH; h++) {
- for(s=hash[h]; s!=S; s=s->hash) {
- if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
- continue;
- if(debug['d']) {
- diag("cannot use dynamic loading and -d");
- errorexit();
- }
- if(!s->dynexport) {
- if(nimpsym%32 == 0) {
- impsym = realloc(impsym, (nimpsym+32)*sizeof impsym[0]);
- if(impsym == nil) {
- diag("out of memory");
- errorexit();
- }
- }
- impsym[nimpsym++] = s;
- continue;
- }
-
- /* symbol table entry - darwin still puts _ prefixes on all C symbols */
- x = nstrtab;
- p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1);
- *p++ = '_';
- strcpy(p, s->dynimpname);
-
- dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize);
- dat[0] = x;
- dat[1] = x>>8;
- dat[2] = x>>16;
- dat[3] = x>>24;
-
- dat[4] = 0x0f; // type: N_SECT | N_EXT - external, defined in sect
- switch(s->type) {
- default:
- case STEXT:
- t = 1;
- break;
- case SDATA:
- t = 2;
- break;
- case SBSS:
- t = 4;
- break;
- }
- dat[5] = t; // sect: section number
-
- if (nexpsym%32 == 0) {
- expsym = realloc(expsym, (nexpsym+32)*sizeof expsym[0]);
- if (expsym == nil) {
- diag("out of memory");
- errorexit();
- }
- }
- expsym[nexpsym].off = nlinkdata - ptrsize;
- expsym[nexpsym++].s = s;
- }
- }
+ Sym *s;
- smacho = lookup("__nl_symbol_ptr", 0);
- smacho->type = SMACHO;
- smacho->reachable = 1;
- for(h=0; h<nimpsym; h++) {
- s = impsym[h];
- s->type = SMACHO | SSUB;
- s->sub = smacho->sub;
- smacho->sub = s;
- s->value = (nexpsym+h) * ptrsize;
- s->reachable = 1;
-
- /* symbol table entry - darwin still puts _ prefixes on all C symbols */
- x = nstrtab;
- p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1);
- *p++ = '_';
- strcpy(p, s->dynimpname);
-
- dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize);
- dat[0] = x;
- dat[1] = x>>8;
- dat[2] = x>>16;
- dat[3] = x>>24;
-
- dat[4] = 0x01; // type: N_EXT - external symbol
-
- if(needlib(s->dynimplib)) {
- if(ndylib%32 == 0) {
- dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]);
- if(dylib == nil) {
- diag("out of memory");
- errorexit();
- }
- }
- dylib[ndylib++] = s->dynimplib;
- }
- }
- free(impsym);
-
- /*
- * list of symbol table indexes.
- * we don't take advantage of the opportunity
- * to order the symbol table differently from
- * this list, so it is boring: 0 1 2 3 4 ...
- */
- for(x=0; x<nexpsym+nimpsym; x++) {
- dat = grow(&linkdata, &nlinkdata, &mlinkdata, 4);
- dat[0] = x;
- dat[1] = x>>8;
- dat[2] = x>>16;
- dat[3] = x>>24;
- }
+ if(debug['d'])
+ return;
- smacho->size = (nexpsym+nimpsym) * ptrsize;
- if(smacho->size == 0)
- smacho->reachable = 0;
+ // empirically, string table must begin with " \x00".
+ s = lookup(".dynstr", 0);
+ s->type = SMACHODYNSTR;
+ s->reachable = 1;
+ adduint8(s, ' ');
+ adduint8(s, '\0');
+
+ s = lookup(".dynsym", 0);
+ s->type = SMACHODYNSYM;
+ s->reachable = 1;
+
+ s = lookup(".plt", 0); // will be __symbol_stub
+ s->type = SMACHOPLT;
+ s->reachable = 1;
+
+ s = lookup(".got", 0); // will be __nl_symbol_ptr
+ s->type = SMACHOGOT;
+ s->reachable = 1;
+
+ s = lookup(".linkedit.plt", 0); // indirect table for .plt
+ s->type = SMACHOINDIRECTPLT;
+ s->reachable = 1;
+
+ s = lookup(".linkedit.got", 0); // indirect table for .got
+ s->type = SMACHOINDIRECTGOT;
+ s->reachable = 1;
}
-vlong
-domacholink(void)
+void
+machoadddynlib(char *lib)
{
- int i;
- uchar *p;
- Sym *s;
- uint64 val;
- Sym *smacho;
-
- smacho = lookup("__nl_symbol_ptr", 0);
-
- linkoff = 0;
- if(nlinkdata > 0 || nstrtab > 0) {
- linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen - smacho->size, INITRND);
- seek(cout, linkoff, 0);
-
- for(i = 0; i<nexpsym; ++i) {
- s = expsym[i].s;
- val = s->value;
- if(s->type == SXREF)
- diag("export of undefined symbol %s", s->name);
- if (s->type != STEXT)
- val += segdata.vaddr;
- p = linkdata+expsym[i].off;
- p[0] = val;
- p[1] = val >> 8;
- p[2] = val >> 16;
- p[3] = val >> 24;
- if (macho64) {
- p[4] = val >> 32;
- p[5] = val >> 40;
- p[6] = val >> 48;
- p[7] = val >> 56;
- }
+ if(ndylib%32 == 0) {
+ dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]);
+ if(dylib == nil) {
+ diag("out of memory");
+ errorexit();
}
-
- ewrite(cout, linkdata, nlinkdata);
- ewrite(cout, strtab, nstrtab);
}
- return rnd(nlinkdata+nstrtab, INITRND);
+ dylib[ndylib++] = lib;
}
void
{
vlong v, w;
vlong va;
- int a, i, ptrsize;
+ int a, i;
char *pkgroot;
MachoHdr *mh;
MachoSect *msect;
MachoSeg *ms;
MachoDebug *md;
MachoLoad *ml;
- Sym *smacho;
+ Sym *s;
/* apple MACH */
va = INITTEXT - HEADR;
case '6':
mh->cpu = MACHO_CPU_AMD64;
mh->subcpu = MACHO_SUBCPU_X86;
- ptrsize = 8;
break;
case '8':
mh->cpu = MACHO_CPU_386;
mh->subcpu = MACHO_SUBCPU_X86;
- ptrsize = 4;
break;
}
/* text */
v = rnd(HEADR+segtext.len, INITRND);
- ms = newMachoSeg("__TEXT", 1);
+ ms = newMachoSeg("__TEXT", 2);
ms->vaddr = va;
ms->vsize = v;
ms->filesize = v;
msect->size = segtext.sect->len;
msect->off = INITTEXT - va;
msect->flag = 0x400; /* flag - some instructions */
+
+ s = lookup(".plt", 0);
+ if(s->size > 0) {
+ msect = newMachoSect(ms, "__symbol_stub1");
+ msect->addr = symaddr(s);
+ msect->size = s->size;
+ msect->off = ms->fileoffset + msect->addr - ms->vaddr;
+ msect->flag = 0x80000408; /* flag */
+ msect->res1 = 0; /* index into indirect symbol table */
+ msect->res2 = 6; /* size of stubs */
+ }
/* data */
- smacho = lookup("__nl_symbol_ptr", 0);
w = segdata.len;
- ms = newMachoSeg("__DATA", 2+(smacho->size > 0));
+ ms = newMachoSeg("__DATA", 3);
ms->vaddr = va+v;
ms->vsize = w;
ms->fileoffset = v;
msect = newMachoSect(ms, "__data");
msect->addr = va+v;
- msect->size = segdata.filelen - smacho->size;
+ msect->size = symaddr(lookup(".got", 0)) - msect->addr;
msect->off = v;
- if(smacho->size > 0) {
+ s = lookup(".got", 0);
+ if(s->size > 0) {
msect = newMachoSect(ms, "__nl_symbol_ptr");
- msect->addr = smacho->value;
- msect->size = smacho->size;
+ msect->addr = symaddr(s);
+ msect->size = s->size;
msect->off = datoff(msect->addr);
msect->align = 2;
msect->flag = 6; /* section with nonlazy symbol pointers */
- /*
- * The reserved1 field is supposed to be the index of
- * the first entry in the list of symbol table indexes
- * in isymtab for the symbols we need. We only use
- * pointers, so we need the entire list, so the index
- * here should be 0, which luckily is what the Mach-O
- * writing code emits by default for this not really reserved field.
- msect->reserved1 = 0; - first indirect symbol table entry we need
- */
+ msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */
}
msect = newMachoSect(ms, "__bss");
}
if(!debug['d']) {
- int nsym;
+ Sym *s1, *s2, *s3, *s4;
- nsym = smacho->size/ptrsize;
+ // must match domacholink below
+ s1 = lookup(".dynsym", 0);
+ s2 = lookup(".dynstr", 0);
+ s3 = lookup(".linkedit.plt", 0);
+ s4 = lookup(".linkedit.got", 0);
ms = newMachoSeg("__LINKEDIT", 0);
ms->vaddr = va+v+rnd(segdata.len, INITRND);
- ms->vsize = nlinkdata+nstrtab;
+ ms->vsize = s1->size + s2->size + s3->size + s4->size;
ms->fileoffset = linkoff;
- ms->filesize = nlinkdata+nstrtab;
+ ms->filesize = ms->vsize;
ms->prot1 = 7;
ms->prot2 = 3;
ml = newMachoLoad(2, 4); /* LC_SYMTAB */
ml->data[0] = linkoff; /* symoff */
- ml->data[1] = nsym; /* nsyms */
- ml->data[2] = linkoff + nlinkdata; /* stroff */
- ml->data[3] = nstrtab; /* strsize */
+ ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */
+ ml->data[2] = linkoff + s1->size; /* stroff */
+ ml->data[3] = s2->size; /* strsize */
ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */
ml->data[0] = 0; /* ilocalsym */
ml->data[1] = 0; /* nlocalsym */
ml->data[2] = 0; /* iextdefsym */
- ml->data[3] = nexpsym; /* nextdefsym */
- ml->data[4] = nexpsym; /* iundefsym */
- ml->data[5] = nimpsym; /* nundefsym */
+ ml->data[3] = 0; /* nextdefsym */ // TODO nexpsym
+ ml->data[4] = 0; /* iundefsym */ // TODO nexpsym
+ ml->data[5] = s1->size / (macho64 ? 16 : 12); /* nundefsym */
ml->data[6] = 0; /* tocoffset */
ml->data[7] = 0; /* ntoc */
ml->data[8] = 0; /* modtaboff */
ml->data[9] = 0; /* nmodtab */
ml->data[10] = 0; /* extrefsymoff */
ml->data[11] = 0; /* nextrefsyms */
- ml->data[12] = linkoff + nlinkdata - nsym*4; /* indirectsymoff */
- ml->data[13] = nsym; /* nindirectsyms */
+ ml->data[12] = linkoff + s1->size + s2->size; /* indirectsymoff */
+ ml->data[13] = (s3->size + s4->size) / 4; /* nindirectsyms */
ml->data[14] = 0; /* extreloff */
ml->data[15] = 0; /* nextrel */
ml->data[16] = 0; /* locreloff */
if(a > MACHORESERVE)
diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE);
}
+
+vlong
+domacholink(void)
+{
+ int size;
+ Sym *s1, *s2, *s3, *s4;
+
+ // write data that will be linkedit section
+ s1 = lookup(".dynsym", 0);
+ s2 = lookup(".dynstr", 0);
+ s3 = lookup(".linkedit.plt", 0);
+ s4 = lookup(".linkedit.got", 0);
+
+ while(s2->size%4)
+ adduint8(s2, 0);
+
+ size = s1->size + s2->size + s3->size + s4->size;
+
+ if(size > 0) {
+ linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);
+ seek(cout, linkoff, 0);
+
+ ewrite(cout, s1->p, s1->size);
+ ewrite(cout, s2->p, s2->size);
+ ewrite(cout, s3->p, s3->size);
+ ewrite(cout, s4->p, s4->size);
+ }
+
+ return rnd(size, INITRND);
+}
uint32 reloc;
uint32 nreloc;
uint32 flag;
+ uint32 res1;
+ uint32 res2;
};
typedef struct MachoSeg MachoSeg;
MACHO32SYMSIZE = 12,
MACHO64SYMSIZE = 16,
+
+ MACHO_X86_64_RELOC_UNSIGNED = 0,
+ MACHO_X86_64_RELOC_SIGNED = 1,
+ MACHO_X86_64_RELOC_BRANCH = 2,
+ MACHO_X86_64_RELOC_GOT_LOAD = 3,
+ MACHO_X86_64_RELOC_GOT = 4,
+ MACHO_X86_64_RELOC_SUBTRACTOR = 5,
+ MACHO_X86_64_RELOC_SIGNED_1 = 6,
+ MACHO_X86_64_RELOC_SIGNED_2 = 7,
+ MACHO_X86_64_RELOC_SIGNED_4 = 8,
+
+ MACHO_GENERIC_RELOC_VANILLA = 0,
+
+ MACHO_FAKE_GOTPCREL = 100,
};
void domacho(void);