Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -13,6 +13,7 @@ #include "Symbols.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ELF.h" @@ -319,6 +320,8 @@ const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA, uint64_t GotVA, bool ForPltEntry) const { + static DenseMap>> DelayedRelocs; + typedef ELFFile::Elf_Rela Elf_Rela; auto &Rel = *reinterpret_cast(RelP); int64_t Addend = Rel.r_addend; @@ -428,16 +431,29 @@ write32be(Loc, Result); } break; case R_PPC64_REL24: { - uint64_t FinalAddress = (BaseAddr + Offset); - int32_t delta = static_cast(SymVA - FinalAddress + Addend); - uint32_t Mask = 0x03FFFFFC; - if (SignExtend32<24>(delta) != delta) - error("Relocation R_PPC64_REL24 overflow"); - write32be(Loc, (read32be(Loc) & ~Mask) | (delta & Mask)); - - if (ForPltEntry && Loc + 8 < BufEnd && - read32be(Loc + 4) == 0x60000000 /* nop */) - write32be(Loc + 4, 0xe8410028); // ld r2,40(r1) + auto ApplyReloc = [=](uint64_t SymVAPlusAddend) { + uint64_t FinalAddress = (BaseAddr + Offset); + int32_t delta = static_cast(SymVAPlusAddend - FinalAddress); + uint32_t Mask = 0x03FFFFFC; + if (SignExtend32<24>(delta) != delta) + error("Relocation R_PPC64_REL24 overflow"); + write32be(Loc, (read32be(Loc) & ~Mask) | (delta & Mask)); + + if (ForPltEntry && Loc + 8 < BufEnd && + read32be(Loc + 4) == 0x60000000 /* nop */) + write32be(Loc + 4, 0xe8410028); // ld r2,40(r1) + }; + + if (ForPltEntry) { + ApplyReloc(SymVA + Addend); + } else { + // This is a branch to a local symbol, but we cannot yet resolve it. The + // address provided here is that of the function's descriptor in the .opd + // section. We need to branch, however, to the address that will appear in + // the .opd section, but we won't know what that is until we process + // relocations in that section. + DelayedRelocs[SymVA + Addend].push_back(ApplyReloc); + } } break; case R_PPC64_REL32: { uint64_t FinalAddress = (BaseAddr + Offset); @@ -451,9 +467,17 @@ uint64_t Delta = SymVA - FinalAddress + Addend; write64be(Loc, Delta); } break; - case R_PPC64_ADDR64: + case R_PPC64_ADDR64: { write64be(Loc, SymVA + Addend); - break; + + // We might be processing the relocation for a function descriptor, and if + // so, we can now go back and fill in this value for local call sites as + // well. + auto DRI = DelayedRelocs.find(BaseAddr + Offset); + if (DRI != DelayedRelocs.end()) + for (auto &DR : DRI->second) + DR(SymVA + Addend); + } break; default: error(Twine("unrecognized reloc ") + Twine(Type)); break;