diff options
author | Jacek Caban <jacek@codeweavers.com> | 2024-08-06 22:06:08 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-06 22:06:08 +0200 |
commit | 94d53984008bc83083fdc5b2edcd1ea4b8a9b8be (patch) | |
tree | 578825bce50b31e3a960c26fecb8f0f968ce037a /llvm/lib/Object/COFFObjectFile.cpp | |
parent | 0371dff99529e93b8650281801e89bc015dc2703 (diff) | |
download | llvm-94d53984008bc83083fdc5b2edcd1ea4b8a9b8be.zip llvm-94d53984008bc83083fdc5b2edcd1ea4b8a9b8be.tar.gz llvm-94d53984008bc83083fdc5b2edcd1ea4b8a9b8be.tar.bz2 |
[Object][COFF][llvm-readobj] Add support for ARM64X dynamic relocations. (#97229)
Diffstat (limited to 'llvm/lib/Object/COFFObjectFile.cpp')
-rw-r--r-- | llvm/lib/Object/COFFObjectFile.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/llvm/lib/Object/COFFObjectFile.cpp b/llvm/lib/Object/COFFObjectFile.cpp index 5a85b8e0..4b6dd1a 100644 --- a/llvm/lib/Object/COFFObjectFile.cpp +++ b/llvm/lib/Object/COFFObjectFile.cpp @@ -798,6 +798,60 @@ Error COFFObjectFile::initLoadConfigPtr() { return E; } } + + if (Config->Size >= + offsetof(coff_load_configuration64, DynamicValueRelocTableSection) + + sizeof(Config->DynamicValueRelocTableSection)) + if (Error E = initDynamicRelocPtr(Config->DynamicValueRelocTableSection, + Config->DynamicValueRelocTableOffset)) + return E; + } else { + auto Config = getLoadConfig32(); + if (Config->Size >= + offsetof(coff_load_configuration32, DynamicValueRelocTableSection) + + sizeof(Config->DynamicValueRelocTableSection)) { + if (Error E = initDynamicRelocPtr(Config->DynamicValueRelocTableSection, + Config->DynamicValueRelocTableOffset)) + return E; + } + } + return Error::success(); +} + +Error COFFObjectFile::initDynamicRelocPtr(uint32_t SectionIndex, + uint32_t SectionOffset) { + Expected<const coff_section *> Section = getSection(SectionIndex); + if (!Section) + return Section.takeError(); + if (!*Section) + return Error::success(); + + // Interpret and validate dynamic relocations. + ArrayRef<uint8_t> Contents; + if (Error E = getSectionContents(*Section, Contents)) + return E; + + Contents = Contents.drop_front(SectionOffset); + if (Contents.size() < sizeof(coff_dynamic_reloc_table)) + return createStringError(object_error::parse_failed, + "Too large DynamicValueRelocTableOffset (" + + Twine(SectionOffset) + ")"); + + DynamicRelocTable = + reinterpret_cast<const coff_dynamic_reloc_table *>(Contents.data()); + + if (DynamicRelocTable->Version != 1 && DynamicRelocTable->Version != 2) + return createStringError(object_error::parse_failed, + "Unsupported dynamic relocations table version (" + + Twine(DynamicRelocTable->Version) + ")"); + if (DynamicRelocTable->Size > Contents.size() - sizeof(*DynamicRelocTable)) + return createStringError(object_error::parse_failed, + "Indvalid dynamic relocations directory size (" + + Twine(DynamicRelocTable->Size) + ")"); + + for (auto DynReloc : dynamic_relocs()) { + if (Error e = DynReloc.validate()) + return e; } return Error::success(); @@ -1047,6 +1101,19 @@ base_reloc_iterator COFFObjectFile::base_reloc_end() const { return base_reloc_iterator(BaseRelocRef(BaseRelocEnd, this)); } +dynamic_reloc_iterator COFFObjectFile::dynamic_reloc_begin() const { + const void *Header = DynamicRelocTable ? DynamicRelocTable + 1 : nullptr; + return dynamic_reloc_iterator(DynamicRelocRef(Header, this)); +} + +dynamic_reloc_iterator COFFObjectFile::dynamic_reloc_end() const { + const void *Header = nullptr; + if (DynamicRelocTable) + Header = reinterpret_cast<const uint8_t *>(DynamicRelocTable + 1) + + DynamicRelocTable->Size; + return dynamic_reloc_iterator(DynamicRelocRef(Header, this)); +} + uint8_t COFFObjectFile::getBytesInAddress() const { return getArch() == Triple::x86_64 || getArch() == Triple::aarch64 ? 8 : 4; } @@ -1100,6 +1167,10 @@ iterator_range<base_reloc_iterator> COFFObjectFile::base_relocs() const { return make_range(base_reloc_begin(), base_reloc_end()); } +iterator_range<dynamic_reloc_iterator> COFFObjectFile::dynamic_relocs() const { + return make_range(dynamic_reloc_begin(), dynamic_reloc_end()); +} + const data_directory *COFFObjectFile::getDataDirectory(uint32_t Index) const { if (!DataDirectory) return nullptr; @@ -1789,6 +1860,275 @@ Error BaseRelocRef::getRVA(uint32_t &Result) const { return Error::success(); } +bool DynamicRelocRef::operator==(const DynamicRelocRef &Other) const { + return Header == Other.Header; +} + +void DynamicRelocRef::moveNext() { + switch (Obj->getDynamicRelocTable()->Version) { + case 1: + if (Obj->is64()) { + auto H = reinterpret_cast<const coff_dynamic_relocation64 *>(Header); + Header += sizeof(*H) + H->BaseRelocSize; + } else { + auto H = reinterpret_cast<const coff_dynamic_relocation32 *>(Header); + Header += sizeof(*H) + H->BaseRelocSize; + } + break; + case 2: + if (Obj->is64()) { + auto H = reinterpret_cast<const coff_dynamic_relocation64_v2 *>(Header); + Header += H->HeaderSize + H->FixupInfoSize; + } else { + auto H = reinterpret_cast<const coff_dynamic_relocation32_v2 *>(Header); + Header += H->HeaderSize + H->FixupInfoSize; + } + break; + } +} + +uint32_t DynamicRelocRef::getType() const { + switch (Obj->getDynamicRelocTable()->Version) { + case 1: + if (Obj->is64()) { + auto H = reinterpret_cast<const coff_dynamic_relocation64 *>(Header); + return H->Symbol; + } else { + auto H = reinterpret_cast<const coff_dynamic_relocation32 *>(Header); + return H->Symbol; + } + break; + case 2: + if (Obj->is64()) { + auto H = reinterpret_cast<const coff_dynamic_relocation64_v2 *>(Header); + return H->Symbol; + } else { + auto H = reinterpret_cast<const coff_dynamic_relocation32_v2 *>(Header); + return H->Symbol; + } + break; + default: + llvm_unreachable("invalid version"); + } +} + +void DynamicRelocRef::getContents(ArrayRef<uint8_t> &Ref) const { + switch (Obj->getDynamicRelocTable()->Version) { + case 1: + if (Obj->is64()) { + auto H = reinterpret_cast<const coff_dynamic_relocation64 *>(Header); + Ref = ArrayRef(Header + sizeof(*H), H->BaseRelocSize); + } else { + auto H = reinterpret_cast<const coff_dynamic_relocation32 *>(Header); + Ref = ArrayRef(Header + sizeof(*H), H->BaseRelocSize); + } + break; + case 2: + if (Obj->is64()) { + auto H = reinterpret_cast<const coff_dynamic_relocation64_v2 *>(Header); + Ref = ArrayRef(Header + H->HeaderSize, H->FixupInfoSize); + } else { + auto H = reinterpret_cast<const coff_dynamic_relocation32_v2 *>(Header); + Ref = ArrayRef(Header + H->HeaderSize, H->FixupInfoSize); + } + break; + } +} + +Error DynamicRelocRef::validate() const { + const coff_dynamic_reloc_table *Table = Obj->getDynamicRelocTable(); + size_t ContentsSize = + reinterpret_cast<const uint8_t *>(Table + 1) + Table->Size - Header; + size_t HeaderSize; + if (Table->Version == 1) + HeaderSize = Obj->is64() ? sizeof(coff_dynamic_relocation64) + : sizeof(coff_dynamic_relocation32); + else + HeaderSize = Obj->is64() ? sizeof(coff_dynamic_relocation64_v2) + : sizeof(coff_dynamic_relocation32_v2); + if (HeaderSize > ContentsSize) + return createStringError(object_error::parse_failed, + "Unexpected end of dynamic relocations data"); + + if (Table->Version == 2) { + size_t Size = + Obj->is64() + ? reinterpret_cast<const coff_dynamic_relocation64_v2 *>(Header) + ->HeaderSize + : reinterpret_cast<const coff_dynamic_relocation32_v2 *>(Header) + ->HeaderSize; + if (Size < HeaderSize || Size > ContentsSize) + return createStringError(object_error::parse_failed, + "Invalid dynamic relocation header size (" + + Twine(Size) + ")"); + HeaderSize = Size; + } + + ArrayRef<uint8_t> Contents; + getContents(Contents); + if (Contents.size() > ContentsSize - HeaderSize) + return createStringError(object_error::parse_failed, + "Too large dynamic relocation size (" + + Twine(Contents.size()) + ")"); + + switch (getType()) { + case COFF::IMAGE_DYNAMIC_RELOCATION_ARM64X: + for (auto Reloc : arm64x_relocs()) { + if (Error E = Reloc.validate(Obj)) + return E; + } + break; + } + + return Error::success(); +} + +arm64x_reloc_iterator DynamicRelocRef::arm64x_reloc_begin() const { + assert(getType() == COFF::IMAGE_DYNAMIC_RELOCATION_ARM64X); + ArrayRef<uint8_t> Content; + getContents(Content); + auto Header = + reinterpret_cast<const coff_base_reloc_block_header *>(Content.begin()); + return arm64x_reloc_iterator(Arm64XRelocRef(Header)); +} + +arm64x_reloc_iterator DynamicRelocRef::arm64x_reloc_end() const { + assert(getType() == COFF::IMAGE_DYNAMIC_RELOCATION_ARM64X); + ArrayRef<uint8_t> Content; + getContents(Content); + auto Header = + reinterpret_cast<const coff_base_reloc_block_header *>(Content.end()); + return arm64x_reloc_iterator(Arm64XRelocRef(Header, 0)); +} + +iterator_range<arm64x_reloc_iterator> DynamicRelocRef::arm64x_relocs() const { + return make_range(arm64x_reloc_begin(), arm64x_reloc_end()); +} + +bool Arm64XRelocRef::operator==(const Arm64XRelocRef &Other) const { + return Header == Other.Header && Index == Other.Index; +} + +uint8_t Arm64XRelocRef::getEntrySize() const { + switch (getType()) { + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE: + return (1u << getArg()) / sizeof(uint16_t) + 1; + break; + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA: + return 2; + default: + return 1; + } +} + +void Arm64XRelocRef::moveNext() { + Index += getEntrySize(); + if (sizeof(*Header) + Index * sizeof(uint16_t) < Header->BlockSize && + !getReloc()) + ++Index; // Skip padding + if (sizeof(*Header) + Index * sizeof(uint16_t) == Header->BlockSize) { + // The end of the block, move to the next one. + Header = + reinterpret_cast<const coff_base_reloc_block_header *>(&getReloc()); + Index = 0; + } +} + +uint8_t Arm64XRelocRef::getSize() const { + switch (getType()) { + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL: + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE: + return 1 << getArg(); + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA: + return sizeof(uint32_t); + } +} + +uint64_t Arm64XRelocRef::getValue() const { + auto Ptr = reinterpret_cast<const ulittle16_t *>(Header + 1) + Index + 1; + + switch (getType()) { + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE: { + ulittle64_t Value(0); + memcpy(&Value, Ptr, getSize()); + return Value; + } + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA: { + uint16_t arg = getArg(); + int delta = *Ptr; + + if (arg & 1) + delta = -delta; + delta *= (arg & 2) ? 8 : 4; + return delta; + } + default: + return 0; + } +} + +Error Arm64XRelocRef::validate(const COFFObjectFile *Obj) const { + if (!Index) { + const coff_dynamic_reloc_table *Table = Obj->getDynamicRelocTable(); + size_t ContentsSize = reinterpret_cast<const uint8_t *>(Table + 1) + + Table->Size - + reinterpret_cast<const uint8_t *>(Header); + if (ContentsSize < sizeof(coff_base_reloc_block_header)) + return createStringError(object_error::parse_failed, + "Unexpected end of ARM64X relocations data"); + if (Header->BlockSize <= sizeof(*Header)) + return createStringError(object_error::parse_failed, + "ARM64X relocations block size (" + + Twine(Header->BlockSize) + ") is too small"); + if (Header->BlockSize % sizeof(uint32_t)) + return createStringError(object_error::parse_failed, + "Unaligned ARM64X relocations block size (" + + Twine(Header->BlockSize) + ")"); + if (Header->BlockSize > ContentsSize) + return createStringError(object_error::parse_failed, + "ARM64X relocations block size (" + + Twine(Header->BlockSize) + ") is too large"); + if (Header->PageRVA & 0xfff) + return createStringError(object_error::parse_failed, + "Unaligned ARM64X relocations page RVA (" + + Twine(Header->PageRVA) + ")"); + } + + switch ((getReloc() >> 12) & 3) { + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL: + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA: + break; + case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE: + if (!getArg()) + return createStringError(object_error::parse_failed, + "Invalid ARM64X relocation value size (0)"); + break; + default: + return createStringError(object_error::parse_failed, + "Invalid relocation type"); + } + + uint32_t RelocsSize = + (Header->BlockSize - sizeof(*Header)) / sizeof(uint16_t); + uint16_t EntrySize = getEntrySize(); + if (!getReloc() || + (Index + EntrySize + 1 < RelocsSize && !getReloc(EntrySize))) + return createStringError(object_error::parse_failed, + "Unexpected ARM64X relocations terminator"); + if (Index + EntrySize > RelocsSize) + return createStringError(object_error::parse_failed, + "Unexpected end of ARM64X relocations"); + if (getRVA() % getSize()) + return createStringError(object_error::parse_failed, + "Unaligned ARM64X relocation RVA (" + + Twine(getRVA()) + ")"); + if (Header->PageRVA) { + uint64_t IntPtr; + return Obj->getRvaPtr(getRVA() + getSize(), IntPtr, "ARM64X reloc"); + } + return Error::success(); +} + #define RETURN_IF_ERROR(Expr) \ do { \ Error E = (Expr); \ |