Skip to content

[DirectX] Add ObjectFile boilerplate for objdump #151434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/include/llvm-c/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef enum {
LLVMBinaryTypeMachO64B, /**< MachO 64-bit, big endian. */
LLVMBinaryTypeWasm, /**< Web Assembly. */
LLVMBinaryTypeOffload, /**< Offloading fatbinary. */
LLVMBinaryTypeDXcontainer, /**< DirectX Binary Container. */

} LLVMBinaryType;

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/Object/Binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class LLVM_ABI Binary {

ID_GOFF,
ID_Wasm,
ID_DXContainer,

ID_EndObjects
};
Expand Down Expand Up @@ -161,6 +162,8 @@ class LLVM_ABI Binary {

bool isWinRes() const { return TypeID == ID_WinRes; }

bool isDXContainer() const { return TypeID == ID_DXContainer; }

Triple::ObjectFormatType getTripleObjectFormat() const {
if (isCOFF())
return Triple::COFF;
Expand Down
75 changes: 75 additions & 0 deletions llvm/include/llvm/Object/DXContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -499,6 +500,7 @@ class DXContainer {
} IteratorState;

friend class DXContainer;
friend class DXContainerObjectFile;

PartIterator(const DXContainer &C,
SmallVectorImpl<uint32_t>::const_iterator It)
Expand Down Expand Up @@ -584,6 +586,79 @@ class DXContainer {
}
};

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

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/Object/ObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class SectionRef;
class SymbolRef;
class symbol_iterator;
class WasmObjectFile;
class DXContainerObjectFile;

using section_iterator = content_iterator<SectionRef>;

Expand Down Expand Up @@ -401,6 +402,9 @@ class LLVM_ABI ObjectFile : public SymbolicFile {

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
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Object/Binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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:
Expand Down
181 changes: 181 additions & 0 deletions llvm/lib/Object/DXContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bogner @llvm-beanz I think the DXContainerObjectFile should present as if it had empty symbol and relocation tables, rather than making these operations unreachable.

From a design perspective you should be able to iterate over the set of symbols and relocations in an object file, and empty sets are perfectly legal.

As a practical matter presenting empty sets will allow llvm-objdump -r and llvm-objdump -t to behave sensibly, whereas unreachable will cause them to crash.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think what you're saying here is in conflict to having llvm_unreachable here. With this patch llvm-objdump -r produces the output:

>bin/llvm-objdump -r part-headers.yaml.tmp

:       file format directx container

and llvm-objdump -t produces:

> bin/llvm-objdump -t part-headers.yaml.tmp

:       file format directx container

SYMBOL TABLE:

The iterators are implemented and all effectively return end iterators so that the result is an empty set. The only way you'd really hit these unreachable is if you tried to access or increment one of the iterators, and since they would be invalid, that would always be a logic error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're totally right. My bad!

LGTM. :)

}
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;
}
Comment on lines +610 to +616
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't actually see where these APIs are used, but arguably the DXIL section could be text and the other sections data I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wasn't sure what to do about DXIL. I'm not sure if it should be Text or Bitcode (both are potentially reasonable). My gut is actually bitcode so we can create an IRObject from it to disassemble, but I'd like to defer that.

I suppose we could make everything else data sections. It isn't really what they traditionally mean by data because it isn't constant data referred to by the program.

These are mostly used during symbol dumping to denote what type of section a symbol points into. Are you okay pushing off any work on this into subsequent changes when we can figure out how to handle other features? Alternatively I could just make isSectionData return true for now and then everything is just data.


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);
}
2 changes: 2 additions & 0 deletions llvm/lib/Object/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Object/ObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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");
}
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Object/SymbolicFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down
Loading