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,191 @@ +## 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 parsed into the 'DWARF' entry and the 'Sections' entry should keep 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 %s | obj2yaml | FileCheck %s --check-prefix=OVERRIDDEN + +# OVERRIDDEN: Sections: +# OVERRIDDEN-NEXT: - Name: .debug_aranges +# OVERRIDDEN-NEXT: Type: SHT_STRTAB +# OVERRIDDEN-NEXT: Flags: [ SHF_ALLOC ] +# OVERRIDDEN-NEXT: Address: 0x0000000000002020 +# OVERRIDDEN-NEXT: Link: .sec +# OVERRIDDEN-NEXT: AddressAlign: 0x0000000000000002 +# OVERRIDDEN-NEXT: EntSize: 0x0000000000000001 +# OVERRIDDEN-NEXT: - Name: .sec +# OVERRIDDEN-NEXT: Type: SHT_PROGBITS +# OVERRIDDEN-NEXT: DWARF: +# OVERRIDDEN-NEXT: debug_aranges: +# OVERRIDDEN-NEXT: - Length: 0x000000000000001C +# OVERRIDDEN-NEXT: Version: 2 +# OVERRIDDEN-NEXT: CuOffset: 0x0000000000001234 +# OVERRIDDEN-NEXT: AddressSize: 0x08 +# OVERRIDDEN-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_aranges + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] ## 0 by default. + Link: .sec ## 0 by default. + EntSize: 1 ## 0 by default. + Info: 1 ## 0 by default. + AddressAlign: 2 ## 0 by default. + Address: 0x2020 ## 0x00 by default. + Offset: 0x50 ## 0x40 for the first section. + - 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 | FileCheck %s --check-prefix=ADDRSIZE4 + +# ADDRSIZE4: DWARF: +# ADDRSIZE4-NEXT: debug_aranges: +# ADDRSIZE4-NEXT: - Length: 0x000000000000001C +# ADDRSIZE4-NEXT: Version: 2 +# ADDRSIZE4-NEXT: CuOffset: 0x0000000000001234 +# ADDRSIZE4-NEXT: AddressSize: 0x04 +# ADDRSIZE4-NEXT: Descriptors: +# ADDRSIZE4-NEXT: - Address: 0x0000000012345678 +# ADDRSIZE4-NEXT: Length: 0x0000000012345678 +# ADDRSIZE4-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_aranges: + - Version: 2 + CuOffset: 0x1234 + AddressSize: 0x04 + Descriptors: + - Address: 0x12345678 + Length: 0x12345678 + +# RUN: yaml2obj --docnum=4 %s | obj2yaml | FileCheck %s --check-prefix=ADDRSIZE8 + +# ADDRSIZE8: DWARF: +# ADDRSIZE8-NEXT: debug_aranges: +# ADDRSIZE8-NEXT: - Length: 0x000000000000002C +# ADDRSIZE8-NEXT: Version: 2 +# ADDRSIZE8-NEXT: CuOffset: 0x0000000000001234 +# ADDRSIZE8-NEXT: AddressSize: 0x08 +# ADDRSIZE8-NEXT: Descriptors: +# ADDRSIZE8-NEXT: - Address: 0x1234567890ABCDEF +# ADDRSIZE8-NEXT: Length: 0x1234567890ABCDEF +# ADDRSIZE8-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_aranges: + - Version: 2 + CuOffset: 0x1234 + AddressSize: 0x08 + Descriptors: + - Address: 0x1234567890abcdef + Length: 0x1234567890abcdef + +## 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=5 %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,10 +307,16 @@ return PhdrsOrErr.takeError(); Y->ProgramHeaders = std::move(*PhdrsOrErr); - llvm::erase_if(Chunks, [this](const std::unique_ptr &C) { - const ELFYAML::Section &S = cast(*C.get()); - return !shouldPrintSection(S, Sections[S.OriginalSecNdx]); - }); + // Dump DWARF sections. + Optional DWARF = dumpDWARFSections(Chunks); + if (DWARF) + Y->DWARF = *DWARF; + + llvm::erase_if( + Chunks, [this, &DWARF](const std::unique_ptr &C) { + const ELFYAML::Section &S = cast(*C.get()); + return !shouldPrintSection(S, Sections[S.OriginalSecNdx], DWARF); + }); Y->Chunks = std::move(Chunks); return Y.release(); @@ -363,6 +392,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 can be successfully parsed, the content will be + // dropped. + 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 +1357,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 +1372,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