diff --git a/llvm/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml b/llvm/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml @@ -0,0 +1,172 @@ +## Test how we dump the .debug_aranges section. + +## a) Test dumping the DWARF32/64 address range table from 32/64-bit, little/big endian object files. +## The .debug_aranges should be written to the 'DWARF' entry and the 'Sections' entry should remain empty. + +# RUN: yaml2obj --docnum=1 -DBITS=32 -DENDIAN=LSB %s | obj2yaml | \ +# RUN: FileCheck -DLENGTH1=24 -DLENGTH2=24 -DADDRSIZE=0x04 %s --check-prefix=BASIC --implicit-check-not=Sections + +# RUN: yaml2obj --docnum=1 -DBITS=32 -DENDIAN=MSB %s | obj2yaml | \ +# RUN: FileCheck -DLENGTH1=24 -DLENGTH2=24 -DADDRSIZE=0x04 %s --check-prefix=BASIC --implicit-check-not=Sections + +# RUN: yaml2obj --docnum=1 -DBITS=64 -DENDIAN=LSB %s | obj2yaml | \ +# RUN: FileCheck -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 %s --check-prefix=BASIC --implicit-check-not=Sections + +# RUN: yaml2obj --docnum=1 -DBITS=64 -DENDIAN=MSB %s | obj2yaml | \ +# RUN: FileCheck -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 %s --check-prefix=BASIC --implicit-check-not=Sections + +# BASIC: DWARF: +# BASIC-NEXT: debug_aranges: +# BASIC-NEXT: - Length: 0x00000000000000[[LENGTH1]] +# BASIC-NEXT: Version: 2 +# BASIC-NEXT: CuOffset: 0x0000000000001234 +# BASIC-NEXT: AddressSize: [[ADDRSIZE]] +# BASIC-NEXT: Descriptors: +# BASIC-NEXT: - Address: 0x0000000000001234 +# BASIC-NEXT: Length: 0x0000000000005678 +# BASIC-NEXT: - Address: 0x0000000000001234 +# BASIC-NEXT: Length: 0x0000000000005678 +# BASIC-NEXT: - Format: DWARF64 +# BASIC-NEXT: Length: 0x00000000000000[[LENGTH2]] +# BASIC-NEXT: Version: 2 +# BASIC-NEXT: CuOffset: 0x1234567890ABCDEF +# BASIC-NEXT: AddressSize: [[ADDRSIZE]] +# BASIC-NEXT: Descriptors: +# BASIC-NEXT: - Address: 0x0000000000001234 +# BASIC-NEXT: Length: 0x0000000000005678 +# BASIC-NEXT: - Address: 0x0000000000001234 +# BASIC-NEXT: Length: 0x0000000000005678 +# BASIC-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS]] + Data: ELFDATA2[[ENDIAN]] + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_aranges: + - Version: 2 + CuOffset: 0x1234 + Descriptors: + - Address: 0x1234 + Length: 0x5678 + - Address: 0x1234 + Length: 0x5678 + - Format: DWARF64 + Version: 2 + CuOffset: 0x1234567890abcdef + Descriptors: + - Address: 0x1234 + Length: 0x5678 + - Address: 0x1234 + Length: 0x5678 + +## b) Test dumping an .debug_aranges section whose section header properties are overridden. + +# RUN: yaml2obj --docnum=2 -DTYPE=SHT_STRTAB %s | obj2yaml | FileCheck %s -DTYPE=STRTAB --check-prefixes=ARANGE,SHDR +# RUN: yaml2obj --docnum=2 -DFLAGS=[SHF_ALLOC] %s | obj2yaml | FileCheck %s -DTYPE=PROGBITS --check-prefixes=ARANGE,SHDR,FLAGS +# RUN: yaml2obj --docnum=2 -DLINK='.sec' %s | obj2yaml | FileCheck %s -DTYPE=PROGBITS --check-prefixes=ARANGE,SHDR,LINK +# RUN: yaml2obj --docnum=2 -DENTSIZE=3 %s | obj2yaml | FileCheck %s -DTYPE=PROGBITS --check-prefixes=ARANGE,SHDR,ENTSIZE +# RUN: yaml2obj --docnum=2 -DINFO=3 %s | obj2yaml | FileCheck %s -DTYPE=PROGBITS --check-prefixes=ARANGE,SHDR,INFO +# RUN: yaml2obj --docnum=2 -DADDRALIGN=3 %s | obj2yaml | FileCheck %s -DTYPE=PROGBITS --check-prefixes=ARANGE,SHDR,ADDRALIGN +# RUN: yaml2obj --docnum=2 -DADDRESS=0x2020 %s | obj2yaml | FileCheck %s -DTYPE=PROGBITS --check-prefixes=ARANGE,SHDR,ADDRESS + +# SHDR: - Name: .debug_aranges +# SHDR-NEXT: Type: SHT_[[TYPE]] +# FLAGS-NEXT: Flags: [ SHF_ALLOC ] +# LINK-NEXT: Link: .sec +# ENTSIZE-NEXT: EntSize: 0x0000000000000003 +# INFO-NEXT: Info: 0x0000000000000003 +# ADDRALIGN-NEXT: AddressAlign: 0x0000000000000003 +# ADDRESS-NEXT: Address: 0x0000000000002020 + +# ARANGE: DWARF: +# ARANGE-NEXT: debug_aranges: +# ARANGE-NEXT: - Length: 0x000000000000001C +# ARANGE-NEXT: Version: 2 +# ARANGE-NEXT: CuOffset: 0x0000000000001234 +# ARANGE-NEXT: AddressSize: 0x08 +# ARANGE-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_aranges + Type: [[TYPE=SHT_PROGBITS]] + Flags: [[FLAGS=]] + Link: [[LINK='']] + EntSize: [[ENTSIZE=]] + Info: [[INFO=]] + AddressAlign: [[ADDRALIGN=0]] + Address: [[ADDRESS=]] + - Name: .sec + Type: SHT_PROGBITS +DWARF: + debug_aranges: + - Version: 2 + CuOffset: 0x1234 + +## c) Test dumping a .debug_aranges section whose address_size doesn't match the +## object file's address size. + +# RUN: yaml2obj --docnum=3 %s | obj2yaml | \ +# RUN: FileCheck %s -DLENGTH=0x000000000000001C -DADDRSIZE=0x04 -DADDRLEN=0x0000000012345678 --check-prefix=ADDRSIZE + +# ADDRSIZE: DWARF: +# ADDRSIZE-NEXT: debug_aranges: +# ADDRSIZE-NEXT: - Length: [[LENGTH]] +# ADDRSIZE-NEXT: Version: 2 +# ADDRSIZE-NEXT: CuOffset: 0x0000000000001234 +# ADDRSIZE-NEXT: AddressSize: [[ADDRSIZE]] +# ADDRSIZE-NEXT: Descriptors: +# ADDRSIZE-NEXT: - Address: [[ADDRLEN]] +# ADDRSIZE-NEXT: Length: [[ADDRLEN]] +# ADDRSIZE-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS=64]] + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_aranges: + - Version: 2 + CuOffset: 0x1234 + AddressSize: [[ADDRSIZE=0x04]] + Descriptors: + - Address: [[ADDRLEN=0x12345678]] + Length: [[ADDRLEN=0x12345678]] + +# RUN: yaml2obj --docnum=3 -DBITS=32 -DADDRSIZE=0x08 -DADDRLEN=0x1234567890abcdef %s | \ +# RUN: obj2yaml | \ +# RUN: FileCheck %s -DLENGTH=0x000000000000002C -DADDRSIZE=0x08 -DADDRLEN=0x1234567890ABCDEF --check-prefix=ADDRSIZE + +## d) Test dumping a .debug_aranges section whose length field doesn't match the actual length. +## This makes the DWARF parser fail to parse it and we will dump it as a raw content section. + +# RUN: yaml2obj --docnum=4 %s | obj2yaml | FileCheck %s --check-prefix=RAW-CONTENT + +# RAW-CONTENT: Sections: +# RAW-CONTENT-NEXT: - Name: .debug_aranges +# RAW-CONTENT-NEXT: Type: SHT_PROGBITS +# RAW-CONTENT-NEXT: AddressAlign: 0x0000000000000001 +# RAW-CONTENT-NEXT: Content: '3412000002003412000008000000000000000000000000000000000000000000' +# RAW-CONTENT-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_aranges: + - Length: 0x1234 + Version: 2 + CuOffset: 0x1234 diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -7,10 +7,13 @@ //===----------------------------------------------------------------------===// #include "Error.h" +#include "obj2yaml.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/ObjectYAML/DWARFYAML.h" #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/ErrorHandling.h" @@ -50,11 +53,15 @@ Expected getSymbolName(uint32_t SymtabNdx, uint32_t SymbolNdx); const object::ELFFile &Obj; + std::unique_ptr DWARFCtx; ArrayRef ShndxTable; Expected> dumpProgramHeaders(ArrayRef> Sections); + Optional + dumpDWARFSections(std::vector> &Sections); + Error dumpSymbols(const Elf_Shdr *Symtab, std::vector &Symbols); Error dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab, @@ -95,18 +102,20 @@ Expected dumpPlaceholderSection(const Elf_Shdr *Shdr); - bool shouldPrintSection(const ELFYAML::Section &S, const Elf_Shdr &SHdr); + bool shouldPrintSection(const ELFYAML::Section &S, const Elf_Shdr &SHdr, + Optional DWARF); public: - ELFDumper(const object::ELFFile &O); + ELFDumper(const object::ELFFile &O, std::unique_ptr DCtx); Expected dump(); }; } template -ELFDumper::ELFDumper(const object::ELFFile &O) - : Obj(O) {} +ELFDumper::ELFDumper(const object::ELFFile &O, + std::unique_ptr DCtx) + : Obj(O), DWARFCtx(std::move(DCtx)) {} template Expected @@ -173,7 +182,8 @@ template bool ELFDumper::shouldPrintSection(const ELFYAML::Section &S, - const Elf_Shdr &SHdr) { + const Elf_Shdr &SHdr, + Optional DWARF) { // We only print the SHT_NULL section at index 0 when it // has at least one non-null field, because yaml2obj // normally creates the zero section at index 0 implicitly. @@ -183,6 +193,19 @@ return std::find_if(Begin, End, [](uint8_t V) { return V != 0; }) != End; } + // Normally we use "DWARF:" to describe contents of DWARF sections. Sometimes + // the content of DWARF sections can be successfully parsed into the "DWARF:" + // entry but their section headers may have special flags, entry size, address + // alignment, etc. We will preserve the header for them under such + // circumstances. + if (DWARF && DWARF->getNonEmptySectionNames().count(S.Name.substr(1))) { + if (const ELFYAML::RawContentSection *RawSec = + dyn_cast(&S)) + return RawSec->Type != ELF::SHT_PROGBITS || RawSec->Flags || + !RawSec->Link.empty() || RawSec->Info || + RawSec->AddressAlign != 1 || RawSec->EntSize; + } + // Normally we use "Symbols:" and "DynamicSymbols:" to describe contents of // symbol tables. We also build and emit corresponding string tables // implicitly. But sometimes it is important to preserve positions and virtual @@ -284,9 +307,12 @@ return PhdrsOrErr.takeError(); Y->ProgramHeaders = std::move(*PhdrsOrErr); - llvm::erase_if(Chunks, [this](const std::unique_ptr &C) { + // Dump DWARF sections. + Y->DWARF = dumpDWARFSections(Chunks); + + llvm::erase_if(Chunks, [this, &Y](const std::unique_ptr &C) { const ELFYAML::Section &S = cast(*C.get()); - return !shouldPrintSection(S, Sections[S.OriginalSecNdx]); + return !shouldPrintSection(S, Sections[S.OriginalSecNdx], Y->DWARF); }); Y->Chunks = std::move(Chunks); @@ -363,6 +389,36 @@ return Ret; } +template +Optional ELFDumper::dumpDWARFSections( + std::vector> &Sections) { + DWARFYAML::Data DWARF; + for (std::unique_ptr &C : Sections) { + if (!C->Name.startswith(".debug_")) + continue; + + if (ELFYAML::RawContentSection *RawSec = + dyn_cast(C.get())) { + Error Err = Error::success(); + cantFail(std::move(Err)); + + if (RawSec->Name == ".debug_aranges") + Err = dumpDebugARanges(*DWARFCtx.get(), DWARF); + + // If the DWARF section cannot be successfully parsed, emit raw content + // instead of an entry in the DWARF section of the YAML. + if (Err) + consumeError(std::move(Err)); + else + RawSec->Content.reset(); + } + } + + if (DWARF.getNonEmptySectionNames().empty()) + return None; + return DWARF; +} + template Expected ELFDumper::dumpPlaceholderSection(const Elf_Shdr *Shdr) { @@ -1298,8 +1354,9 @@ } template -static Error elf2yaml(raw_ostream &Out, const object::ELFFile &Obj) { - ELFDumper Dumper(Obj); +static Error elf2yaml(raw_ostream &Out, const object::ELFFile &Obj, + std::unique_ptr DWARFCtx) { + ELFDumper Dumper(Obj, std::move(DWARFCtx)); Expected YAMLOrErr = Dumper.dump(); if (!YAMLOrErr) return YAMLOrErr.takeError(); @@ -1312,17 +1369,18 @@ } Error elf2yaml(raw_ostream &Out, const object::ObjectFile &Obj) { + std::unique_ptr DWARFCtx = DWARFContext::create(Obj); if (const auto *ELFObj = dyn_cast(&Obj)) - return elf2yaml(Out, *ELFObj->getELFFile()); + return elf2yaml(Out, *ELFObj->getELFFile(), std::move(DWARFCtx)); if (const auto *ELFObj = dyn_cast(&Obj)) - return elf2yaml(Out, *ELFObj->getELFFile()); + return elf2yaml(Out, *ELFObj->getELFFile(), std::move(DWARFCtx)); if (const auto *ELFObj = dyn_cast(&Obj)) - return elf2yaml(Out, *ELFObj->getELFFile()); + return elf2yaml(Out, *ELFObj->getELFFile(), std::move(DWARFCtx)); if (const auto *ELFObj = dyn_cast(&Obj)) - return elf2yaml(Out, *ELFObj->getELFFile()); + return elf2yaml(Out, *ELFObj->getELFFile(), std::move(DWARFCtx)); llvm_unreachable("unknown ELF file format"); } diff --git a/llvm/tools/obj2yaml/obj2yaml.h b/llvm/tools/obj2yaml/obj2yaml.h --- a/llvm/tools/obj2yaml/obj2yaml.h +++ b/llvm/tools/obj2yaml/obj2yaml.h @@ -41,5 +41,6 @@ } llvm::Error dwarf2yaml(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); - +llvm::Error dumpDebugARanges(llvm::DWARFContext &DCtx, + llvm::DWARFYAML::Data &Y); #endif