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