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