diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h --- a/llvm/include/llvm/BinaryFormat/XCOFF.h +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -148,6 +148,68 @@ XTY_CM = 3 ///< Common csect definition. For uninitialized storage. }; +// Relocation types, defined in `/usr/include/reloc.h`. +enum RelocationType : uint8_t { + R_POS = 0x00, ///< Positive relocation. Provides the address of the referenced + ///< symbol. + R_RL = 0x0c, ///< Positive indirect load relocation. Modifiable instruction. + R_RLA = 0x0d, ///< Positive load address relocation. Modifiable instruction. + + R_NEG = 0x01, ///< Negative relocation. Provides the negative of the address + ///< of the referenced symbol. + R_REL = 0x02, ///< Relative to self relocation. Provides a displacement value + ///< between the address of the referenced symbol and the + ///< address being relocated. + + R_TOC = 0x03, ///< Relative to the TOC relocation. Provides a displacement + ///< that is the difference between the address of the + ///< referenced symbol and the TOC anchor csect. + R_TRL = 0x12, ///< TOC relative indirect load relocation. Similar to R_TOC, + ///< but not modifiable instruction. + + R_TRLA = + 0x13, ///< Relative to the TOC or to the thread-local storage base + ///< relocation. Compilers are not permitted to generate this + ///< relocation type. It is the result of a reversible + ///< transformation by the linker of an R_TOC relation that turned a + ///< load instruction into an add-immediate instruction. + + R_GL = 0x05, ///< Global linkage-external TOC address relocation. Provides the + ///< address of the external TOC associated with a defined + ///< external symbol. + R_TCL = 0x06, ///< Local object TOC address relocation. Provides the address + ///< of the local TOC entry of a defined external symbol. + + R_REF = 0x0f, ///< A non-relocating relocation. Used to prevent the binder + ///< from garbage collecting a csect (such as code used for + ///< dynamic initialization of non-local statics) for which + ///< another csect has an implicit dependency. + + R_BA = 0x08, ///< Branch absolute relocation. Provides the address of the + ///< referenced symbol. References a non-modifiable instruction. + R_BR = 0x0a, ///< Branch relative to self relocation. Provides the + ///< displacement that is the difference between the address of + ///< the referenced symbol and the address of the referenced + ///< branch instruction. References a non-modifiable instruction. + R_RBA = 0x18, ///< Branch absolute relocation. Similar to R_BA but + ///< references a modifiable instruction. + R_RBR = 0x1a, ///< Branch relative to self relocation. Similar to the R_BR + ///< relocation type, but references a modifiable instruction. + + R_TLS = 0x20, ///< General-dynamic reference to TLS symbol. + R_TLS_IE = 0x21, ///< Initial-exec reference to TLS symbol. + R_TLS_LD = 0x22, ///< Local-dynamic reference to TLS symbol. + R_TLS_LE = 0x23, ///< Local-exec reference to TLS symbol. + R_TLSM = 0x24, ///< Module reference to TLS. Provides a handle for the module + ///< containing the referenced symbol. + R_TLSML = 0x25, ///< Module reference to the local TLS storage. + + R_TOCU = 0x30, ///< Relative to TOC upper. Specifies the high-order 16 bits of + ///< a large code model TOC-relative relocation. + R_TOCL = 0x31 ///< Relative to TOC lower. Specifies the low-order 16 bits of a + ///< large code model TOC-relative relocation. +}; + struct FileHeader32 { uint16_t Magic; uint16_t NumberOfSections; diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -149,6 +149,38 @@ uint8_t Pad[10]; }; +struct XCOFFRelocation32 { + // Masks for packing/unpacking the r_rsize field of relocations. + + // The msb is used to indicate if the bits being relocated are signed or + // unsigned. + static constexpr uint8_t XR_SIGN_INDICATOR_MASK = 0x80; + + // The 2nd msb is used to indicate that the binder has replaced/modified the + // original instruction. + static constexpr uint8_t XR_FIXUP_INDICATOR_MASK = 0x40; + + // The remaining bits specify the bit length of the relocatable reference + // minus one. + static constexpr uint8_t XR_BIASED_LENGTH_MASK = 0x3f; + +public: + support::ubig32_t VirtualAddress; + support::ubig32_t SymbolIndex; + + // Packed field, see XR_* masks for details of packing. + uint8_t Info; + + XCOFF::RelocationType Type; + +public: + bool isRelocationSigned() const; + bool isFixupIndicated() const; + + // Returns the number of bits being relocated. + uint8_t getRelocatedLength() const; +}; + class XCOFFObjectFile : public ObjectFile { private: const void *FileHeader = nullptr; @@ -278,6 +310,7 @@ uint32_t getNumberOfSymbolTableEntries64() const; uint32_t getSymbolIndex(uintptr_t SymEntPtr) const; + Expected getSymbolNameByIndex(uint32_t SymbolTableIndex) const; Expected getCFileName(const XCOFFFileAuxEnt *CFileEntPtr) const; uint16_t getOptionalHeaderSize() const; @@ -291,6 +324,13 @@ Expected getSectionByNum(int16_t Num) const; void checkSymbolEntryPointer(uintptr_t SymbolEntPtr) const; + + // Relocation-related interfaces. + Expected + getLogicalNumberOfRelocationEntries(const XCOFFSectionHeader32 &Sec) const; + + Expected> + relocations(const XCOFFSectionHeader32 &) const; }; // XCOFFObjectFile class XCOFFSymbolRef { diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -17,10 +17,7 @@ namespace llvm { namespace object { -enum { - FUNCTION_SYM = 0x20, - SYM_TYPE_MASK = 0x07 -}; +enum { FUNCTION_SYM = 0x20, SYM_TYPE_MASK = 0x07, RELOC_OVERFLOW = 65535 }; // Checks that [Ptr, Ptr + Size) bytes fall inside the memory buffer // 'M'. Returns a pointer to the underlying object on success. @@ -49,6 +46,20 @@ : StringRef(Name, XCOFF::NameSize); } +bool XCOFFRelocation32::isRelocationSigned() const { + return Info & XR_SIGN_INDICATOR_MASK; +} + +bool XCOFFRelocation32::isFixupIndicated() const { + return Info & XR_FIXUP_INDICATOR_MASK; +} + +uint8_t XCOFFRelocation32::getRelocatedLength() const { + // The relocation encodes the bit length being relocated minus 1. Add back + // the 1 to get the actual length being relocated. + return (Info & XR_BIASED_LENGTH_MASK) + 1; +} + void XCOFFObjectFile::checkSectionAddress(uintptr_t Addr, uintptr_t TableAddress) const { if (Addr < TableAddress) @@ -494,6 +505,19 @@ XCOFF::SymbolTableEntrySize; } +Expected +XCOFFObjectFile::getSymbolNameByIndex(uint32_t Index) const { + if (is64Bit()) + report_fatal_error("64-bit symbol table support not implemented yet."); + + if (Index >= getLogicalNumberOfSymbolTableEntries32()) + return errorCodeToError(object_error::invalid_symbol_index); + + DataRefImpl SymDRI; + SymDRI.p = reinterpret_cast(getPointerToSymbolTable() + Index); + return getSymbolName(SymDRI); +} + uint16_t XCOFFObjectFile::getFlags() const { return is64Bit() ? fileHeader64()->Flags : fileHeader32()->Flags; } @@ -529,6 +553,46 @@ TablePtr + getNumberOfSections()); } +// In an XCOFF32 file, when the field value is 65535, then an STYP_OVRFLO +// section header contains the actual count of relocation entries in the s_paddr +// field. STYP_OVRFLO headers contain the section index of their corresponding +// sections as their raw "NumberOfRelocations" field value. +Expected XCOFFObjectFile::getLogicalNumberOfRelocationEntries( + const XCOFFSectionHeader32 &Sec) const { + + uint16_t SectionIndex = &Sec - sectionHeaderTable32() + 1; + + if (Sec.NumberOfRelocations < RELOC_OVERFLOW) + return Sec.NumberOfRelocations; + for (const auto &Sec : sections32()) { + if (Sec.Flags == XCOFF::STYP_OVRFLO && + Sec.NumberOfRelocations == SectionIndex) + return Sec.PhysicalAddress; + } + return errorCodeToError(object_error::parse_failed); +} + +Expected> +XCOFFObjectFile::relocations(const XCOFFSectionHeader32 &Sec) const { + uintptr_t RelocAddr = getWithOffset(reinterpret_cast(FileHeader), + Sec.FileOffsetToRelocationInfo); + auto NumRelocEntriesOrErr = getLogicalNumberOfRelocationEntries(Sec); + if (Error E = NumRelocEntriesOrErr.takeError()) + return std::move(E); + + uint32_t NumRelocEntries = NumRelocEntriesOrErr.get(); + + auto RelocationOrErr = + getObject(Data, reinterpret_cast(RelocAddr), + NumRelocEntries * sizeof(XCOFFRelocation32)); + if (Error E = RelocationOrErr.takeError()) + return std::move(E); + + const XCOFFRelocation32 *StartReloc = RelocationOrErr.get(); + + return ArrayRef(StartReloc, StartReloc + NumRelocEntries); +} + Expected XCOFFObjectFile::parseStringTable(const XCOFFObjectFile *Obj, uint64_t Offset) { // If there is a string table, then the buffer must contain at least 4 bytes diff --git a/llvm/test/tools/llvm-readobj/reloc_overflow.test b/llvm/test/tools/llvm-readobj/reloc_overflow.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/reloc_overflow.test @@ -0,0 +1,80 @@ +# RUN: llvm-readobj --sections %p/Inputs/xcoff-reloc-overflow.o | \ +# RUN: FileCheck --check-prefix=SECOVERFLOW %s + +# RUN: llvm-readobj --relocs --expand-relocs %p/Inputs/xcoff-reloc-overflow.o | \ +# RUN: FileCheck --check-prefix=RELOCOVERFLOW %s + +# SECOVERFLOW: File: {{.*}}xcoff-reloc-overflow.o +# SECOVERFLOW-NEXT: Format: aixcoff-rs6000 +# SECOVERFLOW-NEXT: Arch: powerpc +# SECOVERFLOW-NEXT: AddressSize: 32bit +# SECOVERFLOW-NEXT: Sections [ +# SECOVERFLOW-NEXT: Section { +# SECOVERFLOW-NEXT: Index: 1 +# SECOVERFLOW-NEXT: Name: .text +# SECOVERFLOW-NEXT: PhysicalAddress: 0x0 +# SECOVERFLOW-NEXT: VirtualAddress: 0x0 +# SECOVERFLOW-NEXT: Size: 0x38 +# SECOVERFLOW-NEXT: RawDataOffset: 0x8C +# SECOVERFLOW-NEXT: RelocationPointer: 0x0 +# SECOVERFLOW-NEXT: LineNumberPointer: 0x0 +# SECOVERFLOW-NEXT: NumberOfRelocations: 0 +# SECOVERFLOW-NEXT: NumberOfLineNumbers: 0 +# SECOVERFLOW-NEXT: Type: STYP_TEXT (0x20) +# SECOVERFLOW-NEXT: } +# SECOVERFLOW-NEXT: Section { +# SECOVERFLOW-NEXT: Index: 2 +# SECOVERFLOW-NEXT: Name: .data +# SECOVERFLOW-NEXT: PhysicalAddress: 0x38 +# SECOVERFLOW-NEXT: VirtualAddress: 0x38 +# SECOVERFLOW-NEXT: Size: 0x1C +# SECOVERFLOW-NEXT: RawDataOffset: 0xC4 +# SECOVERFLOW-NEXT: RelocationPointer: 0xE0 +# SECOVERFLOW-NEXT: LineNumberPointer: 0x0 +# SECOVERFLOW-NEXT: NumberOfRelocations: 65535 +# SECOVERFLOW-NEXT: NumberOfLineNumbers: 65535 +# SECOVERFLOW-NEXT: Type: STYP_DATA (0x40) +# SECOVERFLOW-NEXT: } +# SECOVERFLOW-NEXT: Section { +# SECOVERFLOW-NEXT: Index: 3 +# SECOVERFLOW-NEXT: Name: .ovrflo +# SECOVERFLOW-NEXT: NumberOfRelocations: 3 +# SECOVERFLOW-NEXT: NumberOfLineNumbers: 3 +# SECOVERFLOW-NEXT: Size: 0x0 +# SECOVERFLOW-NEXT: RawDataOffset: 0x0 +# SECOVERFLOW-NEXT: RelocationPointer: 0xE0 +# SECOVERFLOW-NEXT: LineNumberPointer: 0x0 +# SECOVERFLOW-NEXT: IndexOfSectionOverflowed: 2 +# SECOVERFLOW-NEXT: IndexOfSectionOverflowed: 2 +# SECOVERFLOW-NEXT: Type: STYP_OVRFLO (0x8000) +# SECOVERFLOW-NEXT: } +# SECOVERFLOW-NEXT: ] + +# RELOCOVERFLOW: Relocations [ +# RELOCOVERFLOW-NEXT: Section (index: 2) .data { +# RELOCOVERFLOW-NEXT: Relocation { +# RELOCOVERFLOW-NEXT: Virtual Address: 0x38 +# RELOCOVERFLOW-NEXT: Symbol: .pb (4) +# RELOCOVERFLOW-NEXT: IsSigned: No +# RELOCOVERFLOW-NEXT: FixupBitValue: 0 +# RELOCOVERFLOW-NEXT: Length: 32 +# RELOCOVERFLOW-NEXT: Type: R_POS (0x0) +# RELOCOVERFLOW-NEXT: } +# RELOCOVERFLOW-NEXT: Relocation { +# RELOCOVERFLOW-NEXT: Virtual Address: 0x3C +# RELOCOVERFLOW-NEXT: Symbol: TOC (12) +# RELOCOVERFLOW-NEXT: IsSigned: No +# RELOCOVERFLOW-NEXT: FixupBitValue: 0 +# RELOCOVERFLOW-NEXT: Length: 32 +# RELOCOVERFLOW-NEXT: Type: R_POS (0x0) +# RELOCOVERFLOW-NEXT: } +# RELOCOVERFLOW-NEXT: Relocation { +# RELOCOVERFLOW-NEXT: Virtual Address: 0x50 +# RELOCOVERFLOW-NEXT: Symbol: .text (2) +# RELOCOVERFLOW-NEXT: IsSigned: No +# RELOCOVERFLOW-NEXT: FixupBitValue: 0 +# RELOCOVERFLOW-NEXT: Length: 32 +# RELOCOVERFLOW-NEXT: Type: R_POS (0x0) +# RELOCOVERFLOW-NEXT: } +# RELOCOVERFLOW-NEXT: } +# RELOCOVERFLOW-NEXT: ] diff --git a/llvm/test/tools/llvm-readobj/xcoff-basic.test b/llvm/test/tools/llvm-readobj/xcoff-basic.test --- a/llvm/test/tools/llvm-readobj/xcoff-basic.test +++ b/llvm/test/tools/llvm-readobj/xcoff-basic.test @@ -10,6 +10,9 @@ # RUN: llvm-readobj --file-header %p/Inputs/xcoff-basic-neg-sym-count.o | \ # RUN: FileCheck --check-prefix=NEGSYMCOUNT %s +# RUN: llvm-readobj --relocs --expand-relocs %p/Inputs/xcoff-basic.o | \ +# RUN: FileCheck --check-prefix=RELOCSEXP %s + # FILEHEADER: File: {{.*}}xcoff-basic.o # FILEHEADER-NEXT: Format: aixcoff-rs6000 # FILEHEADER-NEXT: Arch: powerpc @@ -81,3 +84,77 @@ # xcoff-basic-neg-sym-count.o was stripped using the 'strip' utility, and # manually edited to have a negative symbol table entry count. +# RELOCSEXP: File: {{.*}}xcoff-basic.o +# RELOCSEXP-NEXT: Format: aixcoff-rs6000 +# RELOCSEXP-NEXT: Arch: powerpc +# RELOCSEXP-NEXT: AddressSize: 32bit +# RELOCSEXP-NEXT: Relocations [ +# RELOCSEXP-NEXT: Section (index: 1) .text { +# RELOCSEXP-NEXT: Relocation { +# RELOCSEXP-NEXT: Virtual Address: 0x2 +# RELOCSEXP-NEXT: Symbol: a (85) +# RELOCSEXP-NEXT: IsSigned: Yes +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 16 +# RELOCSEXP-NEXT: Type: R_TOC (0x3) +# RELOCSEXP-NEXT: } + +# RELOCSEXP: Virtual Address: 0x90 +# RELOCSEXP-NEXT: Symbol: .__tls_get_addr (118) +# RELOCSEXP-NEXT: IsSigned: Yes +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 26 +# RELOCSEXP-NEXT: Type: R_RBA (0x18) +# RELOCSEXP-NEXT: } +# RELOCSEXP-NEXT: } +# RELOCSEXP-NEXT: Section (index: 2) .data { +# RELOCSEXP-NEXT: Relocation { +# RELOCSEXP-NEXT: Virtual Address: 0x100 +# RELOCSEXP-NEXT: Symbol: A (78) +# RELOCSEXP-NEXT: IsSigned: No +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 32 +# RELOCSEXP-NEXT: Type: R_POS (0x0) +# RELOCSEXP-NEXT: } + +# RELOCSEXP: Virtual Address: 0x110 +# RELOCSEXP-NEXT: Symbol: J (96) +# RELOCSEXP-NEXT: IsSigned: No +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 32 +# RELOCSEXP-NEXT: Type: R_POS (0x0) +# RELOCSEXP-NEXT: } + +# RELOCSEXP: Virtual Address: 0x114 +# RELOCSEXP-NEXT: Symbol: j (100) +# RELOCSEXP-NEXT: IsSigned: No +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 32 +# RELOCSEXP-NEXT: Type: R_TLS (0x20) +# RELOCSEXP-NEXT: } + +# RELOCSEXP: Virtual Address: 0x124 +# RELOCSEXP-NEXT: Symbol: d (111) +# RELOCSEXP-NEXT: IsSigned: No +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 32 +# RELOCSEXP-NEXT: Type: R_TLSM (0x24) +# RELOCSEXP-NEXT: } + +# RELOCSEXP: Virtual Address: 0x128 +# RELOCSEXP-NEXT: Symbol: (76) +# RELOCSEXP-NEXT: IsSigned: No +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 32 +# RELOCSEXP-NEXT: Type: R_POS (0x0) +# RELOCSEXP-NEXT: } + +# RELOCSEXP: Virtual Address: 0x154 +# RELOCSEXP-NEXT: Symbol: TOC (72) +# RELOCSEXP-NEXT: IsSigned: No +# RELOCSEXP-NEXT: FixupBitValue: 0 +# RELOCSEXP-NEXT: Length: 32 +# RELOCSEXP-NEXT: Type: R_POS (0x0) +# RELOCSEXP-NEXT: } +# RELOCSEXP-NEXT: } +# RELOCSEXP-NEXT:] diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp --- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -56,6 +56,7 @@ // The low order 16 bits of section flags denotes the section type. static constexpr unsigned SectionFlagsTypeMask = 0xffffu; + void printRelocations(ArrayRef Sections); const XCOFFObjectFile &Obj; }; } // anonymous namespace @@ -115,7 +116,58 @@ } void XCOFFDumper::printRelocations() { - llvm_unreachable("Unimplemented functionality for XCOFFDumper"); + if (Obj.is64Bit()) + llvm_unreachable("64-bit relocation output not implemented!"); + else + printRelocations(Obj.sections32()); +} + +static const EnumEntry RelocationTypeNameclass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(R_POS), ECase(R_RL), ECase(R_RLA), ECase(R_NEG), + ECase(R_REL), ECase(R_TOC), ECase(R_TRL), ECase(R_TRLA), + ECase(R_GL), ECase(R_TCL), ECase(R_REF), ECase(R_BA), + ECase(R_BR), ECase(R_RBA), ECase(R_RBR), ECase(R_TLS), + ECase(R_TLS_IE), ECase(R_TLS_LD), ECase(R_TLS_LE), ECase(R_TLSM), + ECase(R_TLSML), ECase(R_TOCU), ECase(R_TOCL) +#undef ECase +}; + +void XCOFFDumper::printRelocations(ArrayRef Sections) { + if (!opts::ExpandRelocs) + report_fatal_error("Unexpanded relocation output not implemented."); + + ListScope LS(W, "Relocations"); + uint16_t Index = 0; + for (const auto &Sec : Sections) { + ++Index; + // Only the .text, .data, .tdata, and STYP_DWARF sections have relocation. + if (Sec.Flags != XCOFF::STYP_TEXT && Sec.Flags != XCOFF::STYP_DATA && + Sec.Flags != XCOFF::STYP_TDATA && Sec.Flags != XCOFF::STYP_DWARF) + continue; + auto Relocations = unwrapOrError(Obj.getFileName(), Obj.relocations(Sec)); + if (Relocations.empty()) + continue; + + W.startLine() << "Section (index: " << Index << ") " << Sec.getName() + << " {\n"; + for (auto Reloc : Relocations) { + StringRef SymbolName = unwrapOrError( + Obj.getFileName(), Obj.getSymbolNameByIndex(Reloc.SymbolIndex)); + + DictScope RelocScope(W, "Relocation"); + W.printHex("Virtual Address", Reloc.VirtualAddress); + W.printNumber("Symbol", SymbolName, Reloc.SymbolIndex); + W.printString("IsSigned", Reloc.isRelocationSigned() ? "Yes" : "No"); + W.printNumber("FixupBitValue", Reloc.isFixupIndicated() ? 1 : 0); + W.printNumber("Length", Reloc.getRelocatedLength()); + W.printEnum("Type", (uint8_t)Reloc.Type, + makeArrayRef(RelocationTypeNameclass)); + } + W.unindent(); + W.startLine() << "}\n"; + } } static const EnumEntry FileStringType[] = {