diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h --- a/llvm/include/llvm/ObjCopy/CommonConfig.h +++ b/llvm/include/llvm/ObjCopy/CommonConfig.h @@ -221,6 +221,7 @@ // Repeated options std::vector AddSection; std::vector DumpSection; + std::vector DumpOffloading; std::vector UpdateSection; // Section matchers diff --git a/llvm/lib/ObjCopy/ConfigManager.cpp b/llvm/lib/ObjCopy/ConfigManager.cpp --- a/llvm/lib/ObjCopy/ConfigManager.cpp +++ b/llvm/lib/ObjCopy/ConfigManager.cpp @@ -16,13 +16,13 @@ Expected ConfigManager::getCOFFConfig() const { if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || !Common.DumpSection.empty() || - !Common.KeepSection.empty() || !Common.SymbolsToGlobalize.empty() || - !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || - !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || - !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || - Common.ExtractDWO || Common.PreserveDates || Common.StripDWO || - Common.StripNonAlloc || Common.StripSections || Common.Weaken || - Common.DecompressDebugSections || + !Common.DumpOffloading.empty() || !Common.KeepSection.empty() || + !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || + !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || + !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || + !Common.SetSectionAlignment.empty() || Common.ExtractDWO || + Common.PreserveDates || Common.StripDWO || Common.StripNonAlloc || + Common.StripSections || Common.Weaken || Common.DecompressDebugSections || Common.DiscardMode == DiscardType::Locals || !Common.SymbolsToAdd.empty()) return createStringError(llvm::errc::invalid_argument, "option is not supported for COFF"); @@ -33,9 +33,10 @@ Expected ConfigManager::getMachOConfig() const { if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || !Common.KeepSection.empty() || - !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || - !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || - !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || + !Common.DumpOffloading.empty() || !Common.SymbolsToGlobalize.empty() || + !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || + !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || + !Common.SectionsToRename.empty() || !Common.UnneededSymbolsToRemove.empty() || !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() || Common.ExtractDWO || Common.PreserveDates || Common.StripAllGNU || @@ -51,7 +52,7 @@ Expected ConfigManager::getWasmConfig() const { if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition || !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || - !Common.AllocSectionsPrefix.empty() || + !Common.DumpOffloading.empty() || !Common.AllocSectionsPrefix.empty() || Common.DiscardMode != DiscardType::None || !Common.SymbolsToAdd.empty() || !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() || !Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() || @@ -69,7 +70,7 @@ Expected ConfigManager::getXCOFFConfig() const { if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition || !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || - !Common.AllocSectionsPrefix.empty() || + !Common.DumpOffloading.empty() || !Common.AllocSectionsPrefix.empty() || Common.DiscardMode != DiscardType::None || !Common.AddSection.empty() || !Common.DumpSection.empty() || !Common.SymbolsToAdd.empty() || !Common.KeepSection.empty() || !Common.OnlySection.empty() || diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp --- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp +++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp @@ -23,6 +23,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" +#include "llvm/Object/OffloadBinary.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compression.h" @@ -196,6 +197,53 @@ SecName.str().c_str()); } +static Error dumpOffloadToFile(StringRef Identifier, StringRef Filename, + Object &Obj) { + for (auto &Sec : Obj.sections()) { + if (Sec.Type != ELF::SHT_LLVM_OFFLOADING) + continue; + + // This data can be misaligned if extracted from an archive. + std::unique_ptr Buffer = + MemoryBuffer::getMemBuffer(toStringRef(Sec.OriginalData), "", false); + if (!isAddrAligned(Align(OffloadBinary::getAlignment()), + Buffer->getBufferStart())) + Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(), + Buffer->getBufferIdentifier()); + uint64_t Offset = 0; + uint64_t Index = 0; + while (Offset < Buffer->getBufferSize()) { + MemoryBufferRef BufferRef = + MemoryBufferRef(Buffer->getBuffer().drop_front(Offset), ""); + auto BinaryOrErr = OffloadBinary::create(BufferRef); + if (!BinaryOrErr) + return createStringError(object_error::parse_failed, + "while extracting offloading files: " + + toString(BinaryOrErr.takeError())); + const OffloadBinary &Binary = **BinaryOrErr; + + if (Identifier == std::to_string(Index)) { + Expected> OutputOrErr = + FileOutputBuffer::create(Filename, Binary.getImage().size()); + if (!OutputOrErr) + return OutputOrErr.takeError(); + std::unique_ptr Output = std::move(*OutputOrErr); + std::copy(Binary.getImage().bytes_begin(), + Binary.getImage().bytes_end(), Output->getBufferStart()); + if (Error E = Output->commit()) + return std::move(E); + return Error::success(); + } + + Offset += Binary.getSize(); + Index++; + } + } + return createStringError(object_error::parse_failed, + "Offloading image with index '%s' not found", + Identifier.str().c_str()); +} + static bool isCompressable(const SectionBase &Sec) { return !(Sec.Flags & ELF::SHF_COMPRESSED) && StringRef(Sec.Name).startswith(".debug"); @@ -619,6 +667,15 @@ return E; } + // Dump embedded offloading images matching the input identifier. + for (StringRef Flag : Config.DumpOffloading) { + StringRef Identifer; + StringRef FileName; + std::tie(Identifer, FileName) = Flag.split('='); + if (Error E = dumpOffloadToFile(Identifer, FileName, Obj)) + return E; + } + // It is important to remove the sections first. For example, we want to // remove the relocation sections before removing the symbols. That allows // us to avoid reporting the inappropriate errors about removing symbols diff --git a/llvm/test/tools/llvm-objcopy/Offloading/Inputs/binary.yaml b/llvm/test/tools/llvm-objcopy/Offloading/Inputs/binary.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/Offloading/Inputs/binary.yaml @@ -0,0 +1,35 @@ +# Input image with a single character to dump. +!Offload +Members: + - ImageKind: IMG_Bitcode + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "amdgcn-amd-amdhsa" + - Key: "arch" + Value: "gfx908" + Content: "41" # A + - ImageKind: IMG_Bitcode + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "amdgcn-amd-amdhsa" + - Key: "arch" + Value: "gfx90a" + Content: "42" # B + - ImageKind: IMG_Cubin + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_52" + Content: "43" # C + - ImageKind: IMG_None + OffloadKind: OFK_OpenMP + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_70" + Content: "44" # D diff --git a/llvm/test/tools/llvm-objcopy/Offloading/alignment.test b/llvm/test/tools/llvm-objcopy/Offloading/alignment.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/Offloading/alignment.test @@ -0,0 +1,22 @@ +## Ensure we can dump the contents even if the alignment is bad. +# RUN: yaml2obj %s -o %t.elf +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t.bin +# RUN: llvm-objcopy --update-section .llvm.offloading=%t.bin %t.elf +# RUN: llvm-objcopy --dump-offloading 0=- %t.elf | FileCheck %s -DFILENAME=%t + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .misaligned + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: "41424300" + - Name: .llvm.offloading + Type: SHT_LLVM_OFFLOADING + Flags: [ SHF_EXCLUDE ] + AddressAlign: 0x0000000000000001 + +# CHECK: A diff --git a/llvm/test/tools/llvm-objcopy/Offloading/binary.test b/llvm/test/tools/llvm-objcopy/Offloading/binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/Offloading/binary.test @@ -0,0 +1,19 @@ +## Check dumping the offloading image contents by index. +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t.bin +# RUN: yaml2obj %s -o %t.elf +# RUN: llvm-objcopy --update-section .llvm.offloading=%t.bin %t.elf +# RUN: llvm-objcopy --dump-offloading 0=- --dump-offloading 1=- --dump-offloading 2=- --dump-offloading 3=- %t.elf \ +# RUN: | FileCheck %s --match-full-lines --strict-whitespace --implicit-check-not={{.}} + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm.offloading + Type: SHT_LLVM_OFFLOADING + Flags: [ SHF_EXCLUDE ] + AddressAlign: 0x0000000000000008 + +# CHECK:ABCD diff --git a/llvm/test/tools/llvm-objcopy/Offloading/failure.test b/llvm/test/tools/llvm-objcopy/Offloading/failure.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/Offloading/failure.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objcopy --dump-offloading 0=b %t 2>&1 | FileCheck -DFILENAME=%t %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm.offloading + Type: SHT_LLVM_OFFLOADING + 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-objcopy/Offloading/non-elf.test b/llvm/test/tools/llvm-objcopy/Offloading/non-elf.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/Offloading/non-elf.test @@ -0,0 +1,14 @@ +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objcopy --dump-offloading 0=%t.out %t 2>&1 | FileCheck -DFILENAME=%t %s + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: .rdata + Characteristics: [] + SectionData: 00 +symbols: + +# CHECK: error: option is not supported for COFF diff --git a/llvm/test/tools/llvm-objcopy/Offloading/not-found.test b/llvm/test/tools/llvm-objcopy/Offloading/not-found.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/Offloading/not-found.test @@ -0,0 +1,22 @@ +## Ensure we can dump the contents even if the alignment is bad. +# RUN: yaml2obj %s -o %t.elf +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t.bin +# RUN: llvm-objcopy --update-section .llvm.offloading=%t.bin %t.elf +# RUN: not llvm-objcopy --dump-offloading 9999999=- %t.elf 2>&1 | FileCheck %s -DFILENAME=%t.elf + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .misaligned + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: "41424300" + - Name: .llvm.offloading + Type: SHT_LLVM_OFFLOADING + Flags: [ SHF_EXCLUDE ] + AddressAlign: 0x0000000000000001 + +# CHECK: error: '[[FILENAME]]': Offloading image with index '9999999' not found diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -857,6 +857,14 @@ "bad format for --dump-section, expected section=file"); Config.DumpSection.push_back(Value); } + for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_offloading)) { + StringRef Value(Arg->getValue()); + if (Value.split('=').second.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --dump-section, expected identifier=file"); + Config.DumpOffloading.push_back(Value); + } Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -194,6 +194,10 @@ : Eq<"dump-section", "Dump contents of section named
into file ">, MetaVarName<"section=file">; +defm dump_offloading + : Eq<"dump-offloading", + "Dump the embedded offloading buffer into file ">, + MetaVarName<"index=file">; defm prefix_symbols : Eq<"prefix-symbols", "Add to the start of every symbol name">, MetaVarName<"prefix">;