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
16using namespace llvm;
17using namespace gsym;
18
19/// FunctionInfo information type that is used to encode the optional data
20/// that is associated with a FunctionInfo object.
21enum InfoType : uint32_t {
22  EndOfList = 0u,
23  LineTableInfo = 1u,
24  InlineInfo = 2u
25};
26
27raw_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
36llvm::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
99llvm::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
154llvm::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