Skip to content

[llvm-objdump] Implement decoding auxiliary header for xcoff with llvm-objdump --private-headers #105682

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 8 commits into from
Nov 7, 2024
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
103 changes: 103 additions & 0 deletions llvm/test/tools/llvm-objdump/XCOFF/private-header-auxiliary.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
## Test that `llvm-objdump --private-headers` prints out the auxiliary header of an XCOFF object file.
# RUN: yaml2obj %s -o %t1
# RUN: llvm-objdump --private-headers %t1 | \
# RUN: FileCheck %s --check-prefixes=CHECK32,COMMON --match-full-lines --strict-whitespace
# RUN: yaml2obj %s -DMAGIC=0x1F7 -DFLAG64=0x2 -o %t2
# RUN: llvm-objdump --private-headers %t2 | \
# RUN: FileCheck %s --check-prefixes=CHECK64,COMMON --match-full-lines --strict-whitespace

--- !XCOFF
FileHeader:
MagicNumber: [[MAGIC=0x1DF]]
AuxiliaryHeader:
Magic: 0x10B
Version: 0x1
TextSectionSize: 0x8
DataSectionSize: 0x9
BssSectionSize: 0x10
EntryPointAddr: 0x1111
TextStartAddr: 0x2222
DataStartAddr: 0x3333
TOCAnchorAddr: 0x4444
SecNumOfEntryPoint: 1
SecNumOfText: 2
SecNumOfData: 3
SecNumOfTOC: 4
SecNumOfLoader: 5
SecNumOfBSS: 6
MaxAlignOfText: 0x7
MaxAlignOfData: 0x3
ModuleType: 0x1
TextPageSize: 0x1
DataPageSize: 0x1
StackPageSize: 0x1
SecNumOfTData: 7
SecNumOfTBSS: 8
FlagAndTDataAlignment: 0x1
Flag: [[FLAG64=<none>]]
Sections:
- Flags: [ STYP_TEXT ]
SectionData: "1232"
- Flags: [ STYP_DATA ]
SectionData: "5678"
- Flags: [ STYP_BSS ]
SectionData: "9101"
- Flags: [ STYP_TDATA ]
SectionData: "1112"
- Flags: [ STYP_TBSS ]
SectionData: "1314"

# COMMON:---Auxiliary Header:
# COMMON-NEXT:Magic: 0x10b
# COMMON-NEXT:Version: 0x1
# CHECK32-NEXT:Size of .text section: 0x8
# CHECK32-NEXT:Size of .data section: 0x9
# CHECK32-NEXT:Size of .bss section: 0x10
# CHECK32-NEXT:Entry point address: 0x1111
# CHECK64-NEXT:Reserved for debugger: 0x0
# COMMON-NEXT:.text section start address: 0x2222
# COMMON-NEXT:.data section start address: 0x3333
# COMMON-NEXT:TOC anchor address: 0x4444
# COMMON-NEXT:Section number of entryPoint: 1
# COMMON-NEXT:Section number of .text: 2
# COMMON-NEXT:Section number of .data: 3
# COMMON-NEXT:Section number of TOC: 4
# COMMON-NEXT:Section number of loader data: 5
# COMMON-NEXT:Section number of .bss: 6
# COMMON-NEXT:Maxium alignment of .text: 0x7
# COMMON-NEXT:Maxium alignment of .data: 0x3
# COMMON-NEXT:Module type: 0x0
# COMMON-NEXT:CPU type of objects: 0x1
# CHECK32-NEXT:Maximum stack size: 0x0
# CHECK32-NEXT:Maximum data size: 0x0
# CHECK32-NEXT:Reserved for debugger: 0x0
# COMMON-NEXT:Text page size: 0x1
# COMMON-NEXT:Data page size: 0x1
# COMMON-NEXT:Stack page size: 0x1
# COMMON-NEXT:Flag: 0x0
# COMMON-NEXT:Alignment of thread-local storage: 0x1
# CHECK64-NEXT:Size of .text section: 0x8
# CHECK64-NEXT:Size of .data section: 0x9
# CHECK64-NEXT:Size of .bss section: 0x10
# CHECK64-NEXT:Entry point address: 0x1111
# CHECK64-NEXT:Maximum stack size: 0x0
# CHECK64-NEXT:Maximum data size: 0x0
# COMMON-NEXT:Section number for .tdata: 7
# COMMON-NEXT:Section number for .tbss: 8
# CHECK64-NEXT:Additional flags 64-bit XCOFF: 0x2

