1 //===- FunctionInfo.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/GSYM/FunctionInfo.h"
10 #include "llvm/DebugInfo/GSYM/FileWriter.h"
11 #include "llvm/DebugInfo/GSYM/GsymReader.h"
12 #include "llvm/DebugInfo/GSYM/LineTable.h"
13 #include "llvm/DebugInfo/GSYM/InlineInfo.h"
14 #include "llvm/Support/DataExtractor.h"
15
16 using namespace llvm;
17 using namespace gsym;
18
19 /// FunctionInfo information type that is used to encode the optional data
20 /// that is associated with a FunctionInfo object.
21 enum InfoType : uint32_t {
22 EndOfList = 0u,
23 LineTableInfo = 1u,
24 InlineInfo = 2u
25 };
26
operator <<(raw_ostream & OS,const FunctionInfo & FI)27 raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {
28 OS << FI.Range << ": " << "Name=" << HEX32(FI.Name) << '\n';
29 if (FI.OptLineTable)
30 OS << FI.OptLineTable << '\n';
31 if (FI.Inline)
32 OS << FI.Inline << '\n';
33 return OS;
34 }
35
decode(DataExtractor & Data,uint64_t BaseAddr)36 llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
37 uint64_t BaseAddr) {
38 FunctionInfo FI;
39 uint64_t Offset = 0;
40 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
41 return createStringError(std::errc::io_error,
42 "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset);
43 FI.Range = {BaseAddr, BaseAddr + Data.getU32(&Offset)};
44 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
45 return createStringError(std::errc::io_error,
46 "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);
47 FI.Name = Data.getU32(&Offset);
48 if (FI.Name == 0)
49 return createStringError(std::errc::io_error,
50 "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x",
51 Offset - 4, FI.Name);
52 bool Done = false;
53 while (!Done) {
54 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
55 return createStringError(std::errc::io_error,
56 "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset);
57 const uint32_t IT = Data.getU32(&Offset);
58 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
59 return createStringError(std::errc::io_error,
60 "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset);
61 const uint32_t InfoLength = Data.getU32(&Offset);
62 if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))
63 return createStringError(std::errc::io_error,
64 "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",
65 Offset, IT);
66 DataExtractor InfoData(Data.getData().substr(Offset, InfoLength),
67 Data.isLittleEndian(),
68 Data.getAddressSize());
69 switch (IT) {
70 case InfoType::EndOfList:
71 Done = true;
72 break;
73
74 case InfoType::LineTableInfo:
75 if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr))
76 FI.OptLineTable = std::move(LT.get());
77 else
78 return LT.takeError();
79 break;
80
81 case InfoType::InlineInfo:
82 if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr))
83 FI.Inline = std::move(II.get());
84 else
85 return II.takeError();
86 break;
87
88 default:
89 return createStringError(std::errc::io_error,
90 "0x%8.8" PRIx64 ": unsupported InfoType %u",
91 Offset-8, IT);
92 }
93 Offset += InfoLength;
94 }
95 return std::move(FI);
96 }
97
encode(FileWriter & O) const98 llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const {
99 if (!isValid())
100 return createStringError(std::errc::invalid_argument,
101 "attempted to encode invalid FunctionInfo object");
102 // Align FunctionInfo data to a 4 byte alignment.
103 O.alignTo(4);
104 const uint64_t FuncInfoOffset = O.tell();
105 // Write the size in bytes of this function as a uint32_t. This can be zero
106 // if we just have a symbol from a symbol table and that symbol has no size.
107 O.writeU32(size());
108 // Write the name of this function as a uint32_t string table offset.
109 O.writeU32(Name);
110
111 if (OptLineTable) {
112 O.writeU32(InfoType::LineTableInfo);
113 // Write a uint32_t length as zero for now, we will fix this up after
114 // writing the LineTable out with the number of bytes that were written.
115 O.writeU32(0);
116 const auto StartOffset = O.tell();
117 llvm::Error err = OptLineTable->encode(O, Range.start());
118 if (err)
119 return std::move(err);
120 const auto Length = O.tell() - StartOffset;
121 if (Length > UINT32_MAX)
122 return createStringError(std::errc::invalid_argument,
123 "LineTable length is greater than UINT32_MAX");
124 // Fixup the size of the LineTable data with the correct size.
125 O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
126 }
127
128 // Write out the inline function info if we have any and if it is valid.
129 if (Inline) {
130 O.writeU32(InfoType::InlineInfo);
131 // Write a uint32_t length as zero for now, we will fix this up after
132 // writing the LineTable out with the number of bytes that were written.
133 O.writeU32(0);
134 const auto StartOffset = O.tell();
135 llvm::Error err = Inline->encode(O, Range.start());
136 if (err)
137 return std::move(err);
138 const auto Length = O.tell() - StartOffset;
139 if (Length > UINT32_MAX)
140 return createStringError(std::errc::invalid_argument,
141 "InlineInfo length is greater than UINT32_MAX");
142 // Fixup the size of the InlineInfo data with the correct size.
143 O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
144 }
145
146 // Terminate the data chunks with and end of list with zero size
147 O.writeU32(InfoType::EndOfList);
148 O.writeU32(0);
149 return FuncInfoOffset;
150 }
151
152
lookup(DataExtractor & Data,const GsymReader & GR,uint64_t FuncAddr,uint64_t Addr)153 llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data,
154 const GsymReader &GR,
155 uint64_t FuncAddr,
156 uint64_t Addr) {
157 LookupResult LR;
158 LR.LookupAddr = Addr;
159 uint64_t Offset = 0;
160 LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};
161 uint32_t NameOffset = Data.getU32(&Offset);
162 // The "lookup" functions doesn't report errors as accurately as the "decode"
163 // function as it is meant to be fast. For more accurage errors we could call
164 // "decode".
165 if (!Data.isValidOffset(Offset))
166 return createStringError(std::errc::io_error,
167 "FunctionInfo data is truncated");
168 // This function will be called with the result of a binary search of the
169 // address table, we must still make sure the address does not fall into a
170 // gap between functions are after the last function.
171 if (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))
172 return createStringError(std::errc::io_error,
173 "address 0x%" PRIx64 " is not in GSYM", Addr);
174
175 if (NameOffset == 0)
176 return createStringError(std::errc::io_error,
177 "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",
178 Offset - 4);
179 LR.FuncName = GR.getString(NameOffset);
180 bool Done = false;
181 Optional<LineEntry> LineEntry;
182 Optional<DataExtractor> InlineInfoData;
183 while (!Done) {
184 if (!Data.isValidOffsetForDataOfSize(Offset, 8))
185 return createStringError(std::errc::io_error,
186 "FunctionInfo data is truncated");
187 const uint32_t IT = Data.getU32(&Offset);
188 const uint32_t InfoLength = Data.getU32(&Offset);
189 const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);
190 if (InfoLength != InfoBytes.size())
191 return createStringError(std::errc::io_error,
192 "FunctionInfo data is truncated");
193 DataExtractor InfoData(InfoBytes, Data.isLittleEndian(),
194 Data.getAddressSize());
195 switch (IT) {
196 case InfoType::EndOfList:
197 Done = true;
198 break;
199
200 case InfoType::LineTableInfo:
201 if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))
202 LineEntry = ExpectedLE.get();
203 else
204 return ExpectedLE.takeError();
205 break;
206
207 case InfoType::InlineInfo:
208 // We will parse the inline info after our line table, but only if
209 // we have a line entry.
210 InlineInfoData = InfoData;
211 break;
212
213 default:
214 break;
215 }
216 Offset += InfoLength;
217 }
218
219 if (!LineEntry) {
220 // We don't have a valid line entry for our address, fill in our source
221 // location as best we can and return.
222 SourceLocation SrcLoc;
223 SrcLoc.Name = LR.FuncName;
224 SrcLoc.Offset = Addr - FuncAddr;
225 LR.Locations.push_back(SrcLoc);
226 return LR;
227 }
228
229 Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);
230 if (!LineEntryFile)
231 return createStringError(std::errc::invalid_argument,
232 "failed to extract file[%" PRIu32 "]",
233 LineEntry->File);
234
235 SourceLocation SrcLoc;
236 SrcLoc.Name = LR.FuncName;
237 SrcLoc.Offset = Addr - FuncAddr;
238 SrcLoc.Dir = GR.getString(LineEntryFile->Dir);
239 SrcLoc.Base = GR.getString(LineEntryFile->Base);
240 SrcLoc.Line = LineEntry->Line;
241 LR.Locations.push_back(SrcLoc);
242 // If we don't have inline information, we are done.
243 if (!InlineInfoData)
244 return LR;
245 // We have inline information. Try to augment the lookup result with this
246 // data.
247 llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
248 LR.Locations);
249 if (Err)
250 return std::move(Err);
251 return LR;
252 }
253