diff --git a/llvm/test/tools/llvm-objdump/Offloading/Inputs/binary.yaml b/llvm/test/tools/llvm-objdump/Offloading/Inputs/binary.yaml new file mode 100644 index 0000000000000..703c93b24dcc0 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/Inputs/binary.yaml @@ -0,0 +1,30 @@ +!Offload +Members: + - ImageKind: IMG_Bitcode + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "amdgcn-amd-amdhsa" + - Key: "arch" + Value: "gfx908" + - ImageKind: IMG_Bitcode + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "amdgcn-amd-amdhsa" + - Key: "arch" + Value: "gfx90a" + - ImageKind: IMG_Cubin + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_52" + - ImageKind: IMG_None + OffloadKind: OFK_None + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_70" diff --git a/llvm/test/tools/llvm-objdump/Offloading/Inputs/malformed.yaml b/llvm/test/tools/llvm-objdump/Offloading/Inputs/malformed.yaml new file mode 100644 index 0000000000000..016917831fe04 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/Inputs/malformed.yaml @@ -0,0 +1,12 @@ +!Offload +EntryOffset: 999999999 +Members: + - ImageKind: IMG_Cubin + OffloadKind: OFK_OpenMP + Flags: 0 + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_70" + Content: "deadbeef" diff --git a/llvm/test/tools/llvm-objdump/Offloading/binary.test b/llvm/test/tools/llvm-objdump/Offloading/binary.test new file mode 100644 index 0000000000000..3e2b711057b8c --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/binary.test @@ -0,0 +1,40 @@ +## Check that we can dump an offloading binary directly. +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t.bin +# RUN: llvm-objdump --offloading %t.bin | FileCheck %s --match-full-lines --strict-whitespace --implicit-check-not={{.}} + +## Check that we can dump an offloading binary inside of an ELF section. +# RUN: yaml2obj %s -o %t.elf +# RUN: llvm-objcopy --add-section .llvm.offloading=%t.bin %t.elf +# RUN: llvm-objdump --offloading %t.elf | FileCheck %s --check-prefixes=CHECK,ELF --match-full-lines --strict-whitespace --implicit-check-not={{.}} + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + +# ELF:{{.*}}file format elf64-unknown +# ELF-EMPTY: +# CHECK:OFFLOADING IMAGE [0]: +# CHECK-NEXT:kind llvm ir +# CHECK-NEXT:arch gfx908 +# CHECK-NEXT:triple amdgcn-amd-amdhsa +# CHECK-NEXT:producer openmp +# CHECK-EMPTY: +# CHECK-NEXT:OFFLOADING IMAGE [1]: +# CHECK-NEXT:kind llvm ir +# CHECK-NEXT:arch gfx90a +# CHECK-NEXT:triple amdgcn-amd-amdhsa +# CHECK-NEXT:producer openmp +# CHECK-EMPTY: +# CHECK-NEXT:OFFLOADING IMAGE [2]: +# CHECK-NEXT:kind cubin +# CHECK-NEXT:arch sm_52 +# CHECK-NEXT:triple nvptx64-nvidia-cuda +# CHECK-NEXT:producer openmp +# CHECK-EMPTY: +# CHECK-NEXT:OFFLOADING IMAGE [3]: +# CHECK-NEXT:kind +# CHECK-NEXT:arch sm_70 +# CHECK-NEXT:triple nvptx64-nvidia-cuda +# CHECK-NEXT:producer none diff --git a/llvm/test/tools/llvm-objdump/Offloading/content-failure.test b/llvm/test/tools/llvm-objdump/Offloading/content-failure.test new file mode 100644 index 0000000000000..d445226ce5166 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/content-failure.test @@ -0,0 +1,18 @@ +# Test to check if we fail to get the section contents. +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objdump --offloading %t 2>&1 | FileCheck -DFILENAME=%t %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm.offloading + Type: SHT_PROGBITS + Flags: [ SHF_EXCLUDE ] + Address: 0x0 + ShOffset: 0x99999 + AddressAlign: 0x0000000000000008 + +# CHECK: error: '[[FILENAME]]': The end of the file was unexpectedly encountered diff --git a/llvm/test/tools/llvm-objdump/Offloading/failure.test b/llvm/test/tools/llvm-objdump/Offloading/failure.test new file mode 100644 index 0000000000000..854d8e00f3fa8 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/failure.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objdump --offloading %t 2>&1 | FileCheck -DFILENAME=%t %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm.offloading + Type: SHT_PROGBITS + Flags: [ SHF_EXCLUDE ] + Address: 0x0 + AddressAlign: 0x0000000000000008 + Content: "10ffb0ad" + +# CHECK: error: '[[FILENAME]]': while extracting offloading files: Invalid data was encountered while parsing the file diff --git a/llvm/test/tools/llvm-objdump/Offloading/warning.test b/llvm/test/tools/llvm-objdump/Offloading/warning.test new file mode 100644 index 0000000000000..a7ce7db3d7f96 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/warning.test @@ -0,0 +1,16 @@ +## Ensure we give a warning on bad input following good input. +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t-good.bin +# RUN: yaml2obj %S/Inputs/malformed.yaml -o %t-bad.bin +# RUN: cat %t-bad.bin >> %t-good.bin +# RUN: yaml2obj %s -o %t.elf +# RUN: llvm-objcopy --add-section .llvm.offloading=%t-good.bin %t.elf +# RUN: llvm-objdump --offloading %t.elf 2>&1 | FileCheck %s -DFILENAME=%t.elf + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + +# CHECK: OFFLOADING IMAGE [0]: +# CHECK: warning: '[[FILENAME]]': while parsing offloading files: The end of the file was unexpectedly encountered diff --git a/llvm/tools/llvm-objdump/CMakeLists.txt b/llvm/tools/llvm-objdump/CMakeLists.txt index 051c7f5d3454c..2179728e4a8c2 100644 --- a/llvm/tools/llvm-objdump/CMakeLists.txt +++ b/llvm/tools/llvm-objdump/CMakeLists.txt @@ -28,6 +28,7 @@ add_llvm_tool(llvm-objdump COFFDump.cpp ELFDump.cpp MachODump.cpp + OffloadDump.cpp WasmDump.cpp XCOFFDump.cpp DEPENDS diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td index 5b35ada6e124a..00d7d8ccff171 100644 --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -81,6 +81,9 @@ def dwarf_EQ : Joined<["--"], "dwarf=">, def fault_map_section : Flag<["--"], "fault-map-section">, HelpText<"Display the content of the fault map section">; +def offloading : Flag<["--"], "offloading">, + HelpText<"Display the content of the offloading section">; + def file_headers : Flag<["--"], "file-headers">, HelpText<"Display the contents of the overall file header">; def : Flag<["-"], "f">, Alias, diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp new file mode 100644 index 0000000000000..7d4461f0a70ec --- /dev/null +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -0,0 +1,102 @@ +//===-- OffloadDump.cpp - Offloading 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 offloading-specific dumper for llvm-objdump. +/// +//===----------------------------------------------------------------------===// +#include "OffloadDump.h" +#include "llvm-objdump.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::objdump; + +constexpr const char OffloadSectionString[] = ".llvm.offloading"; + +/// Get the printable name of the image kind. +static StringRef getImageName(const OffloadBinary &OB) { + switch (OB.getImageKind()) { + case IMG_Object: + return "elf"; + case IMG_Bitcode: + return "llvm ir"; + case IMG_Cubin: + return "cubin"; + case IMG_Fatbinary: + return "fatbinary"; + case IMG_PTX: + return "ptx"; + default: + return ""; + } +} + +static void printBinary(const OffloadBinary &OB, uint64_t Index) { + outs() << "\nOFFLOADING IMAGE [" << Index << "]:\n"; + outs() << left_justify("kind", 16) << getImageName(OB) << "\n"; + outs() << left_justify("arch", 16) << OB.getArch() << "\n"; + outs() << left_justify("triple", 16) << OB.getTriple() << "\n"; + outs() << left_justify("producer", 16) + << getOffloadKindName(OB.getOffloadKind()) << "\n"; +} + +static Error visitAllBinaries(const OffloadBinary &OB) { + uint64_t Offset = 0; + uint64_t Index = 0; + while (Offset < OB.getMemoryBufferRef().getBufferSize()) { + MemoryBufferRef Buffer = + MemoryBufferRef(OB.getData().drop_front(Offset), OB.getFileName()); + auto BinaryOrErr = OffloadBinary::create(Buffer); + if (!BinaryOrErr) + return BinaryOrErr.takeError(); + + OffloadBinary &Binary = **BinaryOrErr; + printBinary(Binary, Index++); + + Offset += Binary.getSize(); + } + return Error::success(); +} + +/// Print the embedded offloading contents of an ObjectFile \p O. +void llvm::dumpOffloadBinary(const ObjectFile &O) { + for (SectionRef Sec : O.sections()) { + Expected Name = Sec.getName(); + if (!Name || !Name->startswith(OffloadSectionString)) + continue; + + Expected Contents = Sec.getContents(); + if (!Contents) + reportError(Contents.takeError(), O.getFileName()); + + MemoryBufferRef Buffer = MemoryBufferRef(*Contents, O.getFileName()); + auto BinaryOrErr = OffloadBinary::create(Buffer); + if (!BinaryOrErr) + reportError(O.getFileName(), "while extracting offloading files: " + + toString(BinaryOrErr.takeError())); + OffloadBinary &Binary = **BinaryOrErr; + + // Print out all the binaries that are contained in this buffer. If we fail + // to parse a binary before reaching the end of the buffer emit a warning. + if (Error Err = visitAllBinaries(Binary)) + reportWarning("while parsing offloading files: " + + toString(std::move(Err)), + O.getFileName()); + } +} + +/// Print the contents of an offload binary file \p OB. This may contain +/// multiple binaries stored in the same buffer. +void llvm::dumpOffloadSections(const OffloadBinary &OB) { + // Print out all the binaries that are contained at this buffer. If we fail to + // parse a binary before reaching the end of the buffer emit a warning. + if (Error Err = visitAllBinaries(OB)) + reportWarning("while parsing offloading files: " + toString(std::move(Err)), + OB.getFileName()); +} diff --git a/llvm/tools/llvm-objdump/OffloadDump.h b/llvm/tools/llvm-objdump/OffloadDump.h new file mode 100644 index 0000000000000..75f188e9d5065 --- /dev/null +++ b/llvm/tools/llvm-objdump/OffloadDump.h @@ -0,0 +1,22 @@ +//===-- OffloadDump.h -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_OFFLOADDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_OFFLOADDUMP_H + +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/OffloadBinary.h" + +namespace llvm { + +void dumpOffloadSections(const object::OffloadBinary &OB); +void dumpOffloadBinary(const object::ObjectFile &O); + +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index dc7083b70eb7e..7cd47da9efd97 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -20,6 +20,7 @@ #include "ELFDump.h" #include "MachODump.h" #include "ObjdumpOptID.h" +#include "OffloadDump.h" #include "SourcePrinter.h" #include "WasmDump.h" #include "XCOFFDump.h" @@ -58,6 +59,7 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/OffloadBinary.h" #include "llvm/Object/Wasm.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -198,6 +200,7 @@ std::string objdump::MCPU; std::vector objdump::MAttrs; bool objdump::ShowRawInsn; bool objdump::LeadingAddr; +static bool Offloading; static bool RawClangAST; bool objdump::Relocations; bool objdump::PrintImmHex; @@ -2480,6 +2483,8 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, printRawClangAST(O); if (FaultMapSection) printFaultMaps(O); + if (Offloading) + dumpOffloadBinary(*O); } static void dumpObject(const COFFImportFile *I, const Archive *A, @@ -2543,6 +2548,8 @@ static void dumpInput(StringRef file) { dumpObject(O); else if (MachOUniversalBinary *UB = dyn_cast(&Binary)) parseInputMachO(UB); + else if (OffloadBinary *OB = dyn_cast(&Binary)) + dumpOffloadSections(*OB); else reportError(errorCodeToError(object_error::invalid_file_type), file); } @@ -2646,6 +2653,7 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { } DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc); FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section); + Offloading = InputArgs.hasArg(OBJDUMP_offloading); FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers); SectionContents = InputArgs.hasArg(OBJDUMP_full_contents); PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers); @@ -2813,7 +2821,7 @@ int main(int argc, char **argv) { if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null && !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && - !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && + !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading && !(MachOOpt && (Bind || DataInCode || DyldInfo || DylibId || DylibsUsed || ExportsTrie || FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist || LazyBind || LinkOptHints ||