[PowerPC] ELFv2 dynamic loader support

This patch enables the new ELFv2 ABI in the runtime dynamic loader.
The loader has to implement the following features:
- In the ELFv2 ABI, do not look up a function descriptor in .opd, but
  instead use the local entry point when resolving a direct call.
- Update the TOC restore code to use the new TOC slot linkage area
  offset.
- Create PLT stubs appropriate for the ELFv2 ABI.

Note that this patch also adds common-code changes. These are necessary
because the loader must check the newly added ELF flags: the e_flags
header bits encoding the ABI version, and the st_other symbol table
entry bits encoding the local entry point offset.  There is currently
no way to access these, so I've added ObjectFile::getPlatformFlags and
SymbolRef::getOther accessors.

Reviewed by Hal Finkel.

llvm-svn: 213491
This commit is contained in:
Ulrich Weigand 2014-07-20 23:53:14 +00:00
parent 8658f17eef
commit 752b5c9e6e
5 changed files with 75 additions and 20 deletions

View File

@ -65,6 +65,7 @@ protected:
uint32_t &Res) const override; uint32_t &Res) const override;
std::error_code getSymbolSize(DataRefImpl Symb, uint64_t &Res) const override; std::error_code getSymbolSize(DataRefImpl Symb, uint64_t &Res) const override;
uint32_t getSymbolFlags(DataRefImpl Symb) const override; uint32_t getSymbolFlags(DataRefImpl Symb) const override;
std::error_code getSymbolOther(DataRefImpl Symb, uint8_t &Res) const override;
std::error_code getSymbolType(DataRefImpl Symb, std::error_code getSymbolType(DataRefImpl Symb,
SymbolRef::Type &Res) const override; SymbolRef::Type &Res) const override;
std::error_code getSymbolSection(DataRefImpl Symb, std::error_code getSymbolSection(DataRefImpl Symb,
@ -202,6 +203,11 @@ public:
unsigned getArch() const override; unsigned getArch() const override;
StringRef getLoadName() const override; StringRef getLoadName() const override;
std::error_code getPlatformFlags(unsigned &Result) const override {
Result = EF.getHeader()->e_flags;
return object_error::success;
}
const ELFFile<ELFT> *getELFFile() const { return &EF; } const ELFFile<ELFT> *getELFFile() const { return &EF; }
bool isDyldType() const { return isDyldELFObject; } bool isDyldType() const { return isDyldELFObject; }
@ -294,6 +300,13 @@ std::error_code ELFObjectFile<ELFT>::getSymbolSize(DataRefImpl Symb,
return object_error::success; return object_error::success;
} }
template <class ELFT>
std::error_code ELFObjectFile<ELFT>::getSymbolOther(DataRefImpl Symb,
uint8_t &Result) const {
Result = toELFSymIter(Symb)->st_other;
return object_error::success;
}
template <class ELFT> template <class ELFT>
std::error_code std::error_code
ELFObjectFile<ELFT>::getSymbolType(DataRefImpl Symb, ELFObjectFile<ELFT>::getSymbolType(DataRefImpl Symb,

View File

@ -149,6 +149,7 @@ public:
std::error_code getAlignment(uint32_t &Result) const; std::error_code getAlignment(uint32_t &Result) const;
std::error_code getSize(uint64_t &Result) const; std::error_code getSize(uint64_t &Result) const;
std::error_code getType(SymbolRef::Type &Result) const; std::error_code getType(SymbolRef::Type &Result) const;
std::error_code getOther(uint8_t &Result) const;
/// @brief Get section this symbol is defined in reference to. Result is /// @brief Get section this symbol is defined in reference to. Result is
/// end_sections() if it is undefined or is an absolute symbol. /// end_sections() if it is undefined or is an absolute symbol.
@ -237,6 +238,10 @@ protected:
SymbolRef::Type &Res) const = 0; SymbolRef::Type &Res) const = 0;
virtual std::error_code getSymbolSection(DataRefImpl Symb, virtual std::error_code getSymbolSection(DataRefImpl Symb,
section_iterator &Res) const = 0; section_iterator &Res) const = 0;
virtual std::error_code getSymbolOther(DataRefImpl Symb,
uint8_t &Res) const {
return object_error::invalid_file_type;
}
// Same as above for SectionRef. // Same as above for SectionRef.
friend class SectionRef; friend class SectionRef;
@ -328,6 +333,12 @@ public:
/// LC_ID_DYLIB (install name) on MachO. /// LC_ID_DYLIB (install name) on MachO.
virtual StringRef getLoadName() const = 0; virtual StringRef getLoadName() const = 0;
/// Returns platform-specific object flags, if any.
virtual std::error_code getPlatformFlags(unsigned &Result) const {
Result = 0;
return object_error::invalid_file_type;
}
/// @returns Pointer to ObjectFile subclass to handle this type of object. /// @returns Pointer to ObjectFile subclass to handle this type of object.
/// @param ObjectPath The path to the object file. ObjectPath.isObject must /// @param ObjectPath The path to the object file. ObjectPath.isObject must
/// return true. /// return true.
@ -383,6 +394,10 @@ inline std::error_code SymbolRef::getType(SymbolRef::Type &Result) const {
return getObject()->getSymbolType(getRawDataRefImpl(), Result); return getObject()->getSymbolType(getRawDataRefImpl(), Result);
} }
inline std::error_code SymbolRef::getOther(uint8_t &Result) const {
return getObject()->getSymbolOther(getRawDataRefImpl(), Result);
}
inline const ObjectFile *SymbolRef::getObject() const { inline const ObjectFile *SymbolRef::getObject() const {
const SymbolicFile *O = BasicSymbolRef::getObject(); const SymbolicFile *O = BasicSymbolRef::getObject();
return cast<ObjectFile>(O); return cast<ObjectFile>(O);

View File

@ -517,7 +517,8 @@ void RuntimeDyldImpl::addRelocationForSymbol(const RelocationEntry &RE,
} }
} }
uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr) { uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr,
unsigned AbiVariant) {
if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be || if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be ||
Arch == Triple::arm64 || Arch == Triple::arm64_be) { Arch == Triple::arm64 || Arch == Triple::arm64_be) {
// This stub has to be able to access the full address space, // This stub has to be able to access the full address space,
@ -561,22 +562,31 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr) {
*StubAddr = NopInstr; *StubAddr = NopInstr;
return Addr; return Addr;
} else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) { } else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) {
// PowerPC64 stub: the address points to a function descriptor // Depending on which version of the ELF ABI is in use, we need to
// instead of the function itself. Load the function address // generate one of two variants of the stub. They both start with
// on r11 and sets it to control register. Also loads the function // the same sequence to load the target address into r12.
// TOC in r2 and environment pointer to r11.
writeInt32BE(Addr, 0x3D800000); // lis r12, highest(addr) writeInt32BE(Addr, 0x3D800000); // lis r12, highest(addr)
writeInt32BE(Addr+4, 0x618C0000); // ori r12, higher(addr) writeInt32BE(Addr+4, 0x618C0000); // ori r12, higher(addr)
writeInt32BE(Addr+8, 0x798C07C6); // sldi r12, r12, 32 writeInt32BE(Addr+8, 0x798C07C6); // sldi r12, r12, 32
writeInt32BE(Addr+12, 0x658C0000); // oris r12, r12, h(addr) writeInt32BE(Addr+12, 0x658C0000); // oris r12, r12, h(addr)
writeInt32BE(Addr+16, 0x618C0000); // ori r12, r12, l(addr) writeInt32BE(Addr+16, 0x618C0000); // ori r12, r12, l(addr)
writeInt32BE(Addr+20, 0xF8410028); // std r2, 40(r1) if (AbiVariant == 2) {
writeInt32BE(Addr+24, 0xE96C0000); // ld r11, 0(r12) // PowerPC64 stub ELFv2 ABI: The address points to the function itself.
writeInt32BE(Addr+28, 0xE84C0008); // ld r2, 0(r12) // The address is already in r12 as required by the ABI. Branch to it.
writeInt32BE(Addr+32, 0x7D6903A6); // mtctr r11 writeInt32BE(Addr+20, 0xF8410018); // std r2, 24(r1)
writeInt32BE(Addr+36, 0xE96C0010); // ld r11, 16(r2) writeInt32BE(Addr+24, 0x7D8903A6); // mtctr r12
writeInt32BE(Addr+40, 0x4E800420); // bctr writeInt32BE(Addr+28, 0x4E800420); // bctr
} else {
// PowerPC64 stub ELFv1 ABI: The address points to a function descriptor.
// Load the function address on r11 and sets it to control register. Also
// loads the function TOC in r2 and environment pointer to r11.
writeInt32BE(Addr+20, 0xF8410028); // std r2, 40(r1)
writeInt32BE(Addr+24, 0xE96C0000); // ld r11, 0(r12)
writeInt32BE(Addr+28, 0xE84C0008); // ld r2, 0(r12)
writeInt32BE(Addr+32, 0x7D6903A6); // mtctr r11
writeInt32BE(Addr+36, 0xE96C0010); // ld r11, 16(r2)
writeInt32BE(Addr+40, 0x4E800420); // bctr
}
return Addr; return Addr;
} else if (Arch == Triple::systemz) { } else if (Arch == Triple::systemz) {
writeInt16BE(Addr, 0xC418); // lgrl %r1,.+8 writeInt16BE(Addr, 0xC418); // lgrl %r1,.+8

View File

@ -1141,6 +1141,10 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
} }
} else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) { } else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) {
if (RelType == ELF::R_PPC64_REL24) { if (RelType == ELF::R_PPC64_REL24) {
// Determine ABI variant in use for this object.
unsigned AbiVariant;
Obj.getObjectFile()->getPlatformFlags(AbiVariant);
AbiVariant &= ELF::EF_PPC64_ABI;
// A PPC branch relocation will need a stub function if the target is // A PPC branch relocation will need a stub function if the target is
// an external symbol (Symbol::ST_Unknown) or if the target address // an external symbol (Symbol::ST_Unknown) or if the target address
// is not within the signed 24-bits branch address. // is not within the signed 24-bits branch address.
@ -1148,10 +1152,18 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
uint8_t *Target = Section.Address + Offset; uint8_t *Target = Section.Address + Offset;
bool RangeOverflow = false; bool RangeOverflow = false;
if (SymType != SymbolRef::ST_Unknown) { if (SymType != SymbolRef::ST_Unknown) {
// A function call may points to the .opd entry, so the final symbol if (AbiVariant != 2) {
// value // In the ELFv1 ABI, a function call may point to the .opd entry,
// in calculated based in the relocation values in .opd section. // so the final symbol value is calculated based on the relocation
findOPDEntrySection(Obj, ObjSectionToID, Value); // values in the .opd section.
findOPDEntrySection(Obj, ObjSectionToID, Value);
} else {
// In the ELFv2 ABI, a function symbol may provide a local entry
// point, which must be used for direct calls.
uint8_t SymOther;
Symbol->getOther(SymOther);
Value.Addend += ELF::decodePPC64LocalEntryOffset(SymOther);
}
uint8_t *RelocTarget = Sections[Value.SectionID].Address + Value.Addend; uint8_t *RelocTarget = Sections[Value.SectionID].Address + Value.Addend;
int32_t delta = static_cast<int32_t>(Target - RelocTarget); int32_t delta = static_cast<int32_t>(Target - RelocTarget);
// If it is within 24-bits branch range, just set the branch target // If it is within 24-bits branch range, just set the branch target
@ -1179,7 +1191,8 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
DEBUG(dbgs() << " Create a new stub function\n"); DEBUG(dbgs() << " Create a new stub function\n");
Stubs[Value] = Section.StubOffset; Stubs[Value] = Section.StubOffset;
uint8_t *StubTargetAddr = uint8_t *StubTargetAddr =
createStubFunction(Section.Address + Section.StubOffset); createStubFunction(Section.Address + Section.StubOffset,
AbiVariant);
RelocationEntry RE(SectionID, StubTargetAddr - Section.Address, RelocationEntry RE(SectionID, StubTargetAddr - Section.Address,
ELF::R_PPC64_ADDR64, Value.Addend); ELF::R_PPC64_ADDR64, Value.Addend);
@ -1217,9 +1230,13 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
RelType, 0); RelType, 0);
Section.StubOffset += getMaxStubSize(); Section.StubOffset += getMaxStubSize();
} }
if (SymType == SymbolRef::ST_Unknown) if (SymType == SymbolRef::ST_Unknown) {
// Restore the TOC for external calls // Restore the TOC for external calls
writeInt32BE(Target + 4, 0xE8410028); // ld r2,40(r1) if (AbiVariant == 2)
writeInt32BE(Target + 4, 0xE8410018); // ld r2,28(r1)
else
writeInt32BE(Target + 4, 0xE8410028); // ld r2,40(r1)
}
} }
} else if (RelType == ELF::R_PPC64_TOC16 || } else if (RelType == ELF::R_PPC64_TOC16 ||
RelType == ELF::R_PPC64_TOC16_DS || RelType == ELF::R_PPC64_TOC16_DS ||

View File

@ -312,7 +312,7 @@ protected:
/// \brief Emits long jump instruction to Addr. /// \brief Emits long jump instruction to Addr.
/// \return Pointer to the memory area for emitting target address. /// \return Pointer to the memory area for emitting target address.
uint8_t *createStubFunction(uint8_t *Addr); uint8_t *createStubFunction(uint8_t *Addr, unsigned AbiVariant = 0);
/// \brief Resolves relocations from Relocs list with address from Value. /// \brief Resolves relocations from Relocs list with address from Value.
void resolveRelocationList(const RelocationList &Relocs, uint64_t Value); void resolveRelocationList(const RelocationList &Relocs, uint64_t Value);