diff options
Diffstat (limited to 'src/cmd/ld/ldelf.c')
-rw-r--r-- | src/cmd/ld/ldelf.c | 237 |
1 files changed, 164 insertions, 73 deletions
diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index bd4f3e7d8..2bbf4f83e 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -308,9 +308,19 @@ uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' }; static ElfSect* section(ElfObj*, char*); static int map(ElfObj*, ElfSect*); -static int readsym(ElfObj*, int i, ElfSym*); +static int readsym(ElfObj*, int i, ElfSym*, int); static int reltype(char*, int, uchar*); +int +valuecmp(Sym *a, Sym *b) +{ + if(a->value < b->value) + return -1; + if(a->value > b->value) + return +1; + return 0; +} + void ldelf(Biobuf *f, char *pkg, int64 len, char *pn) { @@ -327,8 +337,10 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) Endian *e; Reloc *r, *rp; Sym *s; + Sym **symbols; + + symbols = nil; - USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); @@ -516,7 +528,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) if(sect->type != ElfSectNobits && map(obj, sect) < 0) goto bad; - name = smprint("%s(%s)", pn, sect->name); + name = smprint("%s(%s)", pkg, sect->name); s = lookup(name, version); free(name); switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { @@ -539,15 +551,99 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) } s->size = sect->size; s->align = sect->align; + sect->sym = s; + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols = malloc(obj->nsymtab * sizeof(symbols[0])); + if(symbols == nil) { + diag("out of memory"); + errorexit(); + } + for(i=1; i<obj->nsymtab; i++) { + if(readsym(obj, i, &sym, 1) < 0) + goto bad; + symbols[i] = sym.sym; + if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) + continue; + if(sym.shndx == ElfSymShnCommon) { + s = sym.sym; + if(s->size < sym.size) + s->size = sym.size; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + continue; + } + if(sym.shndx >= obj->nsect || sym.shndx == 0) + continue; + // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols + if(sym.sym == S) + continue; + sect = obj->sect+sym.shndx; + if(sect->sym == nil) { + diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); + continue; + } + s = sym.sym; + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | (s->type&~SMASK) | SSUB; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + s->value = sym.value; + s->size = sym.size; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) { + if(!s->dupok) + diag("%s: duplicate definition of %s", pn, s->name); + } else { + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; i<obj->nsect; i++) { + s = obj->sect[i].sym; + if(s == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); if(s->type == STEXT) { if(etextp) etextp->next = s; else textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } - sect->sym = s; - } + } // load relocations for(i=0; i<obj->nsect; i++) { @@ -588,14 +684,24 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) p += 4; } } - if(readsym(obj, info>>32, &sym) < 0) - goto bad; - if(sym.sym == nil) { - werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", - sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); - goto bad; + if((info & 0xffffffff) == 0) { // skip R_*_NONE relocation + j--; + n--; + continue; + } + if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol + rp->sym = S; + } else { + if(readsym(obj, info>>32, &sym, 0) < 0) + goto bad; + sym.sym = symbols[info>>32]; + if(sym.sym == nil) { + werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", + sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); + goto bad; + } + rp->sym = sym.sym; } - rp->sym = sym.sym; rp->type = reltype(pn, (uint32)info, &rp->siz); if(rela) rp->add = add; @@ -615,65 +721,13 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->r = r; s->nr = n; } + free(symbols); - // enter sub-symbols into symbol table. - // symbol 0 is the null symbol. - for(i=1; i<obj->nsymtab; i++) { - if(readsym(obj, i, &sym) < 0) - goto bad; - if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) - continue; - if(sym.shndx == ElfSymShnCommon) { - s = sym.sym; - if(s->size < sym.size) - s->size = sym.size; - if(s->type == 0 || s->type == SXREF) - s->type = SBSS; - continue; - } - if(sym.shndx >= obj->nsect || sym.shndx == 0) - continue; - sect = obj->sect+sym.shndx; - if(sect->sym == nil) { - diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); - continue; - } - s = sym.sym; - s->sub = sect->sym->sub; - sect->sym->sub = s; - s->type = sect->sym->type | SSUB; - if(!s->dynexport) { - s->dynimplib = nil; // satisfy dynimport - s->dynimpname = nil; // satisfy dynimport - } - s->value = sym.value; - s->size = sym.size; - s->outer = sect->sym; - if(sect->sym->type == STEXT) { - Prog *p; - - if(s->text != P) - diag("%s: duplicate definition of %s", pn, s->name); - // build a TEXT instruction with a unique pc - // just to make the rest of the linker happy. - p = prg(); - p->as = ATEXT; - p->from.type = D_EXTERN; - p->from.sym = s; - p->textflag = 7; - p->to.type = D_CONST; - p->link = nil; - p->pc = pc++; - s->text = p; - - etextp->next = s; - etextp = s; - } - } return; bad: diag("%s: malformed elf file: %r", pn); + free(symbols); } static ElfSect* @@ -707,7 +761,7 @@ map(ElfObj *obj, ElfSect *sect) } static int -readsym(ElfObj *obj, int i, ElfSym *sym) +readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) { Sym *s; @@ -715,6 +769,9 @@ readsym(ElfObj *obj, int i, ElfSym *sym) werrstr("invalid elf symbol index"); return -1; } + if(i == 0) { + diag("readym: read null symbol!"); + } if(obj->is64) { ElfSymBytes64 *b; @@ -743,8 +800,6 @@ readsym(ElfObj *obj, int i, ElfSym *sym) s = nil; if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) sym->name = ".got"; - if(strcmp(sym->name, "__stack_chk_fail_local") == 0) - sym->other = 0; // rewrite hidden -> default visibility switch(sym->type) { case ElfSymTypeSection: s = obj->sect[sym->shndx].sym; @@ -754,13 +809,37 @@ readsym(ElfObj *obj, int i, ElfSym *sym) case ElfSymTypeNone: switch(sym->bind) { case ElfSymBindGlobal: - if(sym->other != 2) { + if(needSym) { s = lookup(sym->name, 0); - break; + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See http://codereview.appspot.com/5823055/ + // comment #5 for details. + if(s && sym->other == 2) { + s->type |= SHIDDEN; + s->dupok = 1; + } } - // fall through + break; case ElfSymBindLocal: - s = lookup(sym->name, version); + if(!(thechar == '5' && (strcmp(sym->name, "$a") == 0 || strcmp(sym->name, "$d") == 0))) // binutils for arm generate these mapping symbols, ignore these + if(needSym) { + // local names and hidden visiblity global names are unique + // and should only reference by its index, not name, so we + // don't bother to add them into hash table + s = newsym(sym->name, version); + s->type |= SHIDDEN; + } + break; + case ElfSymBindWeak: + if(needSym) { + s = newsym(sym->name, 0); + if(sym->other == 2) + s->type |= SHIDDEN; + } break; default: werrstr("%s: invalid symbol binding %d", sym->name, sym->bind); @@ -797,6 +876,18 @@ 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('5', R_ARM_ABS32): + case R('5', R_ARM_GOT32): + case R('5', R_ARM_PLT32): + case R('5', R_ARM_GOTOFF): + case R('5', R_ARM_GOTPC): + case R('5', R_ARM_THM_PC22): + case R('5', R_ARM_REL32): + case R('5', R_ARM_CALL): + case R('5', R_ARM_V4BX): + case R('5', R_ARM_GOT_PREL): + case R('5', R_ARM_PC24): + case R('5', R_ARM_JUMP24): case R('6', R_X86_64_PC32): case R('6', R_X86_64_PLT32): case R('6', R_X86_64_GOTPCREL): |