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 @@ -258,6 +258,7 @@ }; StringRef getMappingClassString(XCOFF::StorageMappingClass SMC); +StringRef getRelocationTypeString(XCOFF::RelocationType Type); } // end namespace XCOFF } // end namespace llvm 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 @@ -15,6 +15,7 @@ #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/Object/ObjectFile.h" +#include namespace llvm { namespace object { @@ -247,6 +248,9 @@ void checkSectionAddress(uintptr_t Addr, uintptr_t TableAddr) const; public: + static constexpr uint64_t InvalidRelocOffset = + std::numeric_limits::max(); + // Interface inherited from base classes. void moveSymbolNext(DataRefImpl &Symb) const override; uint32_t getSymbolFlags(DataRefImpl Symb) const override; @@ -278,6 +282,10 @@ relocation_iterator section_rel_end(DataRefImpl Sec) const override; void moveRelocationNext(DataRefImpl &Rel) const override; + + /// \returns the relocation offset with the base address of the containing + /// section as zero, or InvalidRelocOffset on errors (such as a relocation + /// that does not refer to an address in any section). uint64_t getRelocationOffset(DataRefImpl Rel) const override; symbol_iterator getRelocationSymbol(DataRefImpl Rel) const override; uint64_t getRelocationType(DataRefImpl Rel) const override; diff --git a/llvm/lib/BinaryFormat/XCOFF.cpp b/llvm/lib/BinaryFormat/XCOFF.cpp --- a/llvm/lib/BinaryFormat/XCOFF.cpp +++ b/llvm/lib/BinaryFormat/XCOFF.cpp @@ -32,3 +32,36 @@ report_fatal_error("Unhandled storage-mapping class."); } } + +#define RELOC_CASE(A) \ + case XCOFF::A: \ + return #A; +StringRef XCOFF::getRelocationTypeString(XCOFF::RelocationType Type) { + switch (Type) { + RELOC_CASE(R_POS) + RELOC_CASE(R_RL) + RELOC_CASE(R_RLA) + RELOC_CASE(R_NEG) + RELOC_CASE(R_REL) + RELOC_CASE(R_TOC) + RELOC_CASE(R_TRL) + RELOC_CASE(R_TRLA) + RELOC_CASE(R_GL) + RELOC_CASE(R_TCL) + RELOC_CASE(R_REF) + RELOC_CASE(R_BA) + RELOC_CASE(R_BR) + RELOC_CASE(R_RBA) + RELOC_CASE(R_RBR) + RELOC_CASE(R_TLS) + RELOC_CASE(R_TLS_IE) + RELOC_CASE(R_TLS_LD) + RELOC_CASE(R_TLS_LE) + RELOC_CASE(R_TLSM) + RELOC_CASE(R_TLSML) + RELOC_CASE(R_TOCU) + RELOC_CASE(R_TOCL) + } + return "Unknown"; +} +#undef RELOC_CASE 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,7 +17,10 @@ namespace llvm { namespace object { -enum { FUNCTION_SYM = 0x20, SYM_TYPE_MASK = 0x07, RELOC_OVERFLOW = 65535 }; +static const uint8_t FunctionSym = 0x20; +static const uint8_t SymTypeMask = 0x07; +static const uint16_t RelocOverflow = 65535; +static const uint16_t NoRelMask = 0x0001; // Checks that [Ptr, Ptr + Size) bytes fall inside the memory buffer // 'M'. Returns a pointer to the underlying object on success. @@ -314,41 +317,79 @@ } relocation_iterator XCOFFObjectFile::section_rel_begin(DataRefImpl Sec) const { - llvm_unreachable("Not yet implemented!"); - return relocation_iterator(RelocationRef()); + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + const XCOFFSectionHeader32 *SectionEntPtr = toSection32(Sec); + auto RelocationsOrErr = relocations(*SectionEntPtr); + if (Error E = RelocationsOrErr.takeError()) + return relocation_iterator(RelocationRef()); + DataRefImpl Ret; + Ret.p = reinterpret_cast(&*RelocationsOrErr.get().begin()); + return relocation_iterator(RelocationRef(Ret, this)); } relocation_iterator XCOFFObjectFile::section_rel_end(DataRefImpl Sec) const { - llvm_unreachable("Not yet implemented!"); - return relocation_iterator(RelocationRef()); + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + const XCOFFSectionHeader32 *SectionEntPtr = toSection32(Sec); + auto RelocationsOrErr = relocations(*SectionEntPtr); + if (Error E = RelocationsOrErr.takeError()) + return relocation_iterator(RelocationRef()); + DataRefImpl Ret; + Ret.p = reinterpret_cast(&*RelocationsOrErr.get().end()); + return relocation_iterator(RelocationRef(Ret, this)); } void XCOFFObjectFile::moveRelocationNext(DataRefImpl &Rel) const { - llvm_unreachable("Not yet implemented!"); - return; + Rel.p = reinterpret_cast(viewAs(Rel.p) + 1); } uint64_t XCOFFObjectFile::getRelocationOffset(DataRefImpl Rel) const { - llvm_unreachable("Not yet implemented!"); - uint64_t Result = 0; - return Result; + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + const XCOFFRelocation32 *Reloc = viewAs(Rel.p); + const XCOFFSectionHeader32 *Sec32 = sectionHeaderTable32(); + const uint32_t RelocAddress = Reloc->VirtualAddress; + const uint16_t NumberOfSections = getNumberOfSections(); + for (uint16_t i = 0; i < NumberOfSections; ++i) { + // Find which section this relocation is belonging to, and get the + // relocation offset relative to the start of the section. + if (Sec32->VirtualAddress <= RelocAddress && + RelocAddress < Sec32->VirtualAddress + Sec32->SectionSize) { + return RelocAddress - Sec32->VirtualAddress; + } + ++Sec32; + } + return InvalidRelocOffset; } symbol_iterator XCOFFObjectFile::getRelocationSymbol(DataRefImpl Rel) const { - llvm_unreachable("Not yet implemented!"); - return symbol_iterator(SymbolRef()); + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + const XCOFFRelocation32 *Reloc = viewAs(Rel.p); + const uint32_t Index = Reloc->SymbolIndex; + + if (Index >= getLogicalNumberOfSymbolTableEntries32()) + return symbol_end(); + + DataRefImpl SymDRI; + SymDRI.p = reinterpret_cast(getPointerToSymbolTable() + Index); + return symbol_iterator(SymbolRef(SymDRI, this)); } uint64_t XCOFFObjectFile::getRelocationType(DataRefImpl Rel) const { - llvm_unreachable("Not yet implemented!"); - uint64_t Result = 0; - return Result; + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + return viewAs(Rel.p)->Type; } void XCOFFObjectFile::getRelocationTypeName( DataRefImpl Rel, SmallVectorImpl &Result) const { - llvm_unreachable("Not yet implemented!"); - return; + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + const XCOFFRelocation32 *Reloc = viewAs(Rel.p); + StringRef Res = XCOFF::getRelocationTypeString(Reloc->Type); + Result.append(Res.begin(), Res.end()); } uint32_t XCOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const { @@ -358,14 +399,16 @@ } basic_symbol_iterator XCOFFObjectFile::symbol_begin() const { - assert(!is64Bit() && "64-bit support not implemented yet."); + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); DataRefImpl SymDRI; SymDRI.p = reinterpret_cast(SymbolTblPtr); return basic_symbol_iterator(SymbolRef(SymDRI, this)); } basic_symbol_iterator XCOFFObjectFile::symbol_end() const { - assert(!is64Bit() && "64-bit support not implemented yet."); + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); DataRefImpl SymDRI; SymDRI.p = reinterpret_cast( SymbolTblPtr + getLogicalNumberOfSymbolTableEntries32()); @@ -400,9 +443,9 @@ } bool XCOFFObjectFile::isRelocatableObject() const { - bool Result = false; - llvm_unreachable("Not yet implemented!"); - return Result; + if (is64Bit()) + report_fatal_error("64-bit support not implemented yet"); + return !(fileHeader32()->Flags & NoRelMask); } Expected XCOFFObjectFile::getStartAddress() const { @@ -588,7 +631,7 @@ uint16_t SectionIndex = &Sec - sectionHeaderTable32() + 1; - if (Sec.NumberOfRelocations < RELOC_OVERFLOW) + if (Sec.NumberOfRelocations < RelocOverflow) return Sec.NumberOfRelocations; for (const auto &Sec : sections32()) { if (Sec.Flags == XCOFF::STYP_OVRFLO && @@ -758,7 +801,7 @@ if (OwningObjectPtr->is64Bit()) report_fatal_error("64-bit support is unimplemented yet."); - if (getType() & FUNCTION_SYM) + if (getType() & FunctionSym) return true; if (!hasCsectAuxEnt()) @@ -767,7 +810,7 @@ const XCOFFCsectAuxEnt32 *CsectAuxEnt = getXCOFFCsectAuxEnt32(); // A function definition should be a label definition. - if ((CsectAuxEnt->SymbolAlignmentAndType & SYM_TYPE_MASK) != XCOFF::XTY_LD) + if ((CsectAuxEnt->SymbolAlignmentAndType & SymTypeMask) != XCOFF::XTY_LD) return false; if (CsectAuxEnt->StorageMappingClass != XCOFF::XMC_PR) diff --git a/llvm/test/CodeGen/PowerPC/aix-xcoff-reloc.ll b/llvm/test/CodeGen/PowerPC/aix-xcoff-reloc.ll --- a/llvm/test/CodeGen/PowerPC/aix-xcoff-reloc.ll +++ b/llvm/test/CodeGen/PowerPC/aix-xcoff-reloc.ll @@ -4,6 +4,7 @@ ; RUN: llvm-readobj --relocs --expand-relocs %t.o | FileCheck --check-prefix=RELOC %s ; RUN: llvm-readobj -t %t.o | FileCheck --check-prefix=SYM %s ; RUN: llvm-objdump -D %t.o | FileCheck --check-prefix=DIS %s +; RUN: llvm-objdump -r %t.o | FileCheck --check-prefix=DIS_REL %s ; RUN: not --crash llc -verify-machineinstrs -mcpu=pwr4 -mtriple powerpc64-ibm-aix-xcoff -mattr=-altivec -filetype=obj < %s 2>&1 | \ ; RUN: FileCheck --check-prefix=XCOFF64 %s @@ -426,3 +427,17 @@ ; DIS-NEXT: 80: 00 00 00 40 ; DIS: 00000084 : ; DIS-NEXT: 84: 00 00 00 44 + +; DIS_REL: {{.*}}aix-xcoff-reloc.ll.tmp.o: file format aixcoff-rs6000 +; DIS_REL: RELOCATION RECORDS FOR [.text]: +; DIS_REL-NEXT: OFFSET TYPE VALUE +; DIS_REL-NEXT: 00000010 R_RBR .bar +; DIS_REL-NEXT: 0000001a R_TOC globalA +; DIS_REL-NEXT: 0000001e R_TOC globalB +; DIS_REL: RELOCATION RECORDS FOR [.data]: +; DIS_REL-NEXT: OFFSET TYPE VALUE +; DIS_REL-NEXT: 00000030 R_POS arr +; DIS_REL-NEXT: 00000034 R_POS .foo +; DIS_REL-NEXT: 00000038 R_POS TOC +; DIS_REL-NEXT: 00000040 R_POS globalA +; DIS_REL-NEXT: 00000044 R_POS globalB diff --git a/llvm/test/tools/llvm-objdump/XCOFF/disassemble-all.test b/llvm/test/tools/llvm-objdump/XCOFF/disassemble-all.test --- a/llvm/test/tools/llvm-objdump/XCOFF/disassemble-all.test +++ b/llvm/test/tools/llvm-objdump/XCOFF/disassemble-all.test @@ -1,22 +1,28 @@ +# REQUIRES: powerpc-registered-target + # RUN: llvm-objdump -D %p/Inputs/xcoff-section-headers.o | \ -# RUN: FileCheck %s +# RUN: FileCheck --check-prefixes=CHECK,ONLY-D %s + +# RUN: llvm-objdump -D -r %p/Inputs/xcoff-section-headers.o | \ +# RUN: FileCheck --check-prefixes=CHECK,WITH-R %s + +## xcoff-section-headers.o Compiled with IBM XL C/C++ for AIX, V16.1.0 +## compiler command: xlc -qtls -o xcoff-section-headers.o -c test.c +## test.c: +## int a; +## int b = 12345; +## __thread int c; +## __thread double d = 3.14159; +## +## int func(void) { +## return a; +## } -# xcoff-section-headers.o Compiled with IBM XL C/C++ for AIX, V16.1.0 -# compiler command: xlc -qtls -o xcoff-section-headers.o -c test.c -# test.c: -# int a; -# int b = 12345; -# __thread int c; -# __thread double d = 3.14159; -# -# int func(void) { -# return a; -# } -; REQUIRES: powerpc-registered-target -CHECK: Inputs/xcoff-section-headers.o: file format aixcoff-rs6000 -CHECK: Disassembly of section .text: -CHECK: 00000000 <.text>: +CHECK: Inputs/xcoff-section-headers.o: file format aixcoff-rs6000 +CHECK: Disassembly of section .text: +CHECK: 00000000 <.text>: CHECK-NEXT: 0: 80 62 00 04 lwz 3, 4(2) +WITH-R-NEXT: 00000002: R_TOC a CHECK-NEXT: 4: 80 63 00 00 lwz 3, 0(3) CHECK-NEXT: 8: 4e 80 00 20 blr CHECK-NEXT: c: 00 00 00 00 @@ -26,30 +32,31 @@ CHECK-NEXT: 1c: 00 04 66 75 CHECK-NEXT: 20: 6e 63 00 00 xoris 3, 19, 0 CHECK-NEXT: ... -CHECK: Disassembly of section .data: -CHECK: 00000080 : +CHECK: Disassembly of section .data: +CHECK: 00000080 : CHECK-NEXT: 80: 00 00 00 94 -CHECK: 00000084 : +CHECK: 00000084 : CHECK-NEXT: 84: 00 00 00 a4 -CHECK: 00000088 : +CHECK: 00000088 : CHECK-NEXT: 88: 00 00 00 a0 -CHECK: 0000008c : +CHECK: 0000008c : CHECK-NEXT: 8c: 00 00 00 08 -CHECK: 00000090 : -CHECK-NEXT: 90: 00 00 00 00 -CHECK: 00000094 : +CHECK: 00000090 : +ONLY-D-NEXT: 90: 00 00 00 00 +WITH-R-NEXT: ... +CHECK: 00000094 : CHECK-NEXT: 94: 00 00 00 00 CHECK-NEXT: 98: 00 00 00 80 CHECK-NEXT: 9c: 00 00 00 00 -CHECK: 000000a0 : +CHECK: 000000a0 : CHECK-NEXT: a0: 00 00 30 39 -CHECK: Disassembly of section .bss: -CHECK: 000000a4 : -CHECK-NEXT: ... -CHECK: Disassembly of section .tdata: -CHECK: 00000000 : +CHECK: Disassembly of section .bss: +CHECK: 000000a4 : +CHECK-NEXT: ... +CHECK: Disassembly of section .tdata: +CHECK: 00000000 : CHECK-NEXT: 0: 40 09 21 f9 bdnzfl 9, .+8696 CHECK-NEXT: 4: f0 1b 86 6e -CHECK: Disassembly of section .tbss: -CHECK: 00000008 : -CHECK-NEXT: ... +CHECK: Disassembly of section .tbss: +CHECK: 00000008 : +CHECK-NEXT: ... diff --git a/llvm/test/tools/llvm-objdump/XCOFF/print-reloc.test b/llvm/test/tools/llvm-objdump/XCOFF/print-reloc.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/XCOFF/print-reloc.test @@ -0,0 +1,31 @@ +# REQUIRES: powerpc-registered-target + +# RUN: llvm-objdump -r %p/Inputs/xcoff-section-headers.o | \ +# RUN: FileCheck --match-full-lines --strict-whitespace %s + +## xcoff-section-headers.o Compiled with IBM XL C/C++ for AIX, V16.1.0 +## compiler command: xlc -qtls -o xcoff-section-headers.o -c test.c +## test.c: +## int a; +## int b = 12345; +## __thread int c; +## __thread double d = 3.14159; +## +## int func(void) { +## return a; +## } + + CHECK:RELOCATION RECORDS FOR [.text]: + CHECK-NEXT:OFFSET TYPE VALUE + CHECK-NEXT:00000002 R_TOC a +CHECK-EMPTY: + CHECK:RELOCATION RECORDS FOR [.data]: + CHECK-NEXT:OFFSET TYPE VALUE + CHECK-NEXT:00000000 R_POS func + CHECK-NEXT:00000004 R_POS a + CHECK-NEXT:00000008 R_POS b + CHECK-NEXT:0000000c R_TLS c + CHECK-NEXT:00000010 R_TLS d + CHECK-NEXT:00000014 R_POS .func + CHECK-NEXT:00000018 R_POS TOC +CHECK-EMPTY: diff --git a/llvm/tools/llvm-objdump/CMakeLists.txt b/llvm/tools/llvm-objdump/CMakeLists.txt --- a/llvm/tools/llvm-objdump/CMakeLists.txt +++ b/llvm/tools/llvm-objdump/CMakeLists.txt @@ -20,6 +20,7 @@ ELFDump.cpp MachODump.cpp WasmDump.cpp + XCOFFDump.cpp ) if(HAVE_LIBXAR) diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp @@ -0,0 +1,34 @@ +//===-- XCOFFDump.cpp - XCOFF-specific dumper -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the XCOFF-specific dumper for llvm-objdump. +/// +//===----------------------------------------------------------------------===// + +#include "llvm-objdump.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm::object; + +llvm::Error llvm::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj, + const RelocationRef &Rel, + SmallVectorImpl &Result) { + symbol_iterator SymI = Rel.getSymbol(); + if (SymI == Obj->symbol_end()) + return make_error( + "invalid symbol reference in relocation entry", + object_error::parse_failed); + + Expected SymNameOrErr = SymI->getName(); + if (!SymNameOrErr) + return SymNameOrErr.takeError(); + StringRef SymName = *SymNameOrErr; + Result.append(SymName.begin(), SymName.end()); + return Error::success(); +} diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -25,6 +25,7 @@ class MachOObjectFile; class MachOUniversalBinary; class RelocationRef; +class XCOFFObjectFile; } extern cl::opt Demangle; @@ -105,6 +106,9 @@ Error getMachORelocationValueString(const object::MachOObjectFile *Obj, const object::RelocationRef &RelRef, llvm::SmallVectorImpl &Result); +Error getXCOFFRelocationValueString(const object::XCOFFObjectFile *Obj, + const object::RelocationRef &RelRef, + llvm::SmallVectorImpl &Result); uint64_t getELFSectionLMA(const object::ELFSectionRef& Sec); diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -45,6 +45,7 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" @@ -500,6 +501,8 @@ return getWasmRelocationValueString(Wasm, Rel, Result); if (auto *MachO = dyn_cast(Obj)) return getMachORelocationValueString(MachO, Rel, Result); + if (auto *XCOFF = dyn_cast(Obj)) + return getXCOFFRelocationValueString(XCOFF, Rel, Result); llvm_unreachable("unknown object file format"); }