forked from OSchip/llvm-project
[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:
parent
8658f17eef
commit
752b5c9e6e
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue