[DWARF] Support for .debug_addr (consumer)

  This patch implements basic support for parsing
  and dumping DWARFv5 .debug_addr section.

llvm-svn: 338447
diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
index d88a027..b4770e5 100644
--- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
@@ -6,6 +6,7 @@
   DWARFContext.cpp
   DWARFDataExtractor.cpp
   DWARFDebugAbbrev.cpp
+  DWARFDebugAddr.cpp
   DWARFDebugArangeSet.cpp
   DWARFDebugAranges.cpp
   DWARFDebugFrame.cpp
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index f41ca0d..9d2554f 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/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,36 @@
   }
 }
 
+// Dump the .debug_addr section.
+static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData,
+                            DIDumpOptions DumpOpts, uint16_t Version,
+                            uint8_t AddrSize) {
+  // TODO: Make this more general: add callback types to Error.h, create
+  // implementation and make all DWARF classes use them.
+  static auto WarnCallback = [](Error Warn) {
+    handleAllErrors(std::move(Warn), [](ErrorInfoBase &Info) {
+      WithColor::warning() << Info.message() << '\n';
+    });
+  };
+  uint32_t Offset = 0;
+  while (AddrData.isValidOffset(Offset)) {
+    DWARFDebugAddrTable AddrTable;
+    uint32_t TableOffset = Offset;
+    if (Error Err = AddrTable.extract(AddrData, &Offset, Version,
+                                      AddrSize, WarnCallback)) {
+      WithColor::error() << toString(std::move(Err)) << '\n';
+      // 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.hasValidLength())
+        break;
+      uint64_t Length = AddrTable.getLength();
+      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,18 +486,16 @@
     }
   }
 
+  if (shouldDump(Explicit, ".debug_addr", DIDT_ID_DebugAddr,
+                 DObj->getAddrSection().Data)) {
+    DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(),
+                                   isLittleEndian(), 0);
+    dumpAddrSection(OS, AddrData, DumpOpts, getMaxVersion(), getCUAddrSize());
+  }
+
   if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges,
                  DObj->getRangeSection().Data)) {
-    // 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 savedAddressByteSize = 0;
-    for (const auto &CU : compile_units()) {
-      savedAddressByteSize = CU->getAddressByteSize();
-      break;
-    }
+    uint8_t savedAddressByteSize = getCUAddrSize();
     DWARFDataExtractor rangesData(*DObj, DObj->getRangeSection(),
                                   isLittleEndian(), savedAddressByteSize);
     uint32_t offset = 0;
@@ -1584,3 +1613,17 @@
   RegInfo.reset(TheTarget->createMCRegInfo(TT.str()));
   return Error::success();
 }
