Index: include/llvm/BinaryFormat/Dwarf.def =================================================================== --- include/llvm/BinaryFormat/Dwarf.def +++ include/llvm/BinaryFormat/Dwarf.def @@ -871,6 +871,7 @@ HANDLE_DWARF_SECTION(DebugGnuPubtypes, ".debug_gnu_pubtypes", "debug-gnu-pubtypes") HANDLE_DWARF_SECTION(DebugRanges, ".debug_ranges", "debug-ranges") HANDLE_DWARF_SECTION(DebugRnglists, ".debug_rnglists", "debug-rnglists") +HANDLE_DWARF_SECTION(DebugAddr, ".debug_addr", "debug-addr") HANDLE_DWARF_SECTION(DebugStr, ".debug_str", "debug-str") HANDLE_DWARF_SECTION(DebugStrOffsets, ".debug_str_offsets", "debug-str-offsets") HANDLE_DWARF_SECTION(DebugCUIndex, ".debug_cu_index", "debug-cu-index") Index: include/llvm/DebugInfo/DIContext.h =================================================================== --- include/llvm/DebugInfo/DIContext.h +++ include/llvm/DebugInfo/DIContext.h @@ -154,6 +154,8 @@ struct DIDumpOptions { unsigned DumpType = DIDT_All; unsigned RecurseDepth = -1U; + uint16_t Version = 0; // DWARF version to assume when extracting. + uint8_t AddrSize = 4; // Address byte size to assume when extracting. bool ShowAddresses = true; bool ShowChildren = false; bool ShowParents = false; Index: include/llvm/DebugInfo/DWARF/DWARFContext.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFContext.h +++ include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -323,6 +323,14 @@ /// have initialized the relevant target descriptions. Error loadRegisterInfo(const object::ObjectFile &Obj); + /// Get address size from CUs. + /// TODO: refactor compile_units() to make this const. + uint8_t getCUAddrSize(); + + /// Get DWARF version from CUs. + /// TODO: refactor compile_units() to make this const. + uint16_t getCUDWARFVersion(); + private: /// Return the compile unit which contains instruction with provided /// address. Index: include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h +++ include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h @@ -51,6 +51,8 @@ /// reflect the absolute address of this pointer. Optional getEncodedPointer(uint32_t *Offset, uint8_t Encoding, uint64_t AbsPosOffset = 0) const; + + size_t size() const { return Section == nullptr ? 0 : Section->Data.size(); } }; } // end namespace llvm Index: include/llvm/DebugInfo/DWARF/DWARFDebugAddr.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/DWARF/DWARFDebugAddr.h @@ -0,0 +1,95 @@ +//===- DWARFDebugAddr.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_DWARFDEBUGADDR_H +#define LLVM_DEBUGINFO_DWARFDEBUGADDR_H + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include +#include +#include + +namespace llvm { + +class Error; +class raw_ostream; +// +/// A class representing an address table as specified in DWARF v5. +/// The table consists of a header followed by an array of address values from +/// .debug_addr section. +class DWARFDebugAddrTable { +public: + struct Header { + /// The total length of the entries for this table, not including the length + /// field itself. + uint32_t Length = 0; + /// The DWARF version number. + uint16_t Version = 5; + /// The size in bytes of an address on the target architecture. For + /// segmented addressing, this is the size of the offset portion of the + /// address. + uint8_t AddrSize; + /// The size in bytes of a segment selector on the target architecture. + /// If the target system uses a flat address space, this value is 0. + uint8_t SegSize = 0; + }; + +private: + dwarf::DwarfFormat Format; + uint32_t HeaderOffset; + Header HeaderData; + uint32_t DataSize = 0; + uint32_t AddrCount = 0; + std::vector Addrs; + +public: + void clear(); + /// Extract an entire table, including all addrs. + Error extract(DWARFDataExtractor Data, uint32_t *OffsetPtr, + uint16_t Version, uint8_t AddrSize); + /// Look up an address based on a given index. + uint32_t getHeaderOffset() const { return HeaderOffset; } + uint8_t getAddrSize() const { return HeaderData.AddrSize; } + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + /// Return the address based on a given index. + Optional getAddrEntry(uint32_t Index) const { + if (Index < Addrs.size()) + return Addrs[Index]; + return None; + } + /// Return the size of the table header including the length + /// but not including the addrs. + uint8_t getHeaderSize() const { + switch (Format) { + case dwarf::DwarfFormat::DWARF32: + return 8; // 4 + 2 + 1 + 1 + case dwarf::DwarfFormat::DWARF64: + return 16; // 12 + 2 + 1 + 1 + } + llvm_unreachable("Invalid DWARF format (expected DWARF32 or DWARF64)"); + } + + /// Returns the length of this table, including the length field, or 0 if the + /// length has not been determined (e.g. because the table has not yet been + /// parsed, or there was a problem in parsing). + uint32_t getLength() const; + + /// Verify that the given length is valid for this table. + bool isLengthValid(uint32_t Length) const { return Length != 0; } + + /// Returns the length of array of addrs. + uint32_t getDataSize() const; +}; + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARFDEBUGADDR_H + Index: include/llvm/Support/Error.h =================================================================== --- include/llvm/Support/Error.h +++ include/llvm/Support/Error.h @@ -1121,6 +1121,15 @@ std::error_code EC; }; +/// Create a StringError instance with formatted message. +template +Error createStringError(char const *Fmt, const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error(Stream.str(), inconvertibleErrorCode()); +} + /// Helper for check-and-exit error handling. /// /// For tool use only. NOT FOR USE IN LIBRARY CODE. Index: lib/DebugInfo/DWARF/CMakeLists.txt =================================================================== --- lib/DebugInfo/DWARF/CMakeLists.txt +++ lib/DebugInfo/DWARF/CMakeLists.txt @@ -14,6 +14,7 @@ DWARFDebugLoc.cpp DWARFDebugMacro.cpp DWARFDebugPubTable.cpp + DWARFDebugAddr.cpp DWARFDebugRangeList.cpp DWARFDebugRnglists.cpp DWARFDie.cpp Index: lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFContext.cpp +++ lib/DebugInfo/DWARF/DWARFContext.cpp @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" @@ -249,6 +250,28 @@ } } +// Dump the .debug_addr section (DWARF v5). +static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, + DIDumpOptions DumpOpts) { + uint32_t Offset = 0; + while (AddrData.isValidOffset(Offset)) { + DWARFDebugAddrTable AddrTable; + uint32_t TableOffset = Offset; + if (Error Err = AddrTable.extract(AddrData, &Offset, DumpOpts.Version, + DumpOpts.AddrSize)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + uint64_t Length = AddrTable.getLength(); + // Keep going after an error, if we can, assuming that the length field + // could be read. If it couldn't, stop reading the section. + if (!AddrTable.isLengthValid(Length)) + break; + Offset = TableOffset + Length; + } else { + AddrTable.dump(OS, DumpOpts); + } + } +} + // Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). static void dumpRnglistsSection(raw_ostream &OS, DWARFDataExtractor &rnglistData, @@ -455,6 +478,15 @@ } } + if (shouldDump(Explicit, ".debug_addr", DIDT_ID_DebugAddr, + DObj->getAddrSection().Data)) { + DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(), + isLittleEndian(), 0); + DumpOpts.Version = getCUDWARFVersion(); + DumpOpts.AddrSize = getCUAddrSize(); + dumpAddrSection(OS, AddrData, DumpOpts); + } + if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, DObj->getRangeSection().Data)) { // In fact, different compile units may have different address byte @@ -462,7 +494,7 @@ // last compile unit (there is no easy and fast way to associate address // range list and the compile unit it describes). // FIXME: savedAddressByteSize seems sketchy. - uint8_t savedAddressByteSize = 0; + uint8_t savedAddressByteSize = getCUAddrSize(); for (const auto &CU : compile_units()) { savedAddressByteSize = CU->getAddressByteSize(); break; @@ -1584,3 +1616,26 @@ RegInfo.reset(TheTarget->createMCRegInfo(TT.str())); return Error::success(); } + +uint8_t DWARFContext::getCUAddrSize() { + // In fact, different compile units may have different address byte + // sizes, but for simplicity we just use the address byte size of the + // last compile unit (there is no easy and fast way to associate address + // range list and the compile unit it describes). + // FIXME: savedAddressByteSize seems sketchy. + uint8_t addr = 0; + for (const auto &CU : compile_units()) { + addr = CU->getAddressByteSize(); + break; + } + return addr; +} + +uint16_t DWARFContext::getCUDWARFVersion() { + uint16_t ver = 0; + for (const auto &CU : compile_units()) { + ver = CU->getVersion(); + break; + } + return ver; +} Index: lib/DebugInfo/DWARF/DWARFDebugAddr.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/DWARF/DWARFDebugAddr.cpp @@ -0,0 +1,137 @@ + +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void DWARFDebugAddrTable::clear() { + HeaderData = {}; + Addrs.clear(); +} + +Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data, + uint32_t *OffsetPtr, + uint16_t Version, + uint8_t AddrSize) { + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createStringError("section is not large enough to contain a " + ".debug_addr table length at offset 0x%" PRIx32 "\n", + *OffsetPtr); + uint16_t SuggestedVersion; + if (Version == 0) { + errs() << "WARNING: CU version is not defined, assuming version 5\n"; + SuggestedVersion = 5; + } + else { + SuggestedVersion = Version; + } + // TODO: Add support for DWARF64. + Format = dwarf::DwarfFormat::DWARF32; + if (SuggestedVersion >= 5) { + HeaderData.Length = Data.getU32(OffsetPtr); + if (HeaderData.Length == 0xffffffffu) + return createStringError( + "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx32 "\n", + HeaderOffset); + if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) + return createStringError(".debug_addr table at offset 0x%" PRIx32 + " has too small length (0x%" PRIx32 + ") to contain a complete header\n", + HeaderOffset, getLength()); + uint32_t End = HeaderOffset + getLength(); + if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) + return createStringError( + "section is not large enough to contain a .debug_addr table " + "of length 0x%" PRIx32 " at offset 0x%" PRIx32 "\n", + getLength(), HeaderOffset); + + HeaderData.Version = Data.getU16(OffsetPtr); + HeaderData.AddrSize = Data.getU8(OffsetPtr); + HeaderData.SegSize = Data.getU8(OffsetPtr); + DataSize = getDataSize(); + } + else { + HeaderData.Version = SuggestedVersion; + HeaderData.AddrSize = AddrSize; + // TODO: Support for non-zero SegSize. + HeaderData.SegSize = 0; + DataSize = Data.size(); + } + + // Perform basic validation of the remaining header fields. + if (HeaderData.Version != SuggestedVersion) + return createStringError(".debug_addr table version %" PRIu16 + " in table at offset 0x% " PRIx32 + " is different from CU version 0x" PRIu16 "\n", + HeaderData.Version, HeaderOffset, SuggestedVersion); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createStringError(".debug_addr table at offset 0x%" PRIx32 + " has unsupported address size %hhu\n", + HeaderOffset, HeaderData.AddrSize); + if (HeaderData.AddrSize != AddrSize && AddrSize != 0) + return createStringError(".debug_addr table at offset 0x%" PRIx32 + " has address size %hhu" + " which is different from CU address size %hhu\n", + HeaderOffset, HeaderData.AddrSize, AddrSize); + + // TODO: add support for non-zero SegSize + if (HeaderData.SegSize != 0) + return createStringError(".debug_addr table at offset 0x%" PRIx32 + " has unsupported segment selector size %" PRIu8 "\n", + HeaderOffset, HeaderData.SegSize); + if (DataSize % HeaderData.AddrSize != 0) + return createStringError(".debug_addr table at offset 0x%" PRIx32 + " contains data of size %" PRIu32 + " which is not a multiple of addr size %" PRIu8 "\n", + HeaderOffset, DataSize, HeaderData.AddrSize); + Data.setAddressSize(HeaderData.AddrSize); + AddrCount = DataSize / HeaderData.AddrSize; + for (uint32_t I = 0; I < AddrCount; ++I) + if (HeaderData.AddrSize == 4) + Addrs.push_back(Data.getU32(OffsetPtr)); + else + Addrs.push_back(Data.getU64(OffsetPtr)); + return Error::success(); +} + +void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (DumpOpts.Verbose) + OS << format("0x%8.8" PRIx32 ": ", HeaderOffset); + OS << format("Addr Section params: length = 0x%8.8" PRIx32 + ", version = 0x%4.4" PRIx16 ", " + "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n", + HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, + HeaderData.SegSize); + + if (AddrCount > 0) { + OS << "Addrs: ["; + for (const auto &Addr : Addrs) { + OS << format("\n0x%8.8" PRIx32, Addr); // TODO: 64bit addrs + if (DumpOpts.Verbose) + OS << format(" => 0x%8.8" PRIx32, + Addr + HeaderOffset + sizeof(HeaderData)); + } + OS << "\n]\n"; + } +} + +uint32_t DWARFDebugAddrTable::getLength() const { + if (HeaderData.Length == 0) + return 0; + // TODO: DWARF64 support. + return HeaderData.Length + sizeof(uint32_t); +} + +uint32_t DWARFDebugAddrTable::getDataSize() const { + if (DataSize != 0) + return DataSize; + if (getLength() == 0) + return 0; + return getLength() - getHeaderSize(); +} Index: test/tools/llvm-dwarfdump/X86/debug_addr_X86.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/debug_addr_X86.s @@ -0,0 +1,23 @@ +# RUN: llvm-mc %s -filetype obj -triple i386-pc-linux -o %t.o +# RUN: llvm-dwarfdump --debug-addr %t.o | FileCheck %s + +# CHECK: .debug_addr contents +# CHECK-NEXT: length = 0x0000000c, version = 0x0005, addr_size = 0x04, seg_size = 0x00 +# CHECK-NEXT: Addrs: [ +# CHECK-NEXT: 0x00000000 +# CHECK-NEXT: 0x00000002 + + .text +.Lfunc_begin0: + nop + nop +.Lfunc_begin1: + + .section .debug_addr,"",@progbits +.Ldebug_addr0: + .long 12 # unit_length = .short + .byte + .byte + .long + .long + .short 5 # version + .byte 4 # address_size + .byte 0 # segment_selector_size + .long .Lfunc_begin0 + .long .Lfunc_begin1 Index: test/tools/llvm-dwarfdump/X86/debug_addr_dwarf4.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/debug_addr_dwarf4.s @@ -0,0 +1,59 @@ +# RUN: llvm-mc %s -filetype obj -triple i386-pc-linux -o %t.o +# RUN: llvm-dwarfdump --debug-addr %t.o | FileCheck %s + +# CHECK: .debug_addr contents +# CHECK-NEXT: length = 0x00000000, version = 0x0004, addr_size = 0x04, seg_size = 0x00 +# CHECK-NEXT: Addrs: [ +# CHECK-NEXT: 0x00000000 +# CHECK-NEXT: 0x00000002 + + .text +.Lfunc_begin0: + nop + nop +.Lfunc_begin1: +.Lfunc_end1: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "foo.dwo" # string offset=0 +.Lskel_string1: + .asciz "/tmp" # string offset=13 + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .ascii "\260B" # DW_AT_GNU_dwo_name + .byte 14 # DW_FORM_strp + .byte 27 # DW_AT_comp_dir + .byte 14 # DW_FORM_strp + .ascii "\261B" # DW_AT_GNU_dwo_id + .byte 7 # DW_FORM_data8 + .ascii "\263B" # DW_AT_GNU_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long 40 # Length of Unit + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 4 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x21 DW_TAG_compile_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lskel_string0 # DW_AT_GNU_dwo_name + .long .Lskel_string1 # DW_AT_comp_dir + .quad 1192015794693568315 # DW_AT_GNU_dwo_id + .long .debug_addr # DW_AT_GNU_addr_base + .long .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc + .section .debug_addr,"",@progbits + .long .Lfunc_begin0 + .long .Lfunc_begin1 +.Lline_table_start0: Index: test/tools/llvm-dwarfdump/X86/debug_addr_empty.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/debug_addr_empty.s @@ -0,0 +1,7 @@ +# RUN: llvm-mc %s -filetype obj -triple i386-pc-linux -o - | \ +# RUN: llvm-dwarfdump -debug-addr - | FileCheck %s +# CHECK: .debug_addr contents: +# CHECK-NOT: Addr +# CHECK-NOT: error: + +.section .debug_addr,"",@progbits Index: test/tools/llvm-dwarfdump/X86/debug_addr_invalid.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/debug_addr_invalid.s @@ -0,0 +1,40 @@ +# RUN: llvm-mc %s -filetype obj -triple i386-pc-linux -o %t.o +# RUN: llvm-dwarfdump --debug-addr %t.o 2>&1 | FileCheck %s + +# CHECK: unsupported address size 3 +# CHECK: unsupported segment selector size 1 +# CHECK: is not a multiple of addr size 4 + + .text +.Lfunc_begin0: +.Lfunc_begin1: +# invalid addr size + .section .debug_addr,"",@progbits +.Ldebug_addr1: + .long 12 # unit_length = .short + .byte + .byte + .long + .long + .short 5 # version + .byte 3 # address_size + .byte 0 # segment_selector_size + .long .Lfunc_begin0 + .long .Lfunc_begin1 + +# invalid segment selector size + .section .debug_addr,"",@progbits +.Ldebug_addr2: + .long 12 # unit_length = .short + .byte + .byte + .long + .long + .short 5 # version + .byte 4 # address_size + .byte 1 # segment_selector_size + .long .Lfunc_begin0 + .long .Lfunc_begin1 + +# invalid length + .section .debug_addr,"",@progbits +.Ldebug_addr3: + .long 11 # unit_length = .short + .byte + .byte + .long + .long + .short 5 # version + .byte 4 # address_size + .byte 0 # segment_selector_size + .long .Lfunc_begin0 + .long .Lfunc_begin1 +