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