+
+uint8_t DWARFContext::getCUAddrSize() {
+  // In theory, different compile units may have different address byte
+  // sizes, but for simplicity we just use the address byte size of the
+  // last compile unit. In practice the address size field is repeated across
+  // various DWARF headers (at least in version 5) to make it easier to dump
+  // them independently, not to enable varying the address size.
+  uint8_t Addr = 0;
+  for (const auto &CU : compile_units()) {
+    Addr = CU->getAddressByteSize();
+    break;
+  }
+  return Addr;
+}
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp
new file mode 100644
index 0000000..7085ca0
--- /dev/null
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp
@@ -0,0 +1,198 @@
+//===- 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"
+
+using namespace llvm;
+
+void DWARFDebugAddrTable::clear() {
+  HeaderData = {};
+  Addrs.clear();
+  invalidateLength();
+}
+
+Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data,
+                                   uint32_t *OffsetPtr,
+                                   uint16_t Version,
+                                   uint8_t AddrSize,
+                                   std::function<void(Error)> WarnCallback) {
+  clear();
+  HeaderOffset = *OffsetPtr;
+  // Read and verify the length field.
+  if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t)))
+    return createStringError(errc::invalid_argument,
+                       "section is not large enough to contain a "
+                       ".debug_addr table length at offset 0x%"
+                       PRIx32, *OffsetPtr);
+  uint16_t UnitVersion;
+  if (Version == 0) {
+    WarnCallback(createStringError(errc::invalid_argument,
+                       "DWARF version is not defined in CU,"
+                       " assuming version 5"));
+    UnitVersion = 5;
+  } else {
+    UnitVersion = Version;
+  }
+  // TODO: Add support for DWARF64.
+  Format = dwarf::DwarfFormat::DWARF32;
+  if (UnitVersion >= 5) {
+    HeaderData.Length = Data.getU32(OffsetPtr);
+    if (HeaderData.Length == 0xffffffffu) {
+      invalidateLength();
+      return createStringError(errc::not_supported,
+          "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx32,
+          HeaderOffset);
+    }
+    if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) {
+      uint32_t TmpLength = getLength();
+      invalidateLength();
+      return createStringError(errc::invalid_argument,
+                         ".debug_addr table at offset 0x%" PRIx32
+                         " has too small length (0x%" PRIx32
+                         ") to contain a complete header",
+                         HeaderOffset, TmpLength);
+    }
+    uint32_t End = HeaderOffset + getLength();
+    if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) {
+      uint32_t TmpLength = getLength();
+      invalidateLength();
+      return createStringError(errc::invalid_argument,
+          "section is not large enough to contain a .debug_addr table "
+          "of length 0x%" PRIx32 " at offset 0x%" PRIx32,
+          TmpLength, HeaderOffset);
+    }
+
+    HeaderData.Version = Data.getU16(OffsetPtr);
+    HeaderData.AddrSize = Data.getU8(OffsetPtr);
+    HeaderData.SegSize = Data.getU8(OffsetPtr);
+    DataSize = getDataSize();
+  } else {
+    HeaderData.Version = UnitVersion;
+    HeaderData.AddrSize = AddrSize;
+    // TODO: Support for non-zero SegSize.
+    HeaderData.SegSize = 0;
+    DataSize = Data.size();
+  }
+
+  // Perform basic validation of the remaining header fields.
+
+  // We support DWARF version 5 for now as well as pre-DWARF5
+  // implementations of .debug_addr table, which doesn't contain a header
+  // and consists only of a series of addresses.
+  if (HeaderData.Version > 5) {
+    return createStringError(errc::not_supported, "version %" PRIu16
+        " of .debug_addr section at offset 0x%" PRIx32 " is not supported",
+        HeaderData.Version, HeaderOffset);
+  }
+  // FIXME: For now we just treat version mismatch as an error,
+  // however the correct way to associate a .debug_addr table
+  // with a .debug_info table is to look at the DW_AT_addr_base
+  // attribute in the info table.
+  if (HeaderData.Version != UnitVersion)
+    return createStringError(errc::invalid_argument,
+                       ".debug_addr table at offset 0x%" PRIx32
+                       " has version %" PRIu16
+                       " which is different from the version suggested"
+                       " by the DWARF unit header: %" PRIu16,
+                       HeaderOffset, HeaderData.Version, UnitVersion);
+  if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)
+    return createStringError(errc::not_supported,
+                       ".debug_addr table at offset 0x%" PRIx32
+                       " has unsupported address size %" PRIu8,
+                       HeaderOffset, HeaderData.AddrSize);
+  if (HeaderData.AddrSize != AddrSize && AddrSize != 0)
+    return createStringError(errc::invalid_argument,
+                       ".debug_addr table at offset 0x%" PRIx32
+                       " has address size %" PRIu8
+                       " which is different from CU address size %" PRIu8,
+                       HeaderOffset, HeaderData.AddrSize, AddrSize);
+
+  // TODO: add support for non-zero segment selector size.
+  if (HeaderData.SegSize != 0)
+    return createStringError(errc::not_supported,
+                       ".debug_addr table at offset 0x%" PRIx32
+                       " has unsupported segment selector size %" PRIu8,
+                       HeaderOffset, HeaderData.SegSize);
+  if (DataSize % HeaderData.AddrSize != 0) {
+    invalidateLength();
+    return createStringError(errc::invalid_argument,
+                       ".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);
+  uint32_t 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: 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);
+
+  static const char *Fmt32 = "0x%8.8" PRIx32;
+  static const char *Fmt64 = "0x%16.16" PRIx64;
+  std::string AddrFmt = "\n";
+  std::string AddrFmtVerbose = " => ";
+  if (HeaderData.AddrSize == 4) {
+    AddrFmt.append(Fmt32);
+    AddrFmtVerbose.append(Fmt32);
+  }
+  else {
+    AddrFmt.append(Fmt64);
+    AddrFmtVerbose.append(Fmt64);
+  }
+
+  if (Addrs.size() > 0) {
+    OS << "Addrs: [";
+    for (uint64_t Addr : Addrs) {
+      OS << format(AddrFmt.c_str(), Addr);
+      if (DumpOpts.Verbose)
+        OS << format(AddrFmtVerbose.c_str(),
+                     Addr + HeaderOffset + sizeof(HeaderData));
+    }
+    OS << "\n]\n";
+  }
+}
+
+Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const {
+  if (Index < Addrs.size())
+    return Addrs[Index];
+  return createStringError(errc::invalid_argument,
+                           "Index %" PRIu32 " is out of range of the "
+                           ".debug_addr table at offset 0x%" PRIx32,
+                           Index, HeaderOffset);
+}
+
+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();
+}