[DirectX] Add ObjectFile boilerplate for objdump (#151434)

This change adds boilerplate code to implement the object::ObjectFile
interface for the DXContainer object file and an empty implementation of
the objdump Dumper object.

Adding an ObjectFile implementation for DXContainer is a bit odd because
the DXContainer format doesn't have a symbol table, so there isn't a
reasonable implementation for the SymbolicFile interfaces. That said, it
does have sections, and it will be useful for objdump to be able to
inspect some of the structured data stored in some of the special named
sections.

At this point in the implementation it can't do much other than dump the
part names, offsets, and sizes. Dumping detailed structured section
contents to be extended in subsequent PRs.

Fixes #151433
This commit is contained in:
Chris B 2025-08-04 10:57:25 -05:00 committed by GitHub
parent 2cf276d48a
commit 2fe96439fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 373 additions and 2 deletions

View File

@ -55,6 +55,7 @@ typedef enum {
LLVMBinaryTypeMachO64B, /**< MachO 64-bit, big endian. */
LLVMBinaryTypeWasm, /**< Web Assembly. */
LLVMBinaryTypeOffload, /**< Offloading fatbinary. */
LLVMBinaryTypeDXcontainer, /**< DirectX Binary Container. */
} LLVMBinaryType;

View File

@ -72,6 +72,7 @@ protected:
ID_GOFF,
ID_Wasm,
ID_DXContainer,
ID_EndObjects
};
@ -161,6 +162,8 @@ public:
bool isWinRes() const { return TypeID == ID_WinRes; }
bool isDXContainer() const { return TypeID == ID_DXContainer; }
Triple::ObjectFormatType getTripleObjectFormat() const {
if (isCOFF())
return Triple::COFF;

View File

@ -20,6 +20,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/DXContainer.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
@ -499,6 +500,7 @@ public:
} IteratorState;
friend class DXContainer;
friend class DXContainerObjectFile;
PartIterator(const DXContainer &C,
SmallVectorImpl<uint32_t>::const_iterator It)
@ -584,6 +586,79 @@ public:
}
};
class DXContainerObjectFile : public ObjectFile {
private:
friend class ObjectFile;
DXContainer Container;
using PartData = DXContainer::PartIterator::PartData;
llvm::SmallVector<PartData> Parts;
using PartIterator = llvm::SmallVector<PartData>::iterator;
DXContainerObjectFile(DXContainer C)
: ObjectFile(ID_DXContainer, MemoryBufferRef(C.getData(), "")),
Container(C) {
for (auto &P : C)
Parts.push_back(P);
}
public:
static bool classof(const Binary *v) { return v->isDXContainer(); }
Expected<StringRef> getSymbolName(DataRefImpl) const override;
Expected<uint64_t> getSymbolAddress(DataRefImpl Symb) const override;
uint64_t getSymbolValueImpl(DataRefImpl Symb) const override;
uint64_t getCommonSymbolSizeImpl(DataRefImpl Symb) const override;
Expected<SymbolRef::Type> getSymbolType(DataRefImpl Symb) const override;
Expected<section_iterator> getSymbolSection(DataRefImpl Symb) const override;
void moveSectionNext(DataRefImpl &Sec) const override;
Expected<StringRef> getSectionName(DataRefImpl Sec) const override;
uint64_t getSectionAddress(DataRefImpl Sec) const override;
uint64_t getSectionIndex(DataRefImpl Sec) const override;
uint64_t getSectionSize(DataRefImpl Sec) const override;
Expected<ArrayRef<uint8_t>>
getSectionContents(DataRefImpl Sec) const override;
uint64_t getSectionAlignment(DataRefImpl Sec) const override;
bool isSectionCompressed(DataRefImpl Sec) const override;
bool isSectionText(DataRefImpl Sec) const override;
bool isSectionData(DataRefImpl Sec) const override;
bool isSectionBSS(DataRefImpl Sec) const override;
bool isSectionVirtual(DataRefImpl Sec) const override;
relocation_iterator section_rel_begin(DataRefImpl Sec) const override;
relocation_iterator section_rel_end(DataRefImpl Sec) const override;
void moveRelocationNext(DataRefImpl &Rel) const override;
uint64_t getRelocationOffset(DataRefImpl Rel) const override;
symbol_iterator getRelocationSymbol(DataRefImpl Rel) const override;
uint64_t getRelocationType(DataRefImpl Rel) const override;
void getRelocationTypeName(DataRefImpl Rel,
SmallVectorImpl<char> &Result) const override;
section_iterator section_begin() const override;
section_iterator section_end() const override;
uint8_t getBytesInAddress() const override;
StringRef getFileFormatName() const override;
Triple::ArchType getArch() const override;
Expected<SubtargetFeatures> getFeatures() const override;
void moveSymbolNext(DataRefImpl &Symb) const override {}
Error printSymbolName(raw_ostream &OS, DataRefImpl Symb) const override;
Expected<uint32_t> getSymbolFlags(DataRefImpl Symb) const override;
basic_symbol_iterator symbol_begin() const override {
return basic_symbol_iterator(SymbolRef());
}
basic_symbol_iterator symbol_end() const override {
return basic_symbol_iterator(SymbolRef());
}
bool is64Bit() const override { return false; }
bool isRelocatableObject() const override { return false; }
};
} // namespace object
} // namespace llvm

