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 void DWARFDebugAddrTable::clear() {
16   HeaderData = {};
17   Addrs.clear();
18   invalidateLength();
19 }
20 
21 Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data,
22                                    uint64_t *OffsetPtr,
23                                    uint16_t Version,
24                                    uint8_t AddrSize,
25                                    std::function<void(Error)> WarnCallback) {
26   clear();
27   HeaderOffset = *OffsetPtr;
28   // Read and verify the length field.
29   if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t)))
30     return createStringError(errc::invalid_argument,
31                        "section is not large enough to contain an "
32                        "address table length at offset 0x%"
33                        PRIx64, *OffsetPtr);
34   uint16_t UnitVersion;
35   if (Version == 0) {
36     WarnCallback(createStringError(errc::invalid_argument,
37                        "DWARF version is not defined in CU,"
38                        " assuming version 5"));
39     UnitVersion = 5;
40   } else {
41     UnitVersion = Version;
42   }
43   // TODO: Add support for DWARF64.
44   Format = dwarf::DwarfFormat::DWARF32;
45   if (UnitVersion >= 5) {
46     HeaderData.Length = Data.getU32(OffsetPtr);
47     if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) {
48       invalidateLength();
49       return createStringError(errc::not_supported,
50           "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64,
51           HeaderOffset);
52     }
53     if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) {
54       uint32_t TmpLength = HeaderData.Length;
55       invalidateLength();
56       return createStringError(errc::invalid_argument,
57                          "address table at offset 0x%" PRIx64
58                          " has a unit_length value of 0x%" PRIx32
59                          ", which is too small to contain a complete header",
60                          HeaderOffset, TmpLength);
61     }
62     uint64_t End = HeaderOffset + getLength();
63     if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) {
64       uint32_t TmpLength = HeaderData.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           HeaderOffset, TmpLength);
71     }
72 
73     HeaderData.Version = Data.getU16(OffsetPtr);
74     HeaderData.AddrSize = Data.getU8(OffsetPtr);
75     HeaderData.SegSize = Data.getU8(OffsetPtr);
76     DataSize = getDataSize();
77   } else {
78     HeaderData.Version = UnitVersion;
79     HeaderData.AddrSize = AddrSize;
80     // TODO: Support for non-zero SegSize.
81     HeaderData.SegSize = 0;
82     DataSize = Data.size();
83   }
84 
85   // Perform basic validation of the remaining header fields.
86 
87   // We support DWARF version 5 for now as well as pre-DWARF5
88   // implementations of .debug_addr table, which doesn't contain a header
89   // and consists only of a series of addresses.
90   if (HeaderData.Version > 5) {
91     return createStringError(errc::not_supported,
92                              "address table at offset 0x%" PRIx64
93                              " has unsupported version %" PRIu16,
94                              HeaderOffset, HeaderData.Version);
95   }
96   // FIXME: For now we just treat version mismatch as an error,
97   // however the correct way to associate a .debug_addr table
98   // with a .debug_info table is to look at the DW_AT_addr_base
99   // attribute in the info table.
100   if (HeaderData.Version != UnitVersion)
101     return createStringError(errc::invalid_argument,
102                        "address table at offset 0x%" PRIx64
103                        " has version %" PRIu16
104                        " which is different from the version suggested"
105                        " by the DWARF unit header: %" PRIu16,
106                        HeaderOffset, HeaderData.Version, UnitVersion);
107   if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)
108     return createStringError(errc::not_supported,
109                        "address table at offset 0x%" PRIx64
110                        " has unsupported address size %" PRIu8
111                        " (4 and 8 are supported)",
112                        HeaderOffset, HeaderData.AddrSize);
113   if (HeaderData.AddrSize != AddrSize && AddrSize != 0)
114     WarnCallback(createStringError(
115         errc::invalid_argument,
116         "address table at offset 0x%" PRIx64 " has address size %" PRIu8
117         " which is different from CU address size %" PRIu8,
118         HeaderOffset, HeaderData.AddrSize, AddrSize));
119 
120   // TODO: add support for non-zero segment selector size.
121   if (HeaderData.SegSize != 0)
122     return createStringError(errc::not_supported,
123                        "address table at offset 0x%" PRIx64
124                        " has unsupported segment selector size %" PRIu8,
125                        HeaderOffset, HeaderData.SegSize);
126   if (DataSize % HeaderData.AddrSize != 0) {
127     invalidateLength();
128     return createStringError(errc::invalid_argument,
129                        "address table at offset 0x%" PRIx64
130                        " contains data of size %" PRIu32
131                        " which is not a multiple of addr size %" PRIu8,
132                        HeaderOffset, DataSize, HeaderData.AddrSize);
133   }
134   Data.setAddressSize(HeaderData.AddrSize);
135   uint32_t AddrCount = DataSize / HeaderData.AddrSize;
136   for (uint32_t I = 0; I < AddrCount; ++I)
137     Addrs.push_back(Data.getRelocatedAddress(OffsetPtr));
138   return Error::success();
139 }
140 
141 void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
142   if (DumpOpts.Verbose)
143     OS << format("0x%8.8" PRIx32 ": ", HeaderOffset);
144   if (HeaderData.Length)
145     OS << format("Address table header: length = 0x%8.8" PRIx32
146                  ", version = 0x%4.4" PRIx16 ", "
147                  "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n",
148                  HeaderData.Length, HeaderData.Version, HeaderData.AddrSize,
149                  HeaderData.SegSize);
150 
151   if (Addrs.size() > 0) {
152     const char *AddrFmt = (HeaderData.AddrSize == 4) ? "0x%8.8" PRIx64 "\n"
153                                                      : "0x%16.16" PRIx64 "\n";
154     OS << "Addrs: [\n";
155     for (uint64_t Addr : Addrs)
156       OS << format(AddrFmt, Addr);
157     OS << "]\n";
158   }
159 }
160 
161 Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const {
162   if (Index < Addrs.size())
163     return Addrs[Index];
164   return createStringError(errc::invalid_argument,
165                            "Index %" PRIu32 " is out of range in the "
166                            "address table at offset 0x%" PRIx64,
167                            Index, HeaderOffset);
168 }
169 
170 uint32_t DWARFDebugAddrTable::getLength() const {
171   if (HeaderData.Length == 0)
172     return 0;
173   // TODO: DWARF64 support.
174   return HeaderData.Length + sizeof(uint32_t);
175 }
176 
177 uint32_t DWARFDebugAddrTable::getDataSize() const {
178   if (DataSize != 0)
179     return DataSize;
180   if (getLength() == 0)
181     return 0;
182   return getLength() - getHeaderSize();
183 }
184