diff options
author | Chao-ying Fu <fu@mips.com> | 2011-11-23 16:31:14 -0800 |
---|---|---|
committer | Logan Chien <loganchien@google.com> | 2011-11-26 18:51:57 +0800 |
commit | 4584cb1a038895f1a9a144bbdb293613373b24ad (patch) | |
tree | 195763cd0f84b65be0be21927c5e8a60e43a7df1 | |
parent | 2050ba7a377e6fb038a36b03cd333fcf91112825 (diff) | |
download | linkloader-4584cb1a038895f1a9a144bbdb293613373b24ad.tar.gz |
Fixed AHL calculation.
Change-Id: Iad80b33d9c490088e664c3b7a9e4c91e09b6448c
-rw-r--r-- | include/impl/ELFObject.hxx | 48 |
1 files changed, 39 insertions, 9 deletions
diff --git a/include/impl/ELFObject.hxx b/include/impl/ELFObject.hxx index e491e15..ceac7be 100644 --- a/include/impl/ELFObject.hxx +++ b/include/impl/ELFObject.hxx @@ -438,12 +438,24 @@ relocateMIPS(void *(*find_sym)(void *context, char const *name), case R_MIPS_HI16: *inst &= 0xFFFF0000; - A = A & 0xFFFF; + A = (A & 0xFFFF) << 16; + // Find the nearest LO16 relocation type after this entry + for (size_t j = i + 1; j < reltab->size(); j++) { + ELFRelocTy *this_rel = (*reltab)[j]; + ELFSymbolTy *this_sym = (*symtab)[this_rel->getSymTabIndex()]; + if (this_rel->getType() == R_MIPS_LO16 && this_sym == sym) { + Inst_t *this_inst = (Inst_t *)&(*text)[this_rel->getOffset()]; + Inst_t this_A = (Inst_t)(uintptr_t)*this_inst; + this_A = this_A & 0xFFFF; + A += (short)this_A; + break; + } + } if (strcmp (sym->getName(), "_gp_disp") == 0) { S = (int)got_address() + GP_OFFSET - (int)P; sym->setAddress((void *)S); } - *inst |= (((S + (A << 16) + (int)0x8000) >> 16) & 0xFFFF); + *inst |= (((S + A + (int)0x8000) >> 16) & 0xFFFF); break; case R_MIPS_LO16: @@ -453,10 +465,6 @@ relocateMIPS(void *(*find_sym)(void *context, char const *name), S = (Inst_t)sym->getAddress(); } *inst |= ((S + A) & 0xFFFF); - // We assume the addend of R_MIPS_LO16 won't affect R_MIPS_HI16. - // If not, we have troubles. - if (((S + (short)A + (int)0x8000) >> 16) != ((S + (int)0x8000) >> 16)) - rsl_assert("AHL cannot be calculated correctly."); break; case R_MIPS_GOT16: @@ -464,9 +472,31 @@ relocateMIPS(void *(*find_sym)(void *context, char const *name), { *inst &= 0xFFFF0000; A = A & 0xFFFF; - // FIXME: We just support addend = 0. - rsl_assert(A == 0 && "R_MIPS_GOT16/R_MIPS_CALL16 addend is not 0."); - int got_index = search_got((int)rel->getSymTabIndex(), (void *)S, + if (rel->getType() == R_MIPS_GOT16) { + if (sym->getBindingAttribute() == STB_LOCAL) { + A <<= 16; + + // Find the nearest LO16 relocation type after this entry + for (size_t j = i + 1; j < reltab->size(); j++) { + ELFRelocTy *this_rel = (*reltab)[j]; + ELFSymbolTy *this_sym = (*symtab)[this_rel->getSymTabIndex()]; + if (this_rel->getType() == R_MIPS_LO16 && this_sym == sym) { + Inst_t *this_inst = (Inst_t *)&(*text)[this_rel->getOffset()]; + Inst_t this_A = (Inst_t)(uintptr_t)*this_inst; + this_A = this_A & 0xFFFF; + A += (short)this_A; + break; + } + } + } + else { + rsl_assert(A == 0 && "R_MIPS_GOT16 addend is not 0."); + } + } + else { // R_MIPS_CALL16 + rsl_assert(A == 0 && "R_MIPS_CALL16 addend is not 0."); + } + int got_index = search_got((int)rel->getSymTabIndex(), (void *)(S + A), sym->getBindingAttribute()); int got_offset = (got_index << 2) - GP_OFFSET; *inst |= (got_offset & 0xFFFF); |