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 
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 
36 llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
37                                                   uint64_t BaseAddr) {
38   FunctionInfo FI;
39   FI.Range.Start = BaseAddr;
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.End = FI.Range.Start + 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.hasValue()) {
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.hasValue()) {
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   LR.FuncRange.Start = FuncAddr;
161   uint64_t Offset = 0;
162   LR.FuncRange.End = FuncAddr + Data.getU32(&Offset);
163   uint32_t NameOffset = Data.getU32(&Offset);
164   // The "lookup" functions doesn't report errors as accurately as the "decode"
165   // function as it is meant to be fast. For more accurage errors we could call
166   // "decode".
167   if (!Data.isValidOffset(Offset))
168     return createStringError(std::errc::io_error,
169                               "FunctionInfo data is truncated");
170   // This function will be called with the result of a binary search of the
171   // address table, we must still make sure the address does not fall into a
172   // gap between functions are after the last function.
173   if (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))
174     return createStringError(std::errc::io_error,
175         "address 0x%" PRIx64 " is not in GSYM", Addr);
176 
177   if (NameOffset == 0)
178     return createStringError(std::errc::io_error,
179         "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",
180         Offset - 4);
181   LR.FuncName = GR.getString(NameOffset);
182   bool Done = false;
183   Optional<LineEntry> LineEntry;
184   Optional<DataExtractor> InlineInfoData;
185   while (!Done) {
186     if (!Data.isValidOffsetForDataOfSize(Offset, 8))
187       return createStringError(std::errc::io_error,
188                                "FunctionInfo data is truncated");
189     const uint32_t IT = Data.getU32(&Offset);
190     const uint32_t InfoLength = Data.getU32(&Offset);
191     const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);
192     if (InfoLength != InfoBytes.size())
193       return createStringError(std::errc::io_error,
194                                "FunctionInfo data is truncated");
195     DataExtractor InfoData(InfoBytes, Data.isLittleEndian(),
196                            Data.getAddressSize());
197     switch (IT) {
198       case InfoType::EndOfList:
199         Done = true;
200         break;
201 
202       case InfoType::LineTableInfo:
203         if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))
204           LineEntry = ExpectedLE.get();
205         else
206           return ExpectedLE.takeError();
207         break;
208 
209       case InfoType::InlineInfo:
210         // We will parse the inline info after our line table, but only if
211         // we have a line entry.
212         InlineInfoData = InfoData;
213         break;
214 
215       default:
216         break;
217     }
218     Offset += InfoLength;
219   }
220 
221   if (!LineEntry) {
222     // We don't have a valid line entry for our address, fill in our source
223     // location as best we can and return.
224     SourceLocation SrcLoc;
225     SrcLoc.Name = LR.FuncName;
226     SrcLoc.Offset = Addr - FuncAddr;
227     LR.Locations.push_back(SrcLoc);
228     return LR;
229   }
230 
231   Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);
232   if (!LineEntryFile)
233     return createStringError(std::errc::invalid_argument,
234                               "failed to extract file[%" PRIu32 "]",
235                               LineEntry->File);
236 
237   SourceLocation SrcLoc;
238   SrcLoc.Name = LR.FuncName;
239   SrcLoc.Offset = Addr - FuncAddr;
240   SrcLoc.Dir = GR.getString(LineEntryFile->Dir);
241   SrcLoc.Base = GR.getString(LineEntryFile->Base);
242   SrcLoc.Line = LineEntry->Line;
243   LR.Locations.push_back(SrcLoc);
244   // If we don't have inline information, we are done.
245   if (!InlineInfoData)
246     return LR;
247   // We have inline information. Try to augment the lookup result with this
248   // data.
249   llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
250                                        LR.Locations);
251   if (Err)
252     return std::move(Err);
253   return LR;
254 }
255