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   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 
98 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 
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