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/DWARF/DWARFDebugAddr.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/DWARF/DWARFDebugAddr.h @@ -0,0 +1,93 @@ +//===- 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; + uint32_t AddrCount; + std::vector Addrs; + +public: + void clear(); + /// Extract an entire table, including all addrs. + Error extract(DWARFDataExtractor Data, uint32_t *OffsetPtr); + /// 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. This is dependent on the table format, which is unambiguously + /// derived from parsing the table. + /// length(4 or 12) + version(2) + address__size(1) + segment_selector(1) + 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 length() const; + + /// Returns the length of array of addrs. + uint32_t getDataSize() const; +}; + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARFDEBUGADDR_H + 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 @@ -24,6 +24,7 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" @@ -248,6 +249,27 @@ } } +// 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)) { + llvm::DWARFDebugAddrTable AddrTable; + uint32_t TableOffset = Offset; + if (Error Err = AddrTable.extract(AddrData, &Offset)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + uint64_t Length = AddrTable.length(); + // 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 (Length == 0) + 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, @@ -479,6 +501,13 @@ } } + if (shouldDump(Explicit, ".debug_addr", DIDT_ID_DebugAddr, + DObj->getAddrSection().Data)) { + DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(), + isLittleEndian(), 0); + dumpAddrSection(OS, AddrData, DumpOpts); + } + if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, DObj->getRnglistsSection().Data)) { DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), Index: lib/DebugInfo/DWARF/DWARFDebugAddr.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/DWARF/DWARFDebugAddr.cpp @@ -0,0 +1,125 @@ +//===- DWARFDebugAddr.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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(); +} + +template +static Error createError(char const *Fmt, const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error(Stream.str(), inconvertibleErrorCode()); +} + +Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data, + uint32_t *OffsetPtr) { + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createError("section is not large enough to contain a " + ".debug_addr table length at offset 0x%" PRIx32, + *OffsetPtr); + // TODO: Add support for DWARF64. + HeaderData.Length = Data.getU32(OffsetPtr); + if (HeaderData.Length == 0xffffffffu) + return createError( + "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx32, + HeaderOffset); + Format = dwarf::DwarfFormat::DWARF32; + if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) + return createError(".debug_addr table at offset 0x%" PRIx32 + " has too small length (0x%" PRIx32 + ") to contain a complete header", + HeaderOffset, length()); + uint32_t End = HeaderOffset + length(); + if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) + return createError( + "section is not large enough to contain a .debug_addr table " + "of length 0x%" PRIx32 " at offset 0x%" PRIx32, + length(), HeaderOffset); + + HeaderData.Version = Data.getU16(OffsetPtr); + HeaderData.AddrSize = Data.getU8(OffsetPtr); + HeaderData.SegSize = Data.getU8(OffsetPtr); + + // Perform basic validation of the remaining header fields. + if (HeaderData.Version < 5) + return createError("unrecognised .debug_addr table version %" PRIu16 + " in table at offset 0x%" PRIx32, + HeaderData.Version, HeaderOffset); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createError(".debug_addr table at offset 0x%" PRIx32 + " has unsupported address size %hhu", + HeaderOffset, HeaderData.AddrSize); + // TODO: add support for non-zero SegSize + if (HeaderData.SegSize != 0) + return createError(".debug_addr table at offset 0x%" PRIx32 + " has unsupported segment selector size %" PRIu8, + HeaderOffset, HeaderData.SegSize); + DataSize = getDataSize(); + if (getDataSize() % HeaderData.AddrSize != 0) + return createError(".debug_addr table at offset 0x%" PRIx32 + " contains data of size %" PRIu32 + " which is not a multiple of addr size %" PRIu8, + 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 Header: 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::length() const { + if (HeaderData.Length == 0) + return 0; + // TODO: DWARF64 support. + return HeaderData.Length + sizeof(uint32_t); +} + +uint32_t DWARFDebugAddrTable::getDataSize() const { + if (length() == 0) + return 0; + return length() - 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,75 @@ +# 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: Addr Header: length = 0x0000000c, version = 0x0005, addr_size = 0x04, seg_size = 0x00 +# CHECK-NEXT: Addrs: [ +# CHECK-NEXT: 0x00000000 +# CHECK-NEXT: 0x00000010 + + .text + .file "foo.c" + .globl foo # -- Begin function foo + .p2align 4, 0x90 + .type foo,@function +foo: # @foo +.Lfunc_begin0: + .file 1 "foo.c" + .loc 1 1 0 # foo.c:1:0 + .cfi_sections .debug_frame + .cfi_startproc +# %bb.0: # %entry + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp, %ebp + .cfi_def_cfa_register %ebp +.Ltmp0: + .loc 1 2 3 prologue_end # foo.c:2:3 + popl %ebp + retl +.Ltmp1: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + # -- End function + .globl bar # -- Begin function bar + .p2align 4, 0x90 + .type bar,@function +bar: # @bar +.Lfunc_begin1: + .loc 1 5 0 # ./foo.c:5:0 + .cfi_startproc +# %bb.0: # %entry + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp, %ebp + .cfi_def_cfa_register %ebp + pushl %eax + movl 8(%ebp), %eax +.Ltmp2: + .loc 1 6 10 prologue_end # ./foo.c:6:10 + movl 8(%ebp), %ecx + .loc 1 6 11 is_stmt 0 # ./foo.c:6:11 + shll $1, %ecx + .loc 1 6 3 # ./foo.c:6:3 + movl %eax, -4(%ebp) # 4-byte Spill + movl %ecx, %eax + addl $4, %esp + popl %ebp + retl +.Ltmp3: +.Lfunc_end1: + .size bar, .Lfunc_end1-bar + .cfi_endproc + # -- End function + + .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_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 Header: +# CHECK-NOT: error: + +.section .debug_rnglists,"",@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,106 @@ +# 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: unrecognised .debug_addr table version 4 +# CHECK: unsupported address size 3 +# CHECK: unsupported segment selector size 1 +# CHECK: is not a multiple of addr size 4 + + .text + .file "foo.c" + .globl foo # -- Begin function foo + .p2align 4, 0x90 + .type foo,@function +foo: # @foo +.Lfunc_begin0: + .file 1 "foo.c" + .loc 1 1 0 # foo.c:1:0 + .cfi_sections .debug_frame + .cfi_startproc +# %bb.0: # %entry + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp, %ebp + .cfi_def_cfa_register %ebp +.Ltmp0: + .loc 1 2 3 prologue_end # foo.c:2:3 + popl %ebp + retl +.Ltmp1: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + # -- End function + .globl bar # -- Begin function bar + .p2align 4, 0x90 + .type bar,@function +bar: # @bar +.Lfunc_begin1: + .loc 1 5 0 # ./foo.c:5:0 + .cfi_startproc +# %bb.0: # %entry + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp, %ebp + .cfi_def_cfa_register %ebp + pushl %eax + movl 8(%ebp), %eax +.Ltmp2: + .loc 1 6 10 prologue_end # ./foo.c:6:10 + movl 8(%ebp), %ecx + .loc 1 6 11 is_stmt 0 # ./foo.c:6:11 + shll $1, %ecx + .loc 1 6 3 # ./foo.c:6:3 + movl %eax, -4(%ebp) # 4-byte Spill + movl %ecx, %eax + addl $4, %esp + popl %ebp + retl +.Ltmp3: +.Lfunc_end1: + .size bar, .Lfunc_end1-bar + .cfi_endproc + # -- End function + +# invalid version + .sectioni .debug_addr,"",@progbits +.Ldebug_addr0: + .long 12 # unit_length = .short + .byte + .byte + .long + .long + .short 4 # version + .byte 4 # address_size + .byte 0 # segment_selector_size + .long .Lfunc_begin0 + .long .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 +