1 //===- GsymReader.cpp -----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/GSYM/GsymReader.h"
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"
26 GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) :
27 MemBuffer(std::move(Buffer)),
28 Endian(support::endian::system_endianness()) {}
30 GsymReader::GsymReader(GsymReader &&RHS) = default;
32 GsymReader::~GsymReader() = default;
34 llvm::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();
40 return llvm::errorCodeToError(Err);
41 return create(BuffOrErr.get());
44 llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) {
45 auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
46 return create(MemBuffer);
49 llvm::Expected<llvm::gsym::GsymReader>
50 GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
52 return createStringError(std::errc::invalid_argument,
53 "invalid memory buffer");
54 GsymReader GR(std::move(MemBuffer));
55 llvm::Error Err = GR.parse();
57 return std::move(Err);
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");
72 const auto HostByteOrder = support::endian::system_endianness();
75 Endian = HostByteOrder;
78 // This is a GSYM file, but not native endianness.
79 Endian = sys::IsBigEndianHost ? support::little : support::big;
80 Swap.reset(new SwappedData);
83 return createStringError(std::errc::invalid_argument,
87 bool DataIsLittleEndian = HostByteOrder != support::little;
88 // Read a correctly byte swapped header if we need to.
90 DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
91 if (auto ExpectedHdr = Header::decode(Data))
92 Swap->Hdr = ExpectedHdr.get();
94 return ExpectedHdr.takeError();
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())
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.
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");
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");
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");
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");
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);
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) {
145 if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses))
146 return createStringError(std::errc::invalid_argument,
147 "failed to read address table");
150 if (!Data.getU16(&Offset,
151 reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()),
153 return createStringError(std::errc::invalid_argument,
154 "failed to read address table");
157 if (!Data.getU32(&Offset,
158 reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()),
160 return createStringError(std::errc::invalid_argument,
161 "failed to read address table");
164 if (!Data.getU64(&Offset,
165 reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()),
167 return createStringError(std::errc::invalid_argument,
168 "failed to read address table");
170 AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets);
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);
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);
183 Swap->Files.resize(NumFiles);
184 if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2))
185 Files = ArrayRef<FileEntry>(Swap->Files);
187 return createStringError(std::errc::invalid_argument,
188 "failed to read file table");
190 // Get the string table.
191 StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset,
193 if (StrTab.Data.empty())
194 return createStringError(std::errc::invalid_argument,
195 "failed to read string table");
197 return Error::success();
201 const 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.
209 Optional<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);
219 Optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
220 const auto NumAddrInfoOffsets = AddrInfoOffsets.size();
221 if (Index < NumAddrInfoOffsets)
222 return AddrInfoOffsets[Index];
227 GsymReader::getAddressIndex(const uint64_t Addr) const {
228 if (Addr < Hdr->BaseAddress)
229 return createStringError(std::errc::invalid_argument,
230 "address 0x%" PRIx64 " not in GSYM", Addr);
231 const uint64_t AddrOffset = Addr - Hdr->BaseAddress;
232 switch (Hdr->AddrOffSize) {
233 case 1: return getAddressOffsetIndex<uint8_t>(AddrOffset);
234 case 2: return getAddressOffsetIndex<uint16_t>(AddrOffset);
235 case 4: return getAddressOffsetIndex<uint32_t>(AddrOffset);
236 case 8: return getAddressOffsetIndex<uint64_t>(AddrOffset);
239 return createStringError(std::errc::invalid_argument,
240 "unsupported address offset size %u",
244 llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
245 Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
247 return AddressIndex.takeError();
248 // Address info offsets size should have been checked in parse().
249 assert(*AddressIndex < AddrInfoOffsets.size());
250 auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
251 DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
252 if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) {
253 auto ExpectedFI = FunctionInfo::decode(Data, *OptAddr);
255 if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0)
257 return createStringError(std::errc::invalid_argument,
258 "address 0x%" PRIx64 " not in GSYM", Addr);
261 return createStringError(std::errc::invalid_argument,
262 "failed to extract address[%" PRIu64 "]",
266 llvm::Expected<LookupResult> GsymReader::lookup(uint64_t Addr) const {
267 Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
269 return AddressIndex.takeError();
270 // Address info offsets size should have been checked in parse().
271 assert(*AddressIndex < AddrInfoOffsets.size());
272 auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
273 DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
274 if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex))
275 return FunctionInfo::lookup(Data, *this, *OptAddr, Addr);
276 return createStringError(std::errc::invalid_argument,
277 "failed to extract address[%" PRIu64 "]",