aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm16/lib/DebugInfo/GSYM/FunctionInfo.cpp
blob: 51058fc09cf195517d1881bfc8ff47dd73aebee6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
//===- FunctionInfo.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
#include "llvm/DebugInfo/GSYM/FileWriter.h"
#include "llvm/DebugInfo/GSYM/GsymReader.h"
#include "llvm/DebugInfo/GSYM/LineTable.h"
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
#include "llvm/Support/DataExtractor.h"
#include <optional>

using namespace llvm;
using namespace gsym;

/// FunctionInfo information type that is used to encode the optional data
/// that is associated with a FunctionInfo object.
enum InfoType : uint32_t {
  EndOfList = 0u,
  LineTableInfo = 1u,
  InlineInfo = 2u
};

raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {
  OS << FI.Range << ": " << "Name=" << HEX32(FI.Name) << '\n';
  if (FI.OptLineTable)
    OS << FI.OptLineTable << '\n';
  if (FI.Inline)
    OS << FI.Inline << '\n';
  return OS;
}

llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
                                                  uint64_t BaseAddr) {
  FunctionInfo FI;
  uint64_t Offset = 0;
  if (!Data.isValidOffsetForDataOfSize(Offset, 4))
    return createStringError(std::errc::io_error,
        "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset);
  FI.Range = {BaseAddr, BaseAddr + Data.getU32(&Offset)};
  if (!Data.isValidOffsetForDataOfSize(Offset, 4))
    return createStringError(std::errc::io_error,
        "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);
  FI.Name = Data.getU32(&Offset);
  if (FI.Name == 0)
    return createStringError(std::errc::io_error,
        "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x",
        Offset - 4, FI.Name);
  bool Done = false;
  while (!Done) {
    if (!Data.isValidOffsetForDataOfSize(Offset, 4))
      return createStringError(std::errc::io_error,
          "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset);
    const uint32_t IT = Data.getU32(&Offset);
    if (!Data.isValidOffsetForDataOfSize(Offset, 4))
      return createStringError(std::errc::io_error,
          "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset);
    const uint32_t InfoLength = Data.getU32(&Offset);
    if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))
      return createStringError(std::errc::io_error,
          "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",
          Offset, IT);
    DataExtractor InfoData(Data.getData().substr(Offset, InfoLength),
                           Data.isLittleEndian(),
                           Data.getAddressSize());
    switch (IT) {
      case InfoType::EndOfList:
        Done = true;
        break;

      case InfoType::LineTableInfo:
        if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr))
          FI.OptLineTable = std::move(LT.get());
        else
          return LT.takeError();
        break;

      case InfoType::InlineInfo:
        if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr))
          FI.Inline = std::move(II.get());
        else
          return II.takeError();
        break;

      default:
        return createStringError(std::errc::io_error,
                                 "0x%8.8" PRIx64 ": unsupported InfoType %u",
                                 Offset-8, IT);
    }
    Offset += InfoLength;
  }
  return std::move(FI);
}

llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const {
  if (!isValid())
    return createStringError(std::errc::invalid_argument,
        "attempted to encode invalid FunctionInfo object");
  // Align FunctionInfo data to a 4 byte alignment.
  O.alignTo(4);
  const uint64_t FuncInfoOffset = O.tell();
  // Write the size in bytes of this function as a uint32_t. This can be zero
  // if we just have a symbol from a symbol table and that symbol has no size.
  O.writeU32(size());
  // Write the name of this function as a uint32_t string table offset.
  O.writeU32(Name);

  if (OptLineTable) {
    O.writeU32(InfoType::LineTableInfo);
    // Write a uint32_t length as zero for now, we will fix this up after
    // writing the LineTable out with the number of bytes that were written.
    O.writeU32(0);
    const auto StartOffset = O.tell();
    llvm::Error err = OptLineTable->encode(O, Range.start());
    if (err)
      return std::move(err);
    const auto Length = O.tell() - StartOffset;
    if (Length > UINT32_MAX)
        return createStringError(std::errc::invalid_argument,
            "LineTable length is greater than UINT32_MAX");
    // Fixup the size of the LineTable data with the correct size.
    O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
  }

  // Write out the inline function info if we have any and if it is valid.
  if (Inline) {
    O.writeU32(InfoType::InlineInfo);
    // Write a uint32_t length as zero for now, we will fix this up after
    // writing the LineTable out with the number of bytes that were written.
    O.writeU32(0);
    const auto StartOffset = O.tell();
    llvm::Error err = Inline->encode(O, Range.start());
    if (err)
      return std::move(err);
    const auto Length = O.tell() - StartOffset;
    if (Length > UINT32_MAX)
        return createStringError(std::errc::invalid_argument,
            "InlineInfo length is greater than UINT32_MAX");
    // Fixup the size of the InlineInfo data with the correct size.
    O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
  }

  // Terminate the data chunks with and end of list with zero size
  O.writeU32(InfoType::EndOfList);
  O.writeU32(0);
  return FuncInfoOffset;
}


llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data,
                                                  const GsymReader &GR,
                                                  uint64_t FuncAddr,
                                                  uint64_t Addr) {
  LookupResult LR;
  LR.LookupAddr = Addr;
  uint64_t Offset = 0;
  LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};
  uint32_t NameOffset = Data.getU32(&Offset);
  // The "lookup" functions doesn't report errors as accurately as the "decode"
  // function as it is meant to be fast. For more accurage errors we could call
  // "decode".
  if (!Data.isValidOffset(Offset))
    return createStringError(std::errc::io_error,
                              "FunctionInfo data is truncated");
  // This function will be called with the result of a binary search of the
  // address table, we must still make sure the address does not fall into a
  // gap between functions are after the last function.
  if (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))
    return createStringError(std::errc::io_error,
        "address 0x%" PRIx64 " is not in GSYM", Addr);

  if (NameOffset == 0)
    return createStringError(std::errc::io_error,
        "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",
        Offset - 4);
  LR.FuncName = GR.getString(NameOffset);
  bool Done = false;
  std::optional<LineEntry> LineEntry;
  std::optional<DataExtractor> InlineInfoData;
  while (!Done) {
    if (!Data.isValidOffsetForDataOfSize(Offset, 8))
      return createStringError(std::errc::io_error,
                               "FunctionInfo data is truncated");
    const uint32_t IT = Data.getU32(&Offset);
    const uint32_t InfoLength = Data.getU32(&Offset);
    const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);
    if (InfoLength != InfoBytes.size())
      return createStringError(std::errc::io_error,
                               "FunctionInfo data is truncated");
    DataExtractor InfoData(InfoBytes, Data.isLittleEndian(),
                           Data.getAddressSize());
    switch (IT) {
      case InfoType::EndOfList:
        Done = true;
        break;

      case InfoType::LineTableInfo:
        if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))
          LineEntry = ExpectedLE.get();
        else
          return ExpectedLE.takeError();
        break;

      case InfoType::InlineInfo:
        // We will parse the inline info after our line table, but only if
        // we have a line entry.
        InlineInfoData = InfoData;
        break;

      default:
        break;
    }
    Offset += InfoLength;
  }

  if (!LineEntry) {
    // We don't have a valid line entry for our address, fill in our source
    // location as best we can and return.
    SourceLocation SrcLoc;
    SrcLoc.Name = LR.FuncName;
    SrcLoc.Offset = Addr - FuncAddr;
    LR.Locations.push_back(SrcLoc);
    return LR;
  }

  std::optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);
  if (!LineEntryFile)
    return createStringError(std::errc::invalid_argument,
                              "failed to extract file[%" PRIu32 "]",
                              LineEntry->File);

  SourceLocation SrcLoc;
  SrcLoc.Name = LR.FuncName;
  SrcLoc.Offset = Addr - FuncAddr;
  SrcLoc.Dir = GR.getString(LineEntryFile->Dir);
  SrcLoc.Base = GR.getString(LineEntryFile->Base);
  SrcLoc.Line = LineEntry->Line;
  LR.Locations.push_back(SrcLoc);
  // If we don't have inline information, we are done.
  if (!InlineInfoData)
    return LR;
  // We have inline information. Try to augment the lookup result with this
  // data.
  llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
                                       LR.Locations);
  if (Err)
    return std::move(Err);
  return LR;
}