## Test how llvm-objdump behaves when the auxiliary header of the XCOFF object file contains a partial field.
# RUN: cp %t1 %t1_err1
# RUN: %python -c "with open(r'%t1_err1', 'r+b') as input: input.seek(17); input.write(b'\x45'); input.seek(4); input.write(b'\x00')"
# RUN: llvm-objdump --private-headers %t1_err1 2>&1 | FileCheck %s --check-prefix=WARN1 --match-full-lines --strict-whitespace

# WARN1:{{.*}}: only partial field for Section number for .tdata: at offset (68)
# WARN1-NEXT:Raw data (00)

## Test how llvm-objdump behaves when the auxiliary header of the XCOFF object file contains extra data.
# RUN: cp %t1 %t1_extra
# RUN: %python -c "with open(r'%t1_extra', 'r+b') as input: input.seek(17); input.write(b'\x4f'); input.seek(4); input.write(b'\x00')"
# RUN: llvm-objdump --private-headers %t1_extra 2>&1 | FileCheck %s --check-prefix=EXTRA --match-full-lines --strict-whitespace

# EXTRA:Extra raw data (00000000 000000)
# EXTRA-NOT:{{.}}
204 changes: 200 additions & 4 deletions llvm/tools/llvm-objdump/XCOFFDump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ using namespace llvm::support;

namespace {
class XCOFFDumper : public objdump::Dumper {
enum PrintStyle { Hex, Number };
const XCOFFObjectFile &Obj;
unsigned Width;

Expand All @@ -41,14 +42,32 @@ class XCOFFDumper : public objdump::Dumper {
private:
void printPrivateHeaders() override;
void printFileHeader();
FormattedString formatName(StringRef Name);
void printAuxiliaryHeader();
void printAuxiliaryHeader(const XCOFFAuxiliaryHeader32 *AuxHeader);
void printAuxiliaryHeader(const XCOFFAuxiliaryHeader64 *AuxHeader);
template <typename AuxHeaderMemberType, typename XCOFFAuxiliaryHeader>
void printAuxMemberHelper(PrintStyle Style, const char *MemberName,
const AuxHeaderMemberType &Member,
const XCOFFAuxiliaryHeader *AuxHeader,
uint16_t AuxSize, uint16_t &PartialFieldOffset,
const char *&PartialFieldName);
template <typename XCOFFAuxiliaryHeader>
void checkAndPrintAuxHeaderParseError(const char *PartialFieldName,
uint16_t PartialFieldOffset,
uint16_t AuxSize,
XCOFFAuxiliaryHeader &AuxHeader);

void printBinary(StringRef Name, ArrayRef<uint8_t> Data);
void printHex(StringRef Name, uint64_t Value);
void printNumber(StringRef Name, uint64_t Value);
FormattedString formatName(StringRef Name);
void printStrHex(StringRef Name, StringRef Str, uint64_t Value);
void setWidth(unsigned W) { Width = W; };
};

void XCOFFDumper::printPrivateHeaders() { printFileHeader(); }
void XCOFFDumper::printPrivateHeaders() {
printFileHeader();
printAuxiliaryHeader();
}

FormattedString XCOFFDumper::formatName(StringRef Name) {
return FormattedString(Name, Width, FormattedString::JustifyLeft);
Expand All @@ -67,8 +86,185 @@ void XCOFFDumper::printStrHex(StringRef Name, StringRef Str, uint64_t Value) {
<< ")\n";
}

void XCOFFDumper::printBinary(StringRef Name, ArrayRef<uint8_t> Data) {
unsigned OrgWidth = Width;
Width = 0;
outs() << formatName(Name) << " (" << format_bytes(Data) << ")\n";
Width = OrgWidth;
}

void XCOFFDumper::printAuxiliaryHeader() {
Width = 36;
if (Obj.is64Bit())
printAuxiliaryHeader(Obj.auxiliaryHeader64());
else
printAuxiliaryHeader(Obj.auxiliaryHeader32());
}

template <typename AuxHeaderMemberType, typename XCOFFAuxiliaryHeader>
void XCOFFDumper::printAuxMemberHelper(PrintStyle Style, const char *MemberName,
const AuxHeaderMemberType &Member,
const XCOFFAuxiliaryHeader *AuxHeader,
uint16_t AuxSize,
uint16_t &PartialFieldOffset,
const char *&PartialFieldName) {
ptrdiff_t Offset = reinterpret_cast<const char *>(&Member) -
reinterpret_cast<const char *>(AuxHeader);
if (Offset + sizeof(Member) <= AuxSize) {
if (Style == Hex)
printHex(MemberName, Member);
else
printNumber(MemberName, Member);
} else if (Offset < AuxSize) {
PartialFieldOffset = Offset;
PartialFieldName = MemberName;
}
}

template <typename XCOFFAuxiliaryHeader>
void XCOFFDumper::checkAndPrintAuxHeaderParseError(
const char *PartialFieldName, uint16_t PartialFieldOffset, uint16_t AuxSize,
XCOFFAuxiliaryHeader &AuxHeader) {
if (PartialFieldOffset < AuxSize) {
std::string Buf;
raw_string_ostream OS(Buf);
OS.flush();
OS << FormattedString("Raw data", 0, FormattedString::JustifyLeft) << " ("
<< format_bytes(
ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&AuxHeader) +
PartialFieldOffset,
AuxSize - PartialFieldOffset))
<< ")\n";
reportUniqueWarning(Twine("only partial field for ") + PartialFieldName +
" at offset (" + Twine(PartialFieldOffset) + ")\n" +
OS.str());
} else if (sizeof(AuxHeader) < AuxSize) {
printBinary(
"Extra raw data",
ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&AuxHeader) +
sizeof(AuxHeader),
AuxSize - sizeof(AuxHeader)));
}
}

