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 
13 using namespace llvm;
14 
15 Error 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 
43 Error DWARFDebugAddrTable::extractV5(const DWARFDataExtractor &Data,
44                                      uint64_t *OffsetPtr, uint8_t CUAddrSize,
45                                      std::function<void(Error)> WarnCallback) {
46   Offset = *OffsetPtr;
47   // Check that we can read the unit length field.
48   if (!Data.isValidOffsetForDataOfSize(Offset, 4))
49     return createStringError(errc::invalid_argument,
50                              "section is not large enough to contain an "
51                              "address table length at offset 0x%" PRIx64,
52                              Offset);
53   // TODO: Add support for DWARF64.
54   Format = dwarf::DwarfFormat::DWARF32;
55   Length = Data.getU32(OffsetPtr);
56   if (Length == dwarf::DW_LENGTH_DWARF64) {
57     invalidateLength();
58     return createStringError(
59         errc::not_supported,
60         "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64, Offset);
61   }
62 
63   if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, Length)) {
64     uint32_t DiagnosticLength = Length;
65     invalidateLength();
66     return createStringError(
67         errc::invalid_argument,
68         "section is not large enough to contain an address table "
69         "at offset 0x%" PRIx64 " with a unit_length value of 0x%" PRIx32,
70         Offset, DiagnosticLength);
71   }
72   uint64_t EndOffset = *OffsetPtr + Length;
73   // Ensure that we can read the remaining header fields.
74   if (Length < 4) {
75     uint32_t DiagnosticLength = Length;
76     invalidateLength();
77     return createStringError(
78         errc::invalid_argument,
79         "address table at offset 0x%" PRIx64
80         " has a unit_length value of 0x%" PRIx32
81         ", which is too small to contain a complete header",
82         Offset, DiagnosticLength);
83   }
84 
85   Version = Data.getU16(OffsetPtr);
86   AddrSize = Data.getU8(OffsetPtr);
87   SegSize = Data.getU8(OffsetPtr);
88 
89   // Perform a basic validation of the header fields.
90   if (Version != 5)
91     return createStringError(errc::not_supported,
92                              "address table at offset 0x%" PRIx64
93                              " has unsupported version %" PRIu16,
94                              Offset, Version);
95   // TODO: add support for non-zero segment selector size.
96   if (SegSize != 0)
97     return createStringError(errc::not_supported,
98                              "address table at offset 0x%" PRIx64
99                              " has unsupported segment selector size %" PRIu8,
100                              Offset, SegSize);
101 
102   if (Error Err = extractAddresses(Data, OffsetPtr, EndOffset))
103     return Err;
104   if (CUAddrSize && AddrSize != CUAddrSize) {
105     WarnCallback(createStringError(
106         errc::invalid_argument,
107         "address table at offset 0x%" PRIx64 " has address size %" PRIu8
108         " which is different from CU address size %" PRIu8,
109         Offset, AddrSize, CUAddrSize));
110   }
111   return Error::success();
112 }
113 
114 Error DWARFDebugAddrTable::extractPreStandard(const DWARFDataExtractor &Data,
115                                               uint64_t *OffsetPtr,
116                                               uint16_t CUVersion,
117                                               uint8_t CUAddrSize) {
118   assert(CUVersion > 0 && CUVersion < 5);
119 
120   Offset = *OffsetPtr;
121   Length = 0;
122   Version = CUVersion;
123   AddrSize = CUAddrSize;
124   SegSize = 0;
125 
126   return extractAddresses(Data, OffsetPtr, Data.size());
127 }
128 
129 Error DWARFDebugAddrTable::extract(const DWARFDataExtractor &Data,
130                                    uint64_t *OffsetPtr,
131                                    uint16_t CUVersion,
132                                    uint8_t CUAddrSize,
133                                    std::function<void(Error)> WarnCallback) {
134   if (CUVersion > 0 && CUVersion < 5)
135     return extractPreStandard(Data, OffsetPtr, CUVersion, CUAddrSize);
136   if (CUVersion == 0)
137     WarnCallback(createStringError(errc::invalid_argument,
138                                    "DWARF version is not defined in CU,"
139                                    " assuming version 5"));
140   return extractV5(Data, OffsetPtr, CUAddrSize, WarnCallback);
141 }
142 
143 void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
144   if (DumpOpts.Verbose)
145     OS << format("0x%8.8" PRIx32 ": ", Offset);
146   if (Length)
147     OS << format("Address table header: length = 0x%8.8" PRIx32
148                  ", version = 0x%4.4" PRIx16 ", addr_size = 0x%2.2" PRIx8
149                  ", seg_size = 0x%2.2" PRIx8 "\n",
150                  Length, Version, AddrSize, SegSize);
151 
152   if (Addrs.size() > 0) {
153     const char *AddrFmt =
154         (AddrSize == 4) ? "0x%8.8" PRIx64 "\n" : "0x%16.16" PRIx64 "\n";
155     OS << "Addrs: [\n";
156     for (uint64_t Addr : Addrs)
157       OS << format(AddrFmt, Addr);
158     OS << "]\n";
159   }
160 }
161 
162 Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const {
163   if (Index < Addrs.size())
164     return Addrs[Index];
165   return createStringError(errc::invalid_argument,
166                            "Index %" PRIu32 " is out of range of the "
167                            "address table at offset 0x%" PRIx64,
168                            Index, Offset);
169 }
170 
171 Optional<uint64_t> DWARFDebugAddrTable::getFullLength() const {
172   if (Length == 0)
173     return None;
174   // TODO: DWARF64 support.
175   return Length + sizeof(uint32_t);
176 }
177 
178