View File

@ -44,6 +44,7 @@ class SectionRef;
class SymbolRef;
class symbol_iterator;
class WasmObjectFile;
class DXContainerObjectFile;
using section_iterator = content_iterator<SectionRef>;
@ -401,6 +402,9 @@ public:
static Expected<std::unique_ptr<WasmObjectFile>>
createWasmObjectFile(MemoryBufferRef Object);
static Expected<std::unique_ptr<DXContainerObjectFile>>
createDXContainerObjectFile(MemoryBufferRef Object);
};
/// A filtered iterator for SectionRefs that skips sections based on some given

View File

@ -75,6 +75,7 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
case file_magic::xcoff_object_32:
case file_magic::xcoff_object_64:
case file_magic::wasm_object:
case file_magic::dxcontainer_object:
return ObjectFile::createSymbolicFile(Buffer, Type, Context, InitContent);
case file_magic::macho_universal_binary:
return MachOUniversalBinary::create(Buffer);
@ -87,7 +88,6 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
case file_magic::clang_ast:
case file_magic::cuda_fatbinary:
case file_magic::coff_cl_gl_object:
case file_magic::dxcontainer_object:
case file_magic::offload_bundle:
case file_magic::offload_bundle_compressed:
case file_magic::spirv_object:

View File

@ -11,6 +11,7 @@
#include "llvm/Object/Error.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/TargetParser/SubtargetFeature.h"
using namespace llvm;
using namespace llvm::object;
@ -515,3 +516,183 @@ uint8_t DirectX::PSVRuntimeInfo::getSigPatchOrPrimCount() const {
return P->SigPatchOrPrimElements;
return 0;
}
class DXNotSupportedError : public ErrorInfo<DXNotSupportedError> {
public:
static char ID;
DXNotSupportedError(StringRef S) : FeatureString(S) {}
void log(raw_ostream &OS) const override {
OS << "DXContainer does not support " << FeatureString;
}
std::error_code convertToErrorCode() const override {
return inconvertibleErrorCode();
}
private:
StringRef FeatureString;
};
char DXNotSupportedError::ID = 0;
Expected<section_iterator>
DXContainerObjectFile::getSymbolSection(DataRefImpl Symb) const {
return make_error<DXNotSupportedError>("Symbol sections");
}
Expected<StringRef> DXContainerObjectFile::getSymbolName(DataRefImpl) const {
return make_error<DXNotSupportedError>("Symbol names");
}
Expected<uint64_t>
DXContainerObjectFile::getSymbolAddress(DataRefImpl Symb) const {
return make_error<DXNotSupportedError>("Symbol addresses");
}
uint64_t DXContainerObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
llvm_unreachable("DXContainer does not support symbols");
}
uint64_t
DXContainerObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
llvm_unreachable("DXContainer does not support symbols");
}
Expected<SymbolRef::Type>
DXContainerObjectFile::getSymbolType(DataRefImpl Symb) const {
return make_error<DXNotSupportedError>("Symbol types");
}
void DXContainerObjectFile::moveSectionNext(DataRefImpl &Sec) const {
PartIterator It = reinterpret_cast<PartIterator>(Sec.p);
if (It == Parts.end())
return;
++It;
Sec.p = reinterpret_cast<uintptr_t>(It);
}
Expected<StringRef>
DXContainerObjectFile::getSectionName(DataRefImpl Sec) const {
PartIterator It = reinterpret_cast<PartIterator>(Sec.p);
return StringRef(It->Part.getName());
}
uint64_t DXContainerObjectFile::getSectionAddress(DataRefImpl Sec) const {
PartIterator It = reinterpret_cast<PartIterator>(Sec.p);
return It->Offset;
}
uint64_t DXContainerObjectFile::getSectionIndex(DataRefImpl Sec) const {
return (Sec.p - reinterpret_cast<uintptr_t>(Parts.begin())) /
sizeof(PartIterator);
}
uint64_t DXContainerObjectFile::getSectionSize(DataRefImpl Sec) const {
PartIterator It = reinterpret_cast<PartIterator>(Sec.p);
return It->Data.size();
}
Expected<ArrayRef<uint8_t>>
DXContainerObjectFile::getSectionContents(DataRefImpl Sec) const {
PartIterator It = reinterpret_cast<PartIterator>(Sec.p);
return ArrayRef<uint8_t>(It->Data.bytes_begin(), It->Data.size());
}
uint64_t DXContainerObjectFile::getSectionAlignment(DataRefImpl Sec) const {
return 1;
}
bool DXContainerObjectFile::isSectionCompressed(DataRefImpl Sec) const {
return false;
}
bool DXContainerObjectFile::isSectionText(DataRefImpl Sec) const {
return false;
}
bool DXContainerObjectFile::isSectionData(DataRefImpl Sec) const {
return false;
}
bool DXContainerObjectFile::isSectionBSS(DataRefImpl Sec) const {
return false;
}
bool DXContainerObjectFile::isSectionVirtual(DataRefImpl Sec) const {
return false;
}
relocation_iterator
DXContainerObjectFile::section_rel_begin(DataRefImpl Sec) const {
return relocation_iterator(RelocationRef());
}
relocation_iterator
DXContainerObjectFile::section_rel_end(DataRefImpl Sec) const {
return relocation_iterator(RelocationRef());
}
void DXContainerObjectFile::moveRelocationNext(DataRefImpl &Rel) const {
llvm_unreachable("DXContainer does not support relocations");
}
uint64_t DXContainerObjectFile::getRelocationOffset(DataRefImpl Rel) const {
llvm_unreachable("DXContainer does not support relocations");
}
symbol_iterator
DXContainerObjectFile::getRelocationSymbol(DataRefImpl Rel) const {
return symbol_iterator(SymbolRef());
}
uint64_t DXContainerObjectFile::getRelocationType(DataRefImpl Rel) const {
llvm_unreachable("DXContainer does not support relocations");
}
void DXContainerObjectFile::getRelocationTypeName(
DataRefImpl Rel, SmallVectorImpl<char> &Result) const {
llvm_unreachable("DXContainer does not support relocations");
}
section_iterator DXContainerObjectFile::section_begin() const {
DataRefImpl Sec;
Sec.p = reinterpret_cast<uintptr_t>(Parts.begin());
return section_iterator(SectionRef(Sec, this));
}
section_iterator DXContainerObjectFile::section_end() const {
DataRefImpl Sec;
Sec.p = reinterpret_cast<uintptr_t>(Parts.end());
return section_iterator(SectionRef(Sec, this));
}
uint8_t DXContainerObjectFile::getBytesInAddress() const { return 4; }
StringRef DXContainerObjectFile::getFileFormatName() const {
return "DirectX Container";
}
Triple::ArchType DXContainerObjectFile::getArch() const { return Triple::dxil; }
Expected<SubtargetFeatures> DXContainerObjectFile::getFeatures() const {
return SubtargetFeatures();
}
Error DXContainerObjectFile::printSymbolName(raw_ostream &OS,
DataRefImpl Symb) const {
return make_error<DXNotSupportedError>("Symbol names");
}
Expected<uint32_t>
DXContainerObjectFile::getSymbolFlags(DataRefImpl Symb) const {
return make_error<DXNotSupportedError>("Symbol flags");
}
Expected<std::unique_ptr<DXContainerObjectFile>>
ObjectFile::createDXContainerObjectFile(MemoryBufferRef Object) {
auto ExC = DXContainer::create(Object);
if (!ExC)
return ExC.takeError();
std::unique_ptr<DXContainerObjectFile> Obj(new DXContainerObjectFile(*ExC));
return std::move(Obj);
}