void XCOFFDumper::printAuxiliaryHeader(
const XCOFFAuxiliaryHeader32 *AuxHeader) {
if (AuxHeader == nullptr)
return;
outs() << "\n---Auxiliary Header:\n";
uint16_t AuxSize = Obj.getOptionalHeaderSize();
uint16_t PartialFieldOffset = AuxSize;
const char *PartialFieldName = nullptr;

auto PrintAuxMember = [&](PrintStyle Style, const char *MemberName,
auto &Member) {
printAuxMemberHelper(Style, MemberName, Member, AuxHeader, AuxSize,
PartialFieldOffset, PartialFieldName);
};

PrintAuxMember(Hex, "Magic:", AuxHeader->AuxMagic);
PrintAuxMember(Hex, "Version:", AuxHeader->Version);
PrintAuxMember(Hex, "Size of .text section:", AuxHeader->TextSize);
PrintAuxMember(Hex, "Size of .data section:", AuxHeader->InitDataSize);
PrintAuxMember(Hex, "Size of .bss section:", AuxHeader->BssDataSize);
PrintAuxMember(Hex, "Entry point address:", AuxHeader->EntryPointAddr);
PrintAuxMember(Hex, ".text section start address:", AuxHeader->TextStartAddr);
PrintAuxMember(Hex, ".data section start address:", AuxHeader->DataStartAddr);
PrintAuxMember(Hex, "TOC anchor address:", AuxHeader->TOCAnchorAddr);
PrintAuxMember(
Number, "Section number of entryPoint:", AuxHeader->SecNumOfEntryPoint);
PrintAuxMember(Number, "Section number of .text:", AuxHeader->SecNumOfText);
PrintAuxMember(Number, "Section number of .data:", AuxHeader->SecNumOfData);
PrintAuxMember(Number, "Section number of TOC:", AuxHeader->SecNumOfTOC);
PrintAuxMember(Number,
"Section number of loader data:", AuxHeader->SecNumOfLoader);
PrintAuxMember(Number, "Section number of .bss:", AuxHeader->SecNumOfBSS);
PrintAuxMember(Hex, "Maxium alignment of .text:", AuxHeader->MaxAlignOfText);
PrintAuxMember(Hex, "Maxium alignment of .data:", AuxHeader->MaxAlignOfData);
PrintAuxMember(Hex, "Module type:", AuxHeader->ModuleType);
PrintAuxMember(Hex, "CPU type of objects:", AuxHeader->CpuFlag);
PrintAuxMember(Hex, "Maximum stack size:", AuxHeader->MaxStackSize);
PrintAuxMember(Hex, "Maximum data size:", AuxHeader->MaxDataSize);
PrintAuxMember(Hex, "Reserved for debugger:", AuxHeader->ReservedForDebugger);
PrintAuxMember(Hex, "Text page size:", AuxHeader->TextPageSize);
PrintAuxMember(Hex, "Data page size:", AuxHeader->DataPageSize);
PrintAuxMember(Hex, "Stack page size:", AuxHeader->StackPageSize);
if (offsetof(XCOFFAuxiliaryHeader32, FlagAndTDataAlignment) +
sizeof(XCOFFAuxiliaryHeader32::FlagAndTDataAlignment) <=
AuxSize) {
printHex("Flag:", AuxHeader->getFlag());
printHex("Alignment of thread-local storage:",
AuxHeader->getTDataAlignment());
}

PrintAuxMember(Number,
"Section number for .tdata:", AuxHeader->SecNumOfTData);
PrintAuxMember(Number, "Section number for .tbss:", AuxHeader->SecNumOfTBSS);

checkAndPrintAuxHeaderParseError(PartialFieldName, PartialFieldOffset,
AuxSize, *AuxHeader);
}

