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