1337137Sdim//===- DWARFDebugAddr.cpp -------------------------------------------------===//
2337137Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6337137Sdim//
7337137Sdim//===----------------------------------------------------------------------===//
8337137Sdim
9337137Sdim#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h"
10337137Sdim#include "llvm/BinaryFormat/Dwarf.h"
11337137Sdim#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
12337137Sdim
13337137Sdimusing namespace llvm;
14337137Sdim
15337137Sdimvoid DWARFDebugAddrTable::clear() {
16337137Sdim  HeaderData = {};
17337137Sdim  Addrs.clear();
18337137Sdim  invalidateLength();
19337137Sdim}
20337137Sdim
21337137SdimError DWARFDebugAddrTable::extract(DWARFDataExtractor Data,
22360784Sdim                                   uint64_t *OffsetPtr,
23337137Sdim                                   uint16_t Version,
24337137Sdim                                   uint8_t AddrSize,
25337137Sdim                                   std::function<void(Error)> WarnCallback) {
26337137Sdim  clear();
27337137Sdim  HeaderOffset = *OffsetPtr;
28337137Sdim  // Read and verify the length field.
29337137Sdim  if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t)))
30337137Sdim    return createStringError(errc::invalid_argument,
31337137Sdim                       "section is not large enough to contain a "
32337137Sdim                       ".debug_addr table length at offset 0x%"
33360784Sdim                       PRIx64, *OffsetPtr);
34337137Sdim  uint16_t UnitVersion;
35337137Sdim  if (Version == 0) {
36337137Sdim    WarnCallback(createStringError(errc::invalid_argument,
37337137Sdim                       "DWARF version is not defined in CU,"
38337137Sdim                       " assuming version 5"));
39337137Sdim    UnitVersion = 5;
40337137Sdim  } else {
41337137Sdim    UnitVersion = Version;
42337137Sdim  }
43337137Sdim  // TODO: Add support for DWARF64.
44337137Sdim  Format = dwarf::DwarfFormat::DWARF32;
45337137Sdim  if (UnitVersion >= 5) {
46337137Sdim    HeaderData.Length = Data.getU32(OffsetPtr);
47360784Sdim    if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) {
48337137Sdim      invalidateLength();
49337137Sdim      return createStringError(errc::not_supported,
50360784Sdim          "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64,
51337137Sdim          HeaderOffset);
52337137Sdim    }
53337137Sdim    if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) {
54337137Sdim      uint32_t TmpLength = getLength();
55337137Sdim      invalidateLength();
56337137Sdim      return createStringError(errc::invalid_argument,
57360784Sdim                         ".debug_addr table at offset 0x%" PRIx64
58337137Sdim                         " has too small length (0x%" PRIx32
59337137Sdim                         ") to contain a complete header",
60337137Sdim                         HeaderOffset, TmpLength);
61337137Sdim    }
62360784Sdim    uint64_t End = HeaderOffset + getLength();
63337137Sdim    if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) {
64337137Sdim      uint32_t TmpLength = getLength();
65337137Sdim      invalidateLength();
66337137Sdim      return createStringError(errc::invalid_argument,
67337137Sdim          "section is not large enough to contain a .debug_addr table "
68360784Sdim          "of length 0x%" PRIx32 " at offset 0x%" PRIx64,
69337137Sdim          TmpLength, HeaderOffset);
70337137Sdim    }
71337137Sdim
72337137Sdim    HeaderData.Version = Data.getU16(OffsetPtr);
73337137Sdim    HeaderData.AddrSize = Data.getU8(OffsetPtr);
74337137Sdim    HeaderData.SegSize = Data.getU8(OffsetPtr);
75337137Sdim    DataSize = getDataSize();
76337137Sdim  } else {
77337137Sdim    HeaderData.Version = UnitVersion;
78337137Sdim    HeaderData.AddrSize = AddrSize;
79337137Sdim    // TODO: Support for non-zero SegSize.
80337137Sdim    HeaderData.SegSize = 0;
81337137Sdim    DataSize = Data.size();
82337137Sdim  }
83337137Sdim
84337137Sdim  // Perform basic validation of the remaining header fields.
85337137Sdim
86337137Sdim  // We support DWARF version 5 for now as well as pre-DWARF5
87337137Sdim  // implementations of .debug_addr table, which doesn't contain a header
88337137Sdim  // and consists only of a series of addresses.
89337137Sdim  if (HeaderData.Version > 5) {
90337137Sdim    return createStringError(errc::not_supported, "version %" PRIu16
91360784Sdim        " of .debug_addr section at offset 0x%" PRIx64 " is not supported",
92337137Sdim        HeaderData.Version, HeaderOffset);
93337137Sdim  }
94337137Sdim  // FIXME: For now we just treat version mismatch as an error,
95337137Sdim  // however the correct way to associate a .debug_addr table
96337137Sdim  // with a .debug_info table is to look at the DW_AT_addr_base
97337137Sdim  // attribute in the info table.
98337137Sdim  if (HeaderData.Version != UnitVersion)
99337137Sdim    return createStringError(errc::invalid_argument,
100360784Sdim                       ".debug_addr table at offset 0x%" PRIx64
101337137Sdim                       " has version %" PRIu16
102337137Sdim                       " which is different from the version suggested"
103337137Sdim                       " by the DWARF unit header: %" PRIu16,
104337137Sdim                       HeaderOffset, HeaderData.Version, UnitVersion);
105337137Sdim  if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)
106337137Sdim    return createStringError(errc::not_supported,
107360784Sdim                       ".debug_addr table at offset 0x%" PRIx64
108337137Sdim                       " has unsupported address size %" PRIu8,
109337137Sdim                       HeaderOffset, HeaderData.AddrSize);
110337137Sdim  if (HeaderData.AddrSize != AddrSize && AddrSize != 0)
111337137Sdim    return createStringError(errc::invalid_argument,
112360784Sdim                       ".debug_addr table at offset 0x%" PRIx64
113337137Sdim                       " has address size %" PRIu8
114337137Sdim                       " which is different from CU address size %" PRIu8,
115337137Sdim                       HeaderOffset, HeaderData.AddrSize, AddrSize);
116337137Sdim
117337137Sdim  // TODO: add support for non-zero segment selector size.
118337137Sdim  if (HeaderData.SegSize != 0)
119337137Sdim    return createStringError(errc::not_supported,
120360784Sdim                       ".debug_addr table at offset 0x%" PRIx64
121337137Sdim                       " has unsupported segment selector size %" PRIu8,
122337137Sdim                       HeaderOffset, HeaderData.SegSize);
123337137Sdim  if (DataSize % HeaderData.AddrSize != 0) {
124337137Sdim    invalidateLength();
125337137Sdim    return createStringError(errc::invalid_argument,
126360784Sdim                       ".debug_addr table at offset 0x%" PRIx64
127337137Sdim                       " contains data of size %" PRIu32
128337137Sdim                       " which is not a multiple of addr size %" PRIu8,
129337137Sdim                       HeaderOffset, DataSize, HeaderData.AddrSize);
130337137Sdim  }
131337137Sdim  Data.setAddressSize(HeaderData.AddrSize);
132337137Sdim  uint32_t AddrCount = DataSize / HeaderData.AddrSize;
133337137Sdim  for (uint32_t I = 0; I < AddrCount; ++I)
134337137Sdim    if (HeaderData.AddrSize == 4)
135337137Sdim      Addrs.push_back(Data.getU32(OffsetPtr));
136337137Sdim    else
137337137Sdim      Addrs.push_back(Data.getU64(OffsetPtr));
138337137Sdim  return Error::success();
139337137Sdim}
140337137Sdim
141337137Sdimvoid DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
142337137Sdim  if (DumpOpts.Verbose)
143337137Sdim    OS << format("0x%8.8" PRIx32 ": ", HeaderOffset);
144337137Sdim  OS << format("Addr Section: length = 0x%8.8" PRIx32
145337137Sdim               ", version = 0x%4.4" PRIx16 ", "
146337137Sdim               "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n",
147337137Sdim               HeaderData.Length, HeaderData.Version, HeaderData.AddrSize,
148337137Sdim               HeaderData.SegSize);
149337137Sdim
150337137Sdim  if (Addrs.size() > 0) {
151353358Sdim    const char *AddrFmt = (HeaderData.AddrSize == 4) ? "0x%8.8" PRIx64 "\n"
152353358Sdim                                                     : "0x%16.16" PRIx64 "\n";
153353358Sdim    OS << "Addrs: [\n";
154353358Sdim    for (uint64_t Addr : Addrs)
155353358Sdim      OS << format(AddrFmt, Addr);
156353358Sdim    OS << "]\n";
157337137Sdim  }
158337137Sdim}
159337137Sdim
160337137SdimExpected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const {
161337137Sdim  if (Index < Addrs.size())
162337137Sdim    return Addrs[Index];
163337137Sdim  return createStringError(errc::invalid_argument,
164337137Sdim                           "Index %" PRIu32 " is out of range of the "
165360784Sdim                           ".debug_addr table at offset 0x%" PRIx64,
166337137Sdim                           Index, HeaderOffset);
167337137Sdim}
168337137Sdim
169337137Sdimuint32_t DWARFDebugAddrTable::getLength() const {
170337137Sdim  if (HeaderData.Length == 0)
171337137Sdim    return 0;
172337137Sdim  // TODO: DWARF64 support.
173337137Sdim  return HeaderData.Length + sizeof(uint32_t);
174337137Sdim}
175337137Sdim
176337137Sdimuint32_t DWARFDebugAddrTable::getDataSize() const {
177337137Sdim  if (DataSize != 0)
178337137Sdim    return DataSize;
179337137Sdim  if (getLength() == 0)
180337137Sdim    return 0;
181337137Sdim  return getLength() - getHeaderSize();
182337137Sdim}
183