1360784Sdim//===- FunctionInfo.cpp ---------------------------------------------------===//
2351278Sdim//
3360784Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4360784Sdim// See https://llvm.org/LICENSE.txt for license information.
5360784Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6351278Sdim//
7351278Sdim//===----------------------------------------------------------------------===//
8351278Sdim
9351278Sdim#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
10360784Sdim#include "llvm/DebugInfo/GSYM/FileWriter.h"
11360784Sdim#include "llvm/DebugInfo/GSYM/GsymReader.h"
12360784Sdim#include "llvm/DebugInfo/GSYM/LineTable.h"
13360784Sdim#include "llvm/DebugInfo/GSYM/InlineInfo.h"
14360784Sdim#include "llvm/Support/DataExtractor.h"
15351278Sdim
16351278Sdimusing namespace llvm;
17351278Sdimusing namespace gsym;
18351278Sdim
19360784Sdim/// FunctionInfo information type that is used to encode the optional data
20360784Sdim/// that is associated with a FunctionInfo object.
21360784Sdimenum InfoType : uint32_t {
22360784Sdim  EndOfList = 0u,
23360784Sdim  LineTableInfo = 1u,
24360784Sdim  InlineInfo = 2u
25360784Sdim};
26360784Sdim
27351278Sdimraw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {
28351278Sdim  OS << '[' << HEX64(FI.Range.Start) << '-' << HEX64(FI.Range.End) << "): "
29360784Sdim     << "Name=" << HEX32(FI.Name) << '\n' << FI.OptLineTable << FI.Inline;
30351278Sdim  return OS;
31351278Sdim}
32360784Sdim
33360784Sdimllvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
34360784Sdim                                                  uint64_t BaseAddr) {
35360784Sdim  FunctionInfo FI;
36360784Sdim  FI.Range.Start = BaseAddr;
37360784Sdim  uint64_t Offset = 0;
38360784Sdim  if (!Data.isValidOffsetForDataOfSize(Offset, 4))
39360784Sdim    return createStringError(std::errc::io_error,
40360784Sdim        "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset);
41360784Sdim  FI.Range.End = FI.Range.Start + Data.getU32(&Offset);
42360784Sdim  if (!Data.isValidOffsetForDataOfSize(Offset, 4))
43360784Sdim    return createStringError(std::errc::io_error,
44360784Sdim        "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);
45360784Sdim  FI.Name = Data.getU32(&Offset);
46360784Sdim  if (FI.Name == 0)
47360784Sdim    return createStringError(std::errc::io_error,
48360784Sdim        "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x",
49360784Sdim        Offset - 4, FI.Name);
50360784Sdim  bool Done = false;
51360784Sdim  while (!Done) {
52360784Sdim    if (!Data.isValidOffsetForDataOfSize(Offset, 4))
53360784Sdim      return createStringError(std::errc::io_error,
54360784Sdim          "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset);
55360784Sdim    const uint32_t IT = Data.getU32(&Offset);
56360784Sdim    if (!Data.isValidOffsetForDataOfSize(Offset, 4))
57360784Sdim      return createStringError(std::errc::io_error,
58360784Sdim          "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset);
59360784Sdim    const uint32_t InfoLength = Data.getU32(&Offset);
60360784Sdim    if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))
61360784Sdim      return createStringError(std::errc::io_error,
62360784Sdim          "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",
63360784Sdim          Offset, IT);
64360784Sdim    DataExtractor InfoData(Data.getData().substr(Offset, InfoLength),
65360784Sdim                           Data.isLittleEndian(),
66360784Sdim                           Data.getAddressSize());
67360784Sdim    switch (IT) {
68360784Sdim      case InfoType::EndOfList:
69360784Sdim        Done = true;
70360784Sdim        break;
71360784Sdim
72360784Sdim      case InfoType::LineTableInfo:
73360784Sdim        if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr))
74360784Sdim          FI.OptLineTable = std::move(LT.get());
75360784Sdim        else
76360784Sdim          return LT.takeError();
77360784Sdim        break;
78360784Sdim
79360784Sdim      case InfoType::InlineInfo:
80360784Sdim        if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr))
81360784Sdim          FI.Inline = std::move(II.get());
82360784Sdim        else
83360784Sdim          return II.takeError();
84360784Sdim        break;
85360784Sdim
86360784Sdim      default:
87360784Sdim        return createStringError(std::errc::io_error,
88360784Sdim                                 "0x%8.8" PRIx64 ": unsupported InfoType %u",
89360784Sdim                                 Offset-8, IT);
90360784Sdim    }
91360784Sdim    Offset += InfoLength;
92360784Sdim  }
93360784Sdim  return std::move(FI);
94360784Sdim}
95360784Sdim
96360784Sdimllvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const {
97360784Sdim  if (!isValid())
98360784Sdim    return createStringError(std::errc::invalid_argument,
99360784Sdim        "attempted to encode invalid FunctionInfo object");
100360784Sdim  // Align FunctionInfo data to a 4 byte alignment.
101360784Sdim  O.alignTo(4);
102360784Sdim  const uint64_t FuncInfoOffset = O.tell();
103360784Sdim  // Write the size in bytes of this function as a uint32_t. This can be zero
104360784Sdim  // if we just have a symbol from a symbol table and that symbol has no size.
105360784Sdim  O.writeU32(size());
106360784Sdim  // Write the name of this function as a uint32_t string table offset.
107360784Sdim  O.writeU32(Name);
108360784Sdim
109360784Sdim  if (OptLineTable.hasValue()) {
110360784Sdim    O.writeU32(InfoType::LineTableInfo);
111360784Sdim    // Write a uint32_t length as zero for now, we will fix this up after
112360784Sdim    // writing the LineTable out with the number of bytes that were written.
113360784Sdim    O.writeU32(0);
114360784Sdim    const auto StartOffset = O.tell();
115360784Sdim    llvm::Error err = OptLineTable->encode(O, Range.Start);
116360784Sdim    if (err)
117360784Sdim      return std::move(err);
118360784Sdim    const auto Length = O.tell() - StartOffset;
119360784Sdim    if (Length > UINT32_MAX)
120360784Sdim        return createStringError(std::errc::invalid_argument,
121360784Sdim            "LineTable length is greater than UINT32_MAX");
122360784Sdim    // Fixup the size of the LineTable data with the correct size.
123360784Sdim    O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
124360784Sdim  }
125360784Sdim
126360784Sdim  // Write out the inline function info if we have any and if it is valid.
127360784Sdim  if (Inline.hasValue()) {
128360784Sdim    O.writeU32(InfoType::InlineInfo);
129360784Sdim    // Write a uint32_t length as zero for now, we will fix this up after
130360784Sdim    // writing the LineTable out with the number of bytes that were written.
131360784Sdim    O.writeU32(0);
132360784Sdim    const auto StartOffset = O.tell();
133360784Sdim    llvm::Error err = Inline->encode(O, Range.Start);
134360784Sdim    if (err)
135360784Sdim      return std::move(err);
136360784Sdim    const auto Length = O.tell() - StartOffset;
137360784Sdim    if (Length > UINT32_MAX)
138360784Sdim        return createStringError(std::errc::invalid_argument,
139360784Sdim            "InlineInfo length is greater than UINT32_MAX");
140360784Sdim    // Fixup the size of the InlineInfo data with the correct size.
141360784Sdim    O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
142360784Sdim  }
143360784Sdim
144360784Sdim  // Terminate the data chunks with and end of list with zero size
145360784Sdim  O.writeU32(InfoType::EndOfList);
146360784Sdim  O.writeU32(0);
147360784Sdim  return FuncInfoOffset;
148360784Sdim}
149360784Sdim
150360784Sdim
151360784Sdimllvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data,
152360784Sdim                                                  const GsymReader &GR,
153360784Sdim                                                  uint64_t FuncAddr,
154360784Sdim                                                  uint64_t Addr) {
155360784Sdim  LookupResult LR;
156360784Sdim  LR.LookupAddr = Addr;
157360784Sdim  LR.FuncRange.Start = FuncAddr;
158360784Sdim  uint64_t Offset = 0;
159360784Sdim  LR.FuncRange.End = FuncAddr + Data.getU32(&Offset);
160360784Sdim  uint32_t NameOffset = Data.getU32(&Offset);
161360784Sdim  // The "lookup" functions doesn't report errors as accurately as the "decode"
162360784Sdim  // function as it is meant to be fast. For more accurage errors we could call
163360784Sdim  // "decode".
164360784Sdim  if (!Data.isValidOffset(Offset))
165360784Sdim    return createStringError(std::errc::io_error,
166360784Sdim                              "FunctionInfo data is truncated");
167360784Sdim  // This function will be called with the result of a binary search of the
168360784Sdim  // address table, we must still make sure the address does not fall into a
169360784Sdim  // gap between functions are after the last function.
170360784Sdim  if (Addr >= LR.FuncRange.End)
171360784Sdim    return createStringError(std::errc::io_error,
172360784Sdim        "address 0x%" PRIx64 " is not in GSYM", Addr);
173360784Sdim
174360784Sdim  if (NameOffset == 0)
175360784Sdim    return createStringError(std::errc::io_error,
176360784Sdim        "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",
177360784Sdim        Offset - 4);
178360784Sdim  LR.FuncName = GR.getString(NameOffset);
179360784Sdim  bool Done = false;
180360784Sdim  Optional<LineEntry> LineEntry;
181360784Sdim  Optional<DataExtractor> InlineInfoData;
182360784Sdim  while (!Done) {
183360784Sdim    if (!Data.isValidOffsetForDataOfSize(Offset, 8))
184360784Sdim      return createStringError(std::errc::io_error,
185360784Sdim                               "FunctionInfo data is truncated");
186360784Sdim    const uint32_t IT = Data.getU32(&Offset);
187360784Sdim    const uint32_t InfoLength = Data.getU32(&Offset);
188360784Sdim    const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);
189360784Sdim    if (InfoLength != InfoBytes.size())
190360784Sdim      return createStringError(std::errc::io_error,
191360784Sdim                               "FunctionInfo data is truncated");
192360784Sdim    DataExtractor InfoData(InfoBytes, Data.isLittleEndian(),
193360784Sdim                           Data.getAddressSize());
194360784Sdim    switch (IT) {
195360784Sdim      case InfoType::EndOfList:
196360784Sdim        Done = true;
197360784Sdim        break;
198360784Sdim
199360784Sdim      case InfoType::LineTableInfo:
200360784Sdim        if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))
201360784Sdim          LineEntry = ExpectedLE.get();
202360784Sdim        else
203360784Sdim          return ExpectedLE.takeError();
204360784Sdim        break;
205360784Sdim
206360784Sdim      case InfoType::InlineInfo:
207360784Sdim        // We will parse the inline info after our line table, but only if
208360784Sdim        // we have a line entry.
209360784Sdim        InlineInfoData = InfoData;
210360784Sdim        break;
211360784Sdim
212360784Sdim      default:
213360784Sdim        break;
214360784Sdim    }
215360784Sdim    Offset += InfoLength;
216360784Sdim  }
217360784Sdim
218360784Sdim  if (!LineEntry) {
219360784Sdim    // We don't have a valid line entry for our address, fill in our source
220360784Sdim    // location as best we can and return.
221360784Sdim    SourceLocation SrcLoc;
222360784Sdim    SrcLoc.Name = LR.FuncName;
223360784Sdim    LR.Locations.push_back(SrcLoc);
224360784Sdim    return LR;
225360784Sdim  }
226360784Sdim
227360784Sdim  Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);
228360784Sdim  if (!LineEntryFile)
229360784Sdim    return createStringError(std::errc::invalid_argument,
230360784Sdim                              "failed to extract file[%" PRIu32 "]",
231360784Sdim                              LineEntry->File);
232360784Sdim
233360784Sdim  SourceLocation SrcLoc;
234360784Sdim  SrcLoc.Name = LR.FuncName;
235360784Sdim  SrcLoc.Dir = GR.getString(LineEntryFile->Dir);
236360784Sdim  SrcLoc.Base = GR.getString(LineEntryFile->Base);
237360784Sdim  SrcLoc.Line = LineEntry->Line;
238360784Sdim  LR.Locations.push_back(SrcLoc);
239360784Sdim  // If we don't have inline information, we are done.
240360784Sdim  if (!InlineInfoData)
241360784Sdim    return LR;
242360784Sdim  // We have inline information. Try to augment the lookup result with this
243360784Sdim  // data.
244360784Sdim  llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
245360784Sdim                                       LR.Locations);
246360784Sdim  if (Err)
247360784Sdim    return std::move(Err);
248360784Sdim  return LR;
249360784Sdim}
250