xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/GsymReader.cpp (revision a7dea1671b87c07d2d266f836bfa8b58efc7c134)
1 //===- GsymReader.cpp -----------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "llvm/DebugInfo/GSYM/GsymReader.h"
11 
12 #include <assert.h>
13 #include <inttypes.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 #include "llvm/DebugInfo/GSYM/GsymCreator.h"
18 #include "llvm/DebugInfo/GSYM/InlineInfo.h"
19 #include "llvm/DebugInfo/GSYM/LineTable.h"
20 #include "llvm/Support/BinaryStreamReader.h"
21 #include "llvm/Support/DataExtractor.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 
24 using namespace llvm;
25 using namespace gsym;
26 
27 GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) :
28     MemBuffer(std::move(Buffer)),
29     Endian(support::endian::system_endianness()) {}
30 
31   GsymReader::GsymReader(GsymReader &&RHS) = default;
32 
33 GsymReader::~GsymReader() = default;
34 
35 llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) {
36   // Open the input file and return an appropriate error if needed.
37   ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
38       MemoryBuffer::getFileOrSTDIN(Filename);
39   auto Err = BuffOrErr.getError();
40   if (Err)
41     return llvm::errorCodeToError(Err);
42   return create(BuffOrErr.get());
43 }
44 
45 llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) {
46   auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
47   return create(MemBuffer);
48 }
49 
50 llvm::Expected<llvm::gsym::GsymReader>
51 GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
52   if (!MemBuffer.get())
53     return createStringError(std::errc::invalid_argument,
54                              "invalid memory buffer");
55   GsymReader GR(std::move(MemBuffer));
56   llvm::Error Err = GR.parse();
57   if (Err)
58     return std::move(Err);
59   return std::move(GR);
60 }
61 
62 llvm::Error
63 GsymReader::parse() {
64   BinaryStreamReader FileData(MemBuffer->getBuffer(),
65                               support::endian::system_endianness());
66   // Check for the magic bytes. This file format is designed to be mmap'ed
67   // into a process and accessed as read only. This is done for performance
68   // and efficiency for symbolicating and parsing GSYM data.
69   if (FileData.readObject(Hdr))
70     return createStringError(std::errc::invalid_argument,
71                              "not enough data for a GSYM header");
72 
73   const auto HostByteOrder = support::endian::system_endianness();
74   switch (Hdr->Magic) {
75     case GSYM_MAGIC:
76       Endian = HostByteOrder;
77       break;
78     case GSYM_CIGAM:
79       // This is a GSYM file, but not native endianness.
80       Endian = sys::IsBigEndianHost ? support::little : support::big;
81       Swap.reset(new SwappedData);
82       break;
83     default:
84       return createStringError(std::errc::invalid_argument,
85                                "not a GSYM file");
86   }
87 
88   bool DataIsLittleEndian = HostByteOrder != support::little;
89   // Read a correctly byte swapped header if we need to.
90   if (Swap) {
91     DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
92     if (auto ExpectedHdr = Header::decode(Data))
93       Swap->Hdr = ExpectedHdr.get();
94     else
95       return ExpectedHdr.takeError();
96     Hdr = &Swap->Hdr;
97   }
98 
99   // Detect errors in the header and report any that are found. If we make it
100   // past this without errors, we know we have a good magic value, a supported
101   // version number, verified address offset size and a valid UUID size.
102   if (Error Err = Hdr->checkForError())
103     return Err;
104 
105   if (!Swap) {
106     // This is the native endianness case that is most common and optimized for
107     // efficient lookups. Here we just grab pointers to the native data and
108     // use ArrayRef objects to allow efficient read only access.
109 
110     // Read the address offsets.
111     if (FileData.padToAlignment(Hdr->AddrOffSize) ||
112         FileData.readArray(AddrOffsets,
113                            Hdr->NumAddresses * Hdr->AddrOffSize))
114       return createStringError(std::errc::invalid_argument,
115                               "failed to read address table");
116 
117     // Read the address info offsets.
118     if (FileData.padToAlignment(4) ||
119         FileData.readArray(AddrInfoOffsets, Hdr->NumAddresses))
120       return createStringError(std::errc::invalid_argument,
121                               "failed to read address info offsets table");
122 
123     // Read the file table.
124     uint32_t NumFiles = 0;
125     if (FileData.readInteger(NumFiles) || FileData.readArray(Files, NumFiles))
126       return createStringError(std::errc::invalid_argument,
127                               "failed to read file table");
128 
129     // Get the string table.
130     FileData.setOffset(Hdr->StrtabOffset);
131     if (FileData.readFixedString(StrTab.Data, Hdr->StrtabSize))
132       return createStringError(std::errc::invalid_argument,
133                               "failed to read string table");
134 } else {
135   // This is the non native endianness case that is not common and not
136   // optimized for lookups. Here we decode the important tables into local
137   // storage and then set the ArrayRef objects to point to these swapped
138   // copies of the read only data so lookups can be as efficient as possible.
139   DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
140 
141   // Read the address offsets.
142   uint64_t Offset = alignTo(sizeof(Header), Hdr->AddrOffSize);
143   Swap->AddrOffsets.resize(Hdr->NumAddresses * Hdr->AddrOffSize);
144   switch (Hdr->AddrOffSize) {
145     case 1:
146       if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses))
147         return createStringError(std::errc::invalid_argument,
148                                   "failed to read address table");
149       break;
150     case 2:
151       if (!Data.getU16(&Offset,
152                         reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()),
153                         Hdr->NumAddresses))
154         return createStringError(std::errc::invalid_argument,
155                                   "failed to read address table");
156       break;
157     case 4:
158       if (!Data.getU32(&Offset,
159                         reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()),
160                         Hdr->NumAddresses))
161         return createStringError(std::errc::invalid_argument,
162                                   "failed to read address table");
163       break;
164     case 8:
165       if (!Data.getU64(&Offset,
166                         reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()),
167                         Hdr->NumAddresses))
168         return createStringError(std::errc::invalid_argument,
169                                   "failed to read address table");
170     }
171     AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets);
172 
173     // Read the address info offsets.
174     Offset = alignTo(Offset, 4);
175     Swap->AddrInfoOffsets.resize(Hdr->NumAddresses);
176     if (Data.getU32(&Offset, Swap->AddrInfoOffsets.data(), Hdr->NumAddresses))
177       AddrInfoOffsets = ArrayRef<uint32_t>(Swap->AddrInfoOffsets);
178     else
179       return createStringError(std::errc::invalid_argument,
180                                "failed to read address table");
181     // Read the file table.
182     const uint32_t NumFiles = Data.getU32(&Offset);
183     if (NumFiles > 0) {
184       Swap->Files.resize(NumFiles);
185       if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2))
186         Files = ArrayRef<FileEntry>(Swap->Files);
187       else
188         return createStringError(std::errc::invalid_argument,
189                                  "failed to read file table");
190     }
191     // Get the string table.
192     StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset,
193                                                 Hdr->StrtabSize);
194     if (StrTab.Data.empty())
195       return createStringError(std::errc::invalid_argument,
196                                "failed to read string table");
197   }
198   return Error::success();
199 
200 }
201 
202 const Header &GsymReader::getHeader() const {
203   // The only way to get a GsymReader is from GsymReader::openFile(...) or
204   // GsymReader::copyBuffer() and the header must be valid and initialized to
205   // a valid pointer value, so the assert below should not trigger.
206   assert(Hdr);
207   return *Hdr;
208 }
209 
210 Optional<uint64_t> GsymReader::getAddress(size_t Index) const {
211   switch (Hdr->AddrOffSize) {
212   case 1: return addressForIndex<uint8_t>(Index);
213   case 2: return addressForIndex<uint16_t>(Index);
214   case 4: return addressForIndex<uint32_t>(Index);
215   case 8: return addressForIndex<uint64_t>(Index);
216   }
217   return llvm::None;
218 }
219 
220 Optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
221   const auto NumAddrInfoOffsets = AddrInfoOffsets.size();
222   if (Index < NumAddrInfoOffsets)
223     return AddrInfoOffsets[Index];
224   return llvm::None;
225 }
226 
227 Expected<uint64_t>
228 GsymReader::getAddressIndex(const uint64_t Addr) const {
229   if (Addr < Hdr->BaseAddress)
230     return createStringError(std::errc::invalid_argument,
231                              "address 0x%" PRIx64 " not in GSYM", Addr);
232   const uint64_t AddrOffset = Addr - Hdr->BaseAddress;
233   switch (Hdr->AddrOffSize) {
234   case 1: return getAddressOffsetIndex<uint8_t>(AddrOffset);
235   case 2: return getAddressOffsetIndex<uint16_t>(AddrOffset);
236   case 4: return getAddressOffsetIndex<uint32_t>(AddrOffset);
237   case 8: return getAddressOffsetIndex<uint64_t>(AddrOffset);
238   default: break;
239   }
240   return createStringError(std::errc::invalid_argument,
241                            "unsupported address offset size %u",
242                            Hdr->AddrOffSize);
243 }
244 
245 llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
246   Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
247   if (!AddressIndex)
248     return AddressIndex.takeError();
249   // Address info offsets size should have been checked in parse().
250   assert(*AddressIndex < AddrInfoOffsets.size());
251   auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
252   DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
253   if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) {
254     auto ExpectedFI = FunctionInfo::decode(Data, *OptAddr);
255     if (ExpectedFI) {
256       if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0)
257         return ExpectedFI;
258       return createStringError(std::errc::invalid_argument,
259                                 "address 0x%" PRIx64 " not in GSYM", Addr);
260     }
261   }
262   return createStringError(std::errc::invalid_argument,
263                            "failed to extract address[%" PRIu64 "]",
264                            *AddressIndex);
265 }
266