Index: llvm/include/llvm/BinaryFormat/XCOFF.h =================================================================== --- llvm/include/llvm/BinaryFormat/XCOFF.h +++ llvm/include/llvm/BinaryFormat/XCOFF.h @@ -148,6 +148,41 @@ XTY_CM = 3 ///< Common csect definition. For uninitialized storage. }; +// Relocation types, defined in `/usr/include/reloc.h`. +enum RelocationType : uint8_t { + R_POS = 0x00, // A(sym) Positive Relocation. + R_RL = 0x0c, // A(sym) Pos indirect load. modifiable instruction. + R_RLA = 0x0d, // A(sym) Pos Load Address. modifiable instruction. + + R_NEG = 0x01, // -A(sym) Negative Relocation. + R_REL = 0x02, // A(sym-*) Relative to self. + + R_TOC = 0x03, // A(sym-TOC) Relative to TOC. + R_TRL = 0x12, // A(sym-TOC) TOC Relative indirect load. + + R_TRLA = 0x13, // A(sym-TOC) TOC Rel load address. modifiable inst. + + R_GL = 0x05, // A(external TOC of sym) Global Linkage. + R_TCL = 0x06, // A(local TOC of sym) Local object TOC address. + + R_REF = 0x0f, // AL0(sym) Non relocating ref. No garbage collec. + + R_BA = 0x08, // A(sym) Branch absolute. Cannot modify instruction. + R_BR = 0x0a, // A(sym-*) Branch rel to self. non modifiable. + R_RBA = 0x18, // A(sym) Branch absolute. modifiable instruction. + R_RBR = 0x1a, // A(sym-*) Branch rel to self. modifiable instr. + + 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 symbol. + R_TLSML = 0x25, // Module reference to local (own) module. + + R_TOCU = 0x30, // Relative to TOC - high order bits. + R_TOCL = 0x31 // Relative to TOC - low order bits. +}; + struct FileHeader32 { uint16_t Magic; uint16_t NumberOfSections; Index: llvm/include/llvm/Object/XCOFFObjectFile.h =================================================================== --- llvm/include/llvm/Object/XCOFFObjectFile.h +++ llvm/include/llvm/Object/XCOFFObjectFile.h @@ -144,6 +144,44 @@ uint8_t Pad[10]; }; +struct XCOFFRelocation32 { + support::ubig32_t VirtualAddress; + support::ubig32_t SymbolIndex; + + // Packed field, see XR_* masks for details of packing. + uint8_t Info; + + XCOFF::RelocationType Type; +}; + +// 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; + +static bool isRelocationSigned(XCOFFRelocation32 &Reloc) { + return Reloc.Info & XR_SIGN_INDICATOR_MASK; +} + +// If the Fixup bit is set, it indicates that the linker has modified +// the instruction the relocation refers to. +static bool isFixupIndicated(XCOFFRelocation32 &Reloc) { + return Reloc.Info & XR_FIXUP_INDICATOR_MASK; +} + +// Returns the number of bits being relocated. +static uint8_t getRelocatedLength(XCOFFRelocation32 &Reloc) { + // The relocation encodes the bit length being relocated minus 1. Add back + // the 1 to get the actual length being relocated. + return (Reloc.Info & XR_BIASED_LENGTH_MASK) + 1; +} + class XCOFFObjectFile : public ObjectFile { private: const void *FileHeader = nullptr; @@ -231,7 +269,6 @@ uint64_t getRelocationType(DataRefImpl Rel) const override; void getRelocationTypeName(DataRefImpl Rel, SmallVectorImpl &Result) const override; - section_iterator section_begin() const override; section_iterator section_end() const override; uint8_t getBytesInAddress() const override; @@ -273,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; @@ -286,6 +324,9 @@ Expected getSectionByNum(int16_t Num) const; void checkSymbolEntryPointer(uintptr_t SymbolEntPtr) const; + // Relocation-related interfaces. + Expected> + relocations(const XCOFFSectionHeader32 &) const; }; // XCOFFObjectFile class XCOFFSymbolRef { Index: llvm/lib/Object/XCOFFObjectFile.cpp =================================================================== --- llvm/lib/Object/XCOFFObjectFile.cpp +++ 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. @@ -494,6 +491,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; } @@ -640,6 +650,30 @@ return OwningObjectPtr->toSymbolEntry(SymEntDataRef)->NumberOfAuxEntries; } +Expected> +XCOFFObjectFile::relocations(const XCOFFSectionHeader32 &Sec) const { + if (is64Bit()) + report_fatal_error("64-bit relocation support not implemented yet."); + + uintptr_t RelocAddr = getWithOffset(reinterpret_cast(FileHeader), + Sec.FileOffsetToRelocationInfo); + // In an XCOFF32 file, if more than 65,534 relocation entries are required, + // the field value will be 65535, and an STYP_OVRFLO section header will + // contain the actual count of relocation entries in the s_paddr field. Refer + // to the discussion of overflow headers in "Sections and Section Headers". + // TODO: Support relocation entries overflow. + auto RelocationOrErr = getObject( + Data, (void *)RelocAddr, + Sec.NumberOfRelocations * sizeof(XCOFFRelocation32)); + if (Error E = RelocationOrErr.takeError()) + return std::move(E); + + const XCOFFRelocation32 *StartReloc = RelocationOrErr.get(); + + return ArrayRef(StartReloc, + StartReloc + Sec.NumberOfRelocations); +} + const XCOFFCsectAuxEnt32 *XCOFFSymbolRef::getXCOFFCsectAuxEnt32() const { assert(!OwningObjectPtr->is64Bit() && "32-bit interface called on 64-bit object file."); Index: llvm/test/tools/llvm-readobj/xcoff-basic.test =================================================================== --- llvm/test/tools/llvm-readobj/xcoff-basic.test +++ 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,61 @@ # 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: 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:] Index: llvm/tools/llvm-readobj/XCOFFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -50,6 +50,7 @@ // Least significant 3 bits are reserved. static constexpr unsigned SectionFlagsReservedMask = 0x7; + void printRelocations(ArrayRef Sections); const XCOFFObjectFile &Obj; }; } // anonymous namespace @@ -109,7 +110,55 @@ } 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) { + auto Relocations = unwrapOrError(Obj.getFileName(), Obj.relocations(Sec)); + + Index++; + 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", isRelocationSigned(Reloc) ? "Yes" : "No"); + W.printNumber("FixupBitValue", isFixupIndicated(Reloc) ? 1 : 0); + W.printNumber("Length", getRelocatedLength(Reloc)); + W.printEnum("Type", (uint8_t)Reloc.Type, + makeArrayRef(RelocationTypeNameclass)); + } + W.unindent(); + W.startLine() << "}\n"; + } } static const EnumEntry FileStringType[] = {