View File

@ -124,6 +124,8 @@ LLVMBinaryType LLVMBinaryGetType(LLVMBinaryRef BR) {
return LLVMBinaryTypeOffload;
case ID_Wasm:
return LLVMBinaryTypeWasm;
case ID_DXContainer:
return LLVMBinaryTypeDXcontainer;
case ID_StartObjects:
case ID_EndObjects:
llvm_unreachable("Marker types are not valid binary kinds!");

View File

@ -15,6 +15,7 @@
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/DXContainer.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/Wasm.h"
@ -165,7 +166,6 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type,
case file_magic::goff_object:
case file_magic::cuda_fatbinary:
case file_magic::offload_binary:
case file_magic::dxcontainer_object:
case file_magic::offload_bundle:
case file_magic::offload_bundle_compressed:
case file_magic::spirv_object:
@ -201,6 +201,8 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type,
return createXCOFFObjectFile(Object, Binary::ID_XCOFF64);
case file_magic::wasm_object:
return createWasmObjectFile(Object);
case file_magic::dxcontainer_object:
return createDXContainerObjectFile(Object);
}
llvm_unreachable("Unexpected Object File Type");
}

View File

@ -68,6 +68,7 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type,
case file_magic::xcoff_object_32:
case file_magic::xcoff_object_64:
case file_magic::wasm_object:
case file_magic::dxcontainer_object:
return ObjectFile::createObjectFile(Object, Type, InitContent);
case file_magic::coff_import_library:
return std::unique_ptr<SymbolicFile>(new COFFImportFile(Object));
@ -123,6 +124,7 @@ bool SymbolicFile::isSymbolicFile(file_magic Type, const LLVMContext *Context) {
case file_magic::elf_relocatable:
case file_magic::macho_object:
case file_magic::coff_object:
case file_magic::dxcontainer_object:
return true;
default:
return false;

View File

@ -0,0 +1,64 @@
# RUN: yaml2obj %s -o %t
# RUN: llvm-objdump -h %t | FileCheck %s --check-prefix=HEADERS
# RUN: llvm-objdump -s %t | FileCheck %s --check-prefix=CONTENTS
#HEADERS: Idx Name Size VMA Type
#HEADERS-NEXT: 0 FKE0 00000008 0000003c
#HEADERS-NEXT: 1 FKE1 00000008 0000004c
#HEADERS-NEXT: 2 FKE2 00000008 0000005c
#HEADERS-NEXT: 3 FKE3 00000078 0000006c
#HEADERS-NEXT: 4 FKE4 00000698 000000ec
#HEADERS-NEXT: 5 FKE5 00000014 0000078c
#HEADERS-NEXT: 6 DXIL 0000001c 000007a8
#CONTENTS: Contents of section FKE0:
#CONTENTS: 003c 00000000 00000000 ........
#CONTENTS: Contents of section FKE1:
#CONTENTS: 004c 00000000 00000000 ........
#CONTENTS: Contents of section FKE2:
#CONTENTS: 005c 00000000 00000000 ........
#CONTENTS: Contents of section FKE3:
#CONTENTS: 006c 00000000 00000000 00000000 00000000 ................
#CONTENTS: Contents of section FKE4:
#CONTENTS: 00ec 00000000 00000000 00000000 00000000 ................
#CONTENTS: Contents of section FKE5:
#CONTENTS: 078c 00000000 00000000 00000000 00000000 ................
#CONTENTS: Contents of section DXIL:
#CONTENTS-NEXT: 07a8 65000500 08000000 4458494c 05010000 e.......DXIL....
#CONTENTS-NEXT: 07b8 10000000 04000000 4243c0de ........BC..
--- !dxcontainer
Header:
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
Version:
Major: 1
Minor: 0
FileSize: 3548
PartCount: 7
PartOffsets: [ 60, 76, 92, 108, 236, 1932, 1960 ]
Parts:
- Name: FKE0
Size: 8
- Name: FKE1
Size: 8
- Name: FKE2
Size: 8
- Name: FKE3
Size: 120
- Name: FKE4
Size: 1688
- Name: FKE5
Size: 20
- Name: DXIL
Size: 28
Program:
MajorVersion: 6
MinorVersion: 5
ShaderKind: 5
Size: 8
DXILMajorVersion: 1
DXILMinorVersion: 5
DXILSize: 4
DXIL: [ 0x42, 0x43, 0xC0, 0xDE, ]
...

View File

@ -28,6 +28,7 @@ add_llvm_tool(llvm-objdump
llvm-objdump.cpp
SourcePrinter.cpp
COFFDump.cpp
DXContainerDump.cpp
ELFDump.cpp
MachODump.cpp
OffloadDump.cpp

View File

@ -0,0 +1,30 @@
//===----------------------------------------------------------------------===//
//
// 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 DXContainer-specific dumper for llvm-objdump.
///
//===----------------------------------------------------------------------===//
#include "llvm-objdump.h"
#include "llvm/Object/DXContainer.h"
using namespace llvm;
namespace {
class DXContainerDumper : public objdump::Dumper {
public:
DXContainerDumper(const object::DXContainerObjectFile &Obj)
: objdump::Dumper(Obj) {}
};
} // namespace
std::unique_ptr<objdump::Dumper> llvm::objdump::createDXContainerDumper(
const object::DXContainerObjectFile &Obj) {
return std::make_unique<DXContainerDumper>(Obj);
}

View File

@ -50,6 +50,7 @@
#include "llvm/Object/BuildID.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/DXContainer.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ELFTypes.h"
#include "llvm/Object/FaultMapParser.h"
@ -386,6 +387,8 @@ static Expected<std::unique_ptr<Dumper>> createDumper(const ObjectFile &Obj) {
return createWasmDumper(*O);
if (const auto *O = dyn_cast<XCOFFObjectFile>(&Obj))
return createXCOFFDumper(*O);
if (const auto *O = dyn_cast<DXContainerObjectFile>(&Obj))
return createDXContainerDumper(*O);
return createStringError(errc::invalid_argument,
"unsupported object file format");

View File

@ -36,6 +36,7 @@ class ELFObjectFileBase;
class MachOObjectFile;
class WasmObjectFile;
class XCOFFObjectFile;
class DXContainer;
} // namespace object
namespace objdump {
@ -105,6 +106,8 @@ std::unique_ptr<Dumper> createELFDumper(const object::ELFObjectFileBase &Obj);
std::unique_ptr<Dumper> createMachODumper(const object::MachOObjectFile &Obj);
std::unique_ptr<Dumper> createWasmDumper(const object::WasmObjectFile &Obj);
std::unique_ptr<Dumper> createXCOFFDumper(const object::XCOFFObjectFile &Obj);
std::unique_ptr<Dumper>
createDXContainerDumper(const object::DXContainerObjectFile &Obj);
// Various helper functions.