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