1//===- GsymReader.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/GsymReader.h"
10
11#include <assert.h>
12#include <inttypes.h>
13#include <stdio.h>
14#include <stdlib.h>
15
16#include "llvm/DebugInfo/GSYM/GsymCreator.h"
17#include "llvm/DebugInfo/GSYM/InlineInfo.h"
18#include "llvm/DebugInfo/GSYM/LineTable.h"
19#include "llvm/Support/BinaryStreamReader.h"
20#include "llvm/Support/DataExtractor.h"
21#include "llvm/Support/MemoryBuffer.h"
22
23using namespace llvm;
24using namespace gsym;
25
26GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) :
27    MemBuffer(std::move(Buffer)),
28    Endian(support::endian::system_endianness()) {}
29
30  GsymReader::GsymReader(GsymReader &&RHS) = default;
31
32GsymReader::~GsymReader() = default;
33
34llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) {
35  // Open the input file and return an appropriate error if needed.
36  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
37      MemoryBuffer::getFileOrSTDIN(Filename);
38  auto Err = BuffOrErr.getError();
39  if (Err)
40    return llvm::errorCodeToError(Err);
41  return create(BuffOrErr.get());
42}
43
44llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) {
45  auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
46  return create(MemBuffer);
47}
48
49llvm::Expected<llvm::gsym::GsymReader>
50GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
51  if (!MemBuffer.get())
52    return createStringError(std::errc::invalid_argument,
53                             "invalid memory buffer");
54  GsymReader GR(std::move(MemBuffer));
55  llvm::Error Err = GR.parse();
56  if (Err)
57    return std::move(Err);
58  return std::move(GR);
59}
60
61llvm::Error
62GsymReader::parse() {
63  BinaryStreamReader FileData(MemBuffer->getBuffer(),
64                              support::endian::system_endianness());
65  // Check for the magic bytes. This file format is designed to be mmap'ed
66  // into a process and accessed as read only. This is done for performance
67  // and efficiency for symbolicating and parsing GSYM data.
68  if (FileData.readObject(Hdr))
69    return createStringError(std::errc::invalid_argument,
70                             "not enough data for a GSYM header");
71
72  const auto HostByteOrder = support::endian::system_endianness();
73  switch (Hdr->Magic) {
74    case GSYM_MAGIC:
75      Endian = HostByteOrder;
76      break;
77    case GSYM_CIGAM:
78      // This is a GSYM file, but not native endianness.
79      Endian = sys::IsBigEndianHost ? support::little : support::big;
80      Swap.reset(new SwappedData);
81      break;
82    default:
83      return createStringError(std::errc::invalid_argument,
84                               "not a GSYM file");
85  }
86
87  bool DataIsLittleEndian = HostByteOrder != support::little;
88  // Read a correctly byte swapped header if we need to.
89  if (Swap) {
90    DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
91    if (auto ExpectedHdr = Header::decode(Data))
92      Swap->Hdr = ExpectedHdr.get();
93    else
94      return ExpectedHdr.takeError();
95    Hdr = &Swap->Hdr;
96  }
97
98  // Detect errors in the header and report any that are found. If we make it
99  // past this without errors, we know we have a good magic value, a supported
100  // version number, verified address offset size and a valid UUID size.
101  if (Error Err = Hdr->checkForError())
102    return Err;
103
104  if (!Swap) {
105    // This is the native endianness case that is most common and optimized for
106    // efficient lookups. Here we just grab pointers to the native data and
107    // use ArrayRef objects to allow efficient read only access.
108
109    // Read the address offsets.
110    if (FileData.padToAlignment(Hdr->AddrOffSize) ||
111        FileData.readArray(AddrOffsets,
112                           Hdr->NumAddresses * Hdr->AddrOffSize))
113      return createStringError(std::errc::invalid_argument,
114                              "failed to read address table");
115
116    // Read the address info offsets.
117    if (FileData.padToAlignment(4) ||
118        FileData.readArray(AddrInfoOffsets, Hdr->NumAddresses))
119      return createStringError(std::errc::invalid_argument,
120                              "failed to read address info offsets table");
121
122    // Read the file table.
123    uint32_t NumFiles = 0;
124    if (FileData.readInteger(NumFiles) || FileData.readArray(Files, NumFiles))
125      return createStringError(std::errc::invalid_argument,
126                              "failed to read file table");
127
128    // Get the string table.
129    FileData.setOffset(Hdr->StrtabOffset);
130    if (FileData.readFixedString(StrTab.Data, Hdr->StrtabSize))
131      return createStringError(std::errc::invalid_argument,
132                              "failed to read string table");
133} else {
134  // This is the non native endianness case that is not common and not
135  // optimized for lookups. Here we decode the important tables into local
136  // storage and then set the ArrayRef objects to point to these swapped
137  // copies of the read only data so lookups can be as efficient as possible.
138  DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
139
140  // Read the address offsets.
141  uint64_t Offset = alignTo(sizeof(Header), Hdr->AddrOffSize);
142  Swap->AddrOffsets.resize(Hdr->NumAddresses * Hdr->AddrOffSize);
143  switch (Hdr->AddrOffSize) {
144    case 1:
145      if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses))
146        return createStringError(std::errc::invalid_argument,
147                                  "failed to read address table");
148      break;
149    case 2:
150      if (!Data.getU16(&Offset,
151                        reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()),
152                        Hdr->NumAddresses))
153        return createStringError(std::errc::invalid_argument,
154                                  "failed to read address table");
155      break;
156    case 4:
157      if (!Data.getU32(&Offset,
158                        reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()),
159                        Hdr->NumAddresses))
160        return createStringError(std::errc::invalid_argument,
161                                  "failed to read address table");
162      break;
163    case 8:
164      if (!Data.getU64(&Offset,
165                        reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()),
166                        Hdr->NumAddresses))
167        return createStringError(std::errc::invalid_argument,
168                                  "failed to read address table");
169    }
170    AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets);
171
172    // Read the address info offsets.
173    Offset = alignTo(Offset, 4);
174    Swap->AddrInfoOffsets.resize(Hdr->NumAddresses);
175    if (Data.getU32(&Offset, Swap->AddrInfoOffsets.data(), Hdr->NumAddresses))
176      AddrInfoOffsets = ArrayRef<uint32_t>(Swap->AddrInfoOffsets);
177    else
178      return createStringError(std::errc::invalid_argument,
179                               "failed to read address table");
180    // Read the file table.
181    const uint32_t NumFiles = Data.getU32(&Offset);
182    if (NumFiles > 0) {
183      Swap->Files.resize(NumFiles);
184      if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2))
185        Files = ArrayRef<FileEntry>(Swap->Files);
186      else
187        return createStringError(std::errc::invalid_argument,
188                                 "failed to read file table");
189    }
190    // Get the string table.
191    StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset,
192                                                Hdr->StrtabSize);
193    if (StrTab.Data.empty())
194      return createStringError(std::errc::invalid_argument,
195                               "failed to read string table");
196  }
197  return Error::success();
198
199}
200
201const Header &GsymReader::getHeader() const {
202  // The only way to get a GsymReader is from GsymReader::openFile(...) or
203  // GsymReader::copyBuffer() and the header must be valid and initialized to
204  // a valid pointer value, so the assert below should not trigger.
205  assert(Hdr);
206  return *Hdr;
207}
208
209Optional<uint64_t> GsymReader::getAddress(size_t Index) const {
210  switch (Hdr->AddrOffSize) {
211  case 1: return addressForIndex<uint8_t>(Index);
212  case 2: return addressForIndex<uint16_t>(Index);
213  case 4: return addressForIndex<uint32_t>(Index);
214  case 8: return addressForIndex<uint64_t>(Index);
215  }
216  return llvm::None;
217}
218
219Optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
220  const auto NumAddrInfoOffsets = AddrInfoOffsets.size();
221  if (Index < NumAddrInfoOffsets)
222    return AddrInfoOffsets[Index];
223  return llvm::None;
224}
225
226Expected<uint64_t>
227GsymReader::getAddressIndex(const uint64_t Addr) const {
228  if (Addr >= Hdr->BaseAddress) {
229    const uint64_t AddrOffset = Addr - Hdr->BaseAddress;
230    Optional<uint64_t> AddrOffsetIndex;
231    switch (Hdr->AddrOffSize) {
232    case 1:
233      AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);
234      break;
235    case 2:
236      AddrOffsetIndex = getAddressOffsetIndex<uint16_t>(AddrOffset);
237      break;
238    case 4:
239      AddrOffsetIndex = getAddressOffsetIndex<uint32_t>(AddrOffset);
240      break;
241    case 8:
242      AddrOffsetIndex = getAddressOffsetIndex<uint64_t>(AddrOffset);
243      break;
244    default:
245      return createStringError(std::errc::invalid_argument,
246                               "unsupported address offset size %u",
247                               Hdr->AddrOffSize);
248    }
249    if (AddrOffsetIndex)
250      return *AddrOffsetIndex;
251  }
252  return createStringError(std::errc::invalid_argument,
253                           "address 0x%" PRIx64 " is not in GSYM", Addr);
254
255}
256
257llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
258  Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
259  if (!AddressIndex)
260    return AddressIndex.takeError();
261  // Address info offsets size should have been checked in parse().
262  assert(*AddressIndex < AddrInfoOffsets.size());
263  auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
264  DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
265  if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) {
266    auto ExpectedFI = FunctionInfo::decode(Data, *OptAddr);
267    if (ExpectedFI) {
268      if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0)
269        return ExpectedFI;
270      return createStringError(std::errc::invalid_argument,
271                                "address 0x%" PRIx64 " is not in GSYM", Addr);
272    }
273  }
274  return createStringError(std::errc::invalid_argument,
275                           "failed to extract address[%" PRIu64 "]",
276                           *AddressIndex);
277}
278
279llvm::Expected<LookupResult> GsymReader::lookup(uint64_t Addr) const {
280  Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
281  if (!AddressIndex)
282    return AddressIndex.takeError();
283  // Address info offsets size should have been checked in parse().
284  assert(*AddressIndex < AddrInfoOffsets.size());
285  auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
286  DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
287  if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex))
288    return FunctionInfo::lookup(Data, *this, *OptAddr, Addr);
289  return createStringError(std::errc::invalid_argument,
290                           "failed to extract address[%" PRIu64 "]",
291                           *AddressIndex);
292}
293
294void GsymReader::dump(raw_ostream &OS) {
295  const auto &Header = getHeader();
296  // Dump the GSYM header.
297  OS << Header << "\n";
298  // Dump the address table.
299  OS << "Address Table:\n";
300  OS << "INDEX  OFFSET";
301
302  switch (Hdr->AddrOffSize) {
303  case 1: OS << "8 "; break;
304  case 2: OS << "16"; break;
305  case 4: OS << "32"; break;
306  case 8: OS << "64"; break;
307  default: OS << "??"; break;
308  }
309  OS << " (ADDRESS)\n";
310  OS << "====== =============================== \n";
311  for (uint32_t I = 0; I < Header.NumAddresses; ++I) {
312    OS << format("[%4u] ", I);
313    switch (Hdr->AddrOffSize) {
314    case 1: OS << HEX8(getAddrOffsets<uint8_t>()[I]); break;
315    case 2: OS << HEX16(getAddrOffsets<uint16_t>()[I]); break;
316    case 4: OS << HEX32(getAddrOffsets<uint32_t>()[I]); break;
317    case 8: OS << HEX32(getAddrOffsets<uint64_t>()[I]); break;
318    default: break;
319    }
320    OS << " (" << HEX64(*getAddress(I)) << ")\n";
321  }
322  // Dump the address info offsets table.
323  OS << "\nAddress Info Offsets:\n";
324  OS << "INDEX  Offset\n";
325  OS << "====== ==========\n";
326  for (uint32_t I = 0; I < Header.NumAddresses; ++I)
327    OS << format("[%4u] ", I) << HEX32(AddrInfoOffsets[I]) << "\n";
328  // Dump the file table.
329  OS << "\nFiles:\n";
330  OS << "INDEX  DIRECTORY  BASENAME   PATH\n";
331  OS << "====== ========== ========== ==============================\n";
332  for (uint32_t I = 0; I < Files.size(); ++I) {
333    OS << format("[%4u] ", I) << HEX32(Files[I].Dir) << ' '
334       << HEX32(Files[I].Base) << ' ';
335    dump(OS, getFile(I));
336    OS << "\n";
337  }
338  OS << "\n" << StrTab << "\n";
339
340  for (uint32_t I = 0; I < Header.NumAddresses; ++I) {
341    OS << "FunctionInfo @ " << HEX32(AddrInfoOffsets[I]) << ": ";
342    if (auto FI = getFunctionInfo(*getAddress(I)))
343      dump(OS, *FI);
344    else
345      logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:");
346  }
347}
348
349void GsymReader::dump(raw_ostream &OS, const FunctionInfo &FI) {
350  OS << FI.Range << " \"" << getString(FI.Name) << "\"\n";
351  if (FI.OptLineTable)
352    dump(OS, *FI.OptLineTable);
353  if (FI.Inline)
354    dump(OS, *FI.Inline);
355}
356
357void GsymReader::dump(raw_ostream &OS, const LineTable &LT) {
358  OS << "LineTable:\n";
359  for (auto &LE: LT) {
360    OS << "  " << HEX64(LE.Addr) << ' ';
361    if (LE.File)
362      dump(OS, getFile(LE.File));
363    OS << ':' << LE.Line << '\n';
364  }
365}
366
367void GsymReader::dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent) {
368  if (Indent == 0)
369    OS << "InlineInfo:\n";
370  else
371    OS.indent(Indent);
372  OS << II.Ranges << ' ' << getString(II.Name);
373  if (II.CallFile != 0) {
374    if (auto File = getFile(II.CallFile)) {
375      OS << " called from ";
376      dump(OS, File);
377      OS << ':' << II.CallLine;
378    }
379  }
380  OS << '\n';
381  for (const auto &ChildII: II.Children)
382    dump(OS, ChildII, Indent + 2);
383}
384
385void GsymReader::dump(raw_ostream &OS, Optional<FileEntry> FE) {
386  if (FE) {
387    // IF we have the file from index 0, then don't print anything
388    if (FE->Dir == 0 && FE->Base == 0)
389      return;
390    StringRef Dir = getString(FE->Dir);
391    StringRef Base = getString(FE->Base);
392    if (!Dir.empty()) {
393      OS << Dir;
394      if (Dir.contains('\\') && !Dir.contains('/'))
395        OS << '\\';
396      else
397        OS << '/';
398    }
399    if (!Base.empty()) {
400      OS << Base;
401    }
402    if (!Dir.empty() || !Base.empty())
403      return;
404  }
405  OS << "<invalid-file>";
406}
407