1 //===- GsymReader.cpp -----------------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "llvm/DebugInfo/GSYM/GsymReader.h"
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"
27 GsymReader::GsymReader(std::unique_ptr
<MemoryBuffer
> Buffer
) :
28 MemBuffer(std::move(Buffer
)),
29 Endian(support::endian::system_endianness()) {}
31 GsymReader::GsymReader(GsymReader
&&RHS
) = default;
33 GsymReader::~GsymReader() = default;
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();
41 return llvm::errorCodeToError(Err
);
42 return create(BuffOrErr
.get());
45 llvm::Expected
<GsymReader
> GsymReader::copyBuffer(StringRef Bytes
) {
46 auto MemBuffer
= MemoryBuffer::getMemBufferCopy(Bytes
, "GSYM bytes");
47 return create(MemBuffer
);
50 llvm::Expected
<llvm::gsym::GsymReader
>
51 GsymReader::create(std::unique_ptr
<MemoryBuffer
> &MemBuffer
) {
53 return createStringError(std::errc::invalid_argument
,
54 "invalid memory buffer");
55 GsymReader
GR(std::move(MemBuffer
));
56 llvm::Error Err
= GR
.parse();
58 return std::move(Err
);
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");
73 const auto HostByteOrder
= support::endian::system_endianness();
76 Endian
= HostByteOrder
;
79 // This is a GSYM file, but not native endianness.
80 Endian
= sys::IsBigEndianHost
? support::little
: support::big
;
81 Swap
.reset(new SwappedData
);
84 return createStringError(std::errc::invalid_argument
,
88 bool DataIsLittleEndian
= HostByteOrder
!= support::little
;
89 // Read a correctly byte swapped header if we need to.
91 DataExtractor
Data(MemBuffer
->getBuffer(), DataIsLittleEndian
, 4);
92 if (auto ExpectedHdr
= Header::decode(Data
))
93 Swap
->Hdr
= ExpectedHdr
.get();
95 return ExpectedHdr
.takeError();
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())
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.
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");
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");
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");
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");
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);
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
) {
146 if (!Data
.getU8(&Offset
, Swap
->AddrOffsets
.data(), Hdr
->NumAddresses
))
147 return createStringError(std::errc::invalid_argument
,
148 "failed to read address table");
151 if (!Data
.getU16(&Offset
,
152 reinterpret_cast<uint16_t *>(Swap
->AddrOffsets
.data()),
154 return createStringError(std::errc::invalid_argument
,
155 "failed to read address table");
158 if (!Data
.getU32(&Offset
,
159 reinterpret_cast<uint32_t *>(Swap
->AddrOffsets
.data()),
161 return createStringError(std::errc::invalid_argument
,
162 "failed to read address table");
165 if (!Data
.getU64(&Offset
,
166 reinterpret_cast<uint64_t *>(Swap
->AddrOffsets
.data()),
168 return createStringError(std::errc::invalid_argument
,
169 "failed to read address table");
171 AddrOffsets
= ArrayRef
<uint8_t>(Swap
->AddrOffsets
);
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
);
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
);
184 Swap
->Files
.resize(NumFiles
);
185 if (Data
.getU32(&Offset
, &Swap
->Files
[0].Dir
, NumFiles
*2))
186 Files
= ArrayRef
<FileEntry
>(Swap
->Files
);
188 return createStringError(std::errc::invalid_argument
,
189 "failed to read file table");
191 // Get the string table.
192 StrTab
.Data
= MemBuffer
->getBuffer().substr(Hdr
->StrtabOffset
,
194 if (StrTab
.Data
.empty())
195 return createStringError(std::errc::invalid_argument
,
196 "failed to read string table");
198 return Error::success();
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.
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
);
220 Optional
<uint64_t> GsymReader::getAddressInfoOffset(size_t Index
) const {
221 const auto NumAddrInfoOffsets
= AddrInfoOffsets
.size();
222 if (Index
< NumAddrInfoOffsets
)
223 return AddrInfoOffsets
[Index
];
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
);
240 return createStringError(std::errc::invalid_argument
,
241 "unsupported address offset size %u",
245 llvm::Expected
<FunctionInfo
> GsymReader::getFunctionInfo(uint64_t Addr
) const {
246 Expected
<uint64_t> AddressIndex
= getAddressIndex(Addr
);
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
);
256 if (ExpectedFI
->Range
.contains(Addr
) || ExpectedFI
->Range
.size() == 0)
258 return createStringError(std::errc::invalid_argument
,
259 "address 0x%" PRIx64
" not in GSYM", Addr
);
262 return createStringError(std::errc::invalid_argument
,
263 "failed to extract address[%" PRIu64
"]",