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 23 using namespace llvm; 24 using namespace gsym; 25 26 GsymReader::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 32 GsymReader::~GsymReader() = default; 33 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(); 39 if (Err) 40 return llvm::errorCodeToError(Err); 41 return create(BuffOrErr.get()); 42 } 43 44 llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) { 45 auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes"); 46 return create(MemBuffer); 47 } 48 49 llvm::Expected<llvm::gsym::GsymReader> 50 GsymReader::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 61 llvm::Error 62 GsymReader::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 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. 205 assert(Hdr); 206 return *Hdr; 207 } 208 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); 215 } 216 return llvm::None; 217 } 218 219 Optional<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 226 Expected<uint64_t> 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); 237 default: break; 238 } 239 return createStringError(std::errc::invalid_argument, 240 "unsupported address offset size %u", 241 Hdr->AddrOffSize); 242 } 243 244 llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const { 245 Expected<uint64_t> AddressIndex = getAddressIndex(Addr); 246 if (!AddressIndex) 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); 254 if (ExpectedFI) { 255 if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0) 256 return ExpectedFI; 257 return createStringError(std::errc::invalid_argument, 258 "address 0x%" PRIx64 " not in GSYM", Addr); 259 } 260 } 261 return createStringError(std::errc::invalid_argument, 262 "failed to extract address[%" PRIu64 "]", 263 *AddressIndex); 264 } 265 266 llvm::Expected<LookupResult> GsymReader::lookup(uint64_t Addr) const { 267 Expected<uint64_t> AddressIndex = getAddressIndex(Addr); 268 if (!AddressIndex) 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 "]", 278 *AddressIndex); 279 } 280