xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp (revision cab6a39d7b343596a5823e65c0f7b426551ec22d)
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   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 
108 Error 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 
123 Error 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 
137 void 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 
160 Expected<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 
169 Optional<uint64_t> DWARFDebugAddrTable::getFullLength() const {
170   if (Length == 0)
171     return None;
172   return Length + dwarf::getUnitLengthFieldByteSize(Format);
173 }
174 
175