void XCOFFDumper::printAuxiliaryHeader(
const XCOFFAuxiliaryHeader64 *AuxHeader) {
if (AuxHeader == nullptr)
return;
uint16_t AuxSize = Obj.getOptionalHeaderSize();
outs() << "\n---Auxiliary Header:\n";
uint16_t PartialFieldOffset = AuxSize;
const char *PartialFieldName = nullptr;

auto PrintAuxMember = [&](PrintStyle Style, const char *MemberName,
auto &Member) {
printAuxMemberHelper(Style, MemberName, Member, AuxHeader, AuxSize,
PartialFieldOffset, PartialFieldName);
};

PrintAuxMember(Hex, "Magic:", AuxHeader->AuxMagic);
PrintAuxMember(Hex, "Version:", AuxHeader->Version);
PrintAuxMember(Hex, "Reserved for debugger:", AuxHeader->ReservedForDebugger);
PrintAuxMember(Hex, ".text section start address:", AuxHeader->TextStartAddr);
PrintAuxMember(Hex, ".data section start address:", AuxHeader->DataStartAddr);
PrintAuxMember(Hex, "TOC anchor address:", AuxHeader->TOCAnchorAddr);
PrintAuxMember(
Number, "Section number of entryPoint:", AuxHeader->SecNumOfEntryPoint);
PrintAuxMember(Number, "Section number of .text:", AuxHeader->SecNumOfText);
PrintAuxMember(Number, "Section number of .data:", AuxHeader->SecNumOfData);
PrintAuxMember(Number, "Section number of TOC:", AuxHeader->SecNumOfTOC);
PrintAuxMember(Number,
"Section number of loader data:", AuxHeader->SecNumOfLoader);
PrintAuxMember(Number, "Section number of .bss:", AuxHeader->SecNumOfBSS);
PrintAuxMember(Hex, "Maxium alignment of .text:", AuxHeader->MaxAlignOfText);
PrintAuxMember(Hex, "Maxium alignment of .data:", AuxHeader->MaxAlignOfData);
PrintAuxMember(Hex, "Module type:", AuxHeader->ModuleType);
PrintAuxMember(Hex, "CPU type of objects:", AuxHeader->CpuFlag);
PrintAuxMember(Hex, "Text page size:", AuxHeader->TextPageSize);
PrintAuxMember(Hex, "Data page size:", AuxHeader->DataPageSize);
PrintAuxMember(Hex, "Stack page size:", AuxHeader->StackPageSize);
if (offsetof(XCOFFAuxiliaryHeader64, FlagAndTDataAlignment) +
sizeof(XCOFFAuxiliaryHeader64::FlagAndTDataAlignment) <=
AuxSize) {
printHex("Flag:", AuxHeader->getFlag());
printHex("Alignment of thread-local storage:",
AuxHeader->getTDataAlignment());
}
PrintAuxMember(Hex, "Size of .text section:", AuxHeader->TextSize);
PrintAuxMember(Hex, "Size of .data section:", AuxHeader->InitDataSize);
PrintAuxMember(Hex, "Size of .bss section:", AuxHeader->BssDataSize);
PrintAuxMember(Hex, "Entry point address:", AuxHeader->EntryPointAddr);
PrintAuxMember(Hex, "Maximum stack size:", AuxHeader->MaxStackSize);
PrintAuxMember(Hex, "Maximum data size:", AuxHeader->MaxDataSize);
PrintAuxMember(Number,
"Section number for .tdata:", AuxHeader->SecNumOfTData);
PrintAuxMember(Number, "Section number for .tbss:", AuxHeader->SecNumOfTBSS);
PrintAuxMember(Hex, "Additional flags 64-bit XCOFF:", AuxHeader->XCOFF64Flag);

checkAndPrintAuxHeaderParseError(PartialFieldName, PartialFieldOffset,
AuxSize, *AuxHeader);
}

void XCOFFDumper::printFileHeader() {
setWidth(20);
Width = 20;
outs() << "\n---File Header:\n";
printHex("Magic:", Obj.getMagic());
printNumber("NumberOfSections:", Obj.getNumberOfSections());
Expand Down
Loading