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