1//===- DWARFDebugAddr.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h"
10#include "llvm/BinaryFormat/Dwarf.h"
11#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
12
13using namespace llvm;
14
15Error DWARFDebugAddrTable::extractAddresses(const DWARFDataExtractor &Data,
16                                            uint64_t *OffsetPtr,
17                                            uint64_t EndOffset) {
18  assert(EndOffset >= *OffsetPtr);
19  uint64_t DataSize = EndOffset - *OffsetPtr;
20  assert(Data.isValidOffsetForDataOfSize(*OffsetPtr, DataSize));
21  if (AddrSize != 4 && AddrSize != 8)
22    return createStringError(errc::not_supported,
23                             "address table at offset 0x%" PRIx64
24                             " has unsupported address size %" PRIu8
25                             " (4 and 8 are supported)",
26                             Offset, AddrSize);
27  if (DataSize % AddrSize != 0) {
28    invalidateLength();
29    return createStringError(errc::invalid_argument,
30                             "address table at offset 0x%" PRIx64
31                             " contains data of size 0x%" PRIx64
32                             " which is not a multiple of addr size %" PRIu8,
33                             Offset, DataSize, AddrSize);
34  }
35  Addrs.clear();
36  size_t Count = DataSize / AddrSize;
37  Addrs.reserve(Count);
38  while (Count--)
39    Addrs.push_back(Data.getRelocatedValue(AddrSize, OffsetPtr));
40  return Error::success();
41}
42
43Error DWARFDebugAddrTable::extractV5(const DWARFDataExtractor &Data,
44                                     uint64_t *OffsetPtr, uint8_t CUAddrSize,
45                                     std::function<void(Error)> WarnCallback) {
46  Offset = *OffsetPtr;
47  llvm::Error Err = Error::success();
48  std::tie(Length, Format) = Data.getInitialLength(OffsetPtr, &Err);
49  if (Err) {
50    invalidateLength();
51    return createStringError(errc::invalid_argument,
52                             "parsing address table at offset 0x%" PRIx64
53                             ": %s",
54                             Offset, toString(std::move(Err)).c_str());
55  }
56
57  if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, Length)) {
58    uint64_t DiagnosticLength = Length;
59    invalidateLength();
60    return createStringError(
61        errc::invalid_argument,
62        "section is not large enough to contain an address table "
63        "at offset 0x%" PRIx64 " with a unit_length value of 0x%" PRIx64,
64        Offset, DiagnosticLength);
65  }
66  uint64_t EndOffset = *OffsetPtr + Length;
67  // Ensure that we can read the remaining header fields.
68  if (Length < 4) {
69    uint64_t DiagnosticLength = Length;
70    invalidateLength();
71    return createStringError(
72        errc::invalid_argument,
73        "address table at offset 0x%" PRIx64
74        " has a unit_length value of 0x%" PRIx64
75        ", which is too small to contain a complete header",
76        Offset, DiagnosticLength);
77  }
78
79  Version = Data.getU16(OffsetPtr);
80  AddrSize = Data.getU8(OffsetPtr);
81  SegSize = Data.getU8(OffsetPtr);
82
83  // Perform a basic validation of the header fields.
84  if (Version != 5)
85    return createStringError(errc::not_supported,
86                             "address table at offset 0x%" PRIx64
87                             " has unsupported version %" PRIu16,
88                             Offset, Version);
89  // TODO: add support for non-zero segment selector size.
90  if (SegSize != 0)
91    return createStringError(errc::not_supported,
92                             "address table at offset 0x%" PRIx64
93                             " has unsupported segment selector size %" PRIu8,
94                             Offset, SegSize);
95
96  if (Error Err = extractAddresses(Data, OffsetPtr, EndOffset))
97    return Err;
98  if (CUAddrSize && AddrSize != CUAddrSize) {
99    WarnCallback(createStringError(
100        errc::invalid_argument,
101        "address table at offset 0x%" PRIx64 " has address size %" PRIu8
102        " which is different from CU address size %" PRIu8,
103        Offset, AddrSize, CUAddrSize));
104  }
105  return Error::success();
106}
107
108Error DWARFDebugAddrTable::extractPreStandard(const DWARFDataExtractor &Data,
109                                              uint64_t *OffsetPtr,
110                                              uint16_t CUVersion,
111                                              uint8_t CUAddrSize) {
112  assert(CUVersion > 0 && CUVersion < 5);
113
114  Offset = *OffsetPtr;
115  Length = 0;
116  Version = CUVersion;
117  AddrSize = CUAddrSize;
118  SegSize = 0;
119
120  return extractAddresses(Data, OffsetPtr, Data.size());
121}
122
123Error DWARFDebugAddrTable::extract(const DWARFDataExtractor &Data,
124                                   uint64_t *OffsetPtr,
125                                   uint16_t CUVersion,
126                                   uint8_t CUAddrSize,
127                                   std::function<void(Error)> WarnCallback) {
128  if (CUVersion > 0 && CUVersion < 5)
129    return extractPreStandard(Data, OffsetPtr, CUVersion, CUAddrSize);
130  if (CUVersion == 0)
131    WarnCallback(createStringError(errc::invalid_argument,
132                                   "DWARF version is not defined in CU,"
133                                   " assuming version 5"));
134  return extractV5(Data, OffsetPtr, CUAddrSize, WarnCallback);
135}
136
137void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
138  if (DumpOpts.Verbose)
139    OS << format("0x%8.8" PRIx64 ": ", Offset);
140  if (Length) {
141    int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Format);
142    OS << "Address table header: "
143       << format("length = 0x%0*" PRIx64, OffsetDumpWidth, Length)
144       << ", format = " << dwarf::FormatString(Format)
145       << format(", version = 0x%4.4" PRIx16, Version)
146       << format(", addr_size = 0x%2.2" PRIx8, AddrSize)
147       << format(", seg_size = 0x%2.2" PRIx8, SegSize) << "\n";
148  }
149
150  if (Addrs.size() > 0) {
151    const char *AddrFmt =
152        (AddrSize == 4) ? "0x%8.8" PRIx64 "\n" : "0x%16.16" PRIx64 "\n";
153    OS << "Addrs: [\n";
154    for (uint64_t Addr : Addrs)
155      OS << format(AddrFmt, Addr);
156    OS << "]\n";
157  }
158}
159
160Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const {
161  if (Index < Addrs.size())
162    return Addrs[Index];
163  return createStringError(errc::invalid_argument,
164                           "Index %" PRIu32 " is out of range of the "
165                           "address table at offset 0x%" PRIx64,
166                           Index, Offset);
167}
168
169Optional<uint64_t> DWARFDebugAddrTable::getFullLength() const {
170  if (Length == 0)
171    return None;
172  return Length + dwarf::getUnitLengthFieldByteSize(Format);
173}
174
175