1360784Sdim//===- FunctionInfo.cpp ---------------------------------------------------===// 2351278Sdim// 3360784Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4360784Sdim// See https://llvm.org/LICENSE.txt for license information. 5360784Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6351278Sdim// 7351278Sdim//===----------------------------------------------------------------------===// 8351278Sdim 9351278Sdim#include "llvm/DebugInfo/GSYM/FunctionInfo.h" 10360784Sdim#include "llvm/DebugInfo/GSYM/FileWriter.h" 11360784Sdim#include "llvm/DebugInfo/GSYM/GsymReader.h" 12360784Sdim#include "llvm/DebugInfo/GSYM/LineTable.h" 13360784Sdim#include "llvm/DebugInfo/GSYM/InlineInfo.h" 14360784Sdim#include "llvm/Support/DataExtractor.h" 15351278Sdim 16351278Sdimusing namespace llvm; 17351278Sdimusing namespace gsym; 18351278Sdim 19360784Sdim/// FunctionInfo information type that is used to encode the optional data 20360784Sdim/// that is associated with a FunctionInfo object. 21360784Sdimenum InfoType : uint32_t { 22360784Sdim EndOfList = 0u, 23360784Sdim LineTableInfo = 1u, 24360784Sdim InlineInfo = 2u 25360784Sdim}; 26360784Sdim 27351278Sdimraw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) { 28351278Sdim OS << '[' << HEX64(FI.Range.Start) << '-' << HEX64(FI.Range.End) << "): " 29360784Sdim << "Name=" << HEX32(FI.Name) << '\n' << FI.OptLineTable << FI.Inline; 30351278Sdim return OS; 31351278Sdim} 32360784Sdim 33360784Sdimllvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data, 34360784Sdim uint64_t BaseAddr) { 35360784Sdim FunctionInfo FI; 36360784Sdim FI.Range.Start = BaseAddr; 37360784Sdim uint64_t Offset = 0; 38360784Sdim if (!Data.isValidOffsetForDataOfSize(Offset, 4)) 39360784Sdim return createStringError(std::errc::io_error, 40360784Sdim "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset); 41360784Sdim FI.Range.End = FI.Range.Start + Data.getU32(&Offset); 42360784Sdim if (!Data.isValidOffsetForDataOfSize(Offset, 4)) 43360784Sdim return createStringError(std::errc::io_error, 44360784Sdim "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset); 45360784Sdim FI.Name = Data.getU32(&Offset); 46360784Sdim if (FI.Name == 0) 47360784Sdim return createStringError(std::errc::io_error, 48360784Sdim "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x", 49360784Sdim Offset - 4, FI.Name); 50360784Sdim bool Done = false; 51360784Sdim while (!Done) { 52360784Sdim if (!Data.isValidOffsetForDataOfSize(Offset, 4)) 53360784Sdim return createStringError(std::errc::io_error, 54360784Sdim "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset); 55360784Sdim const uint32_t IT = Data.getU32(&Offset); 56360784Sdim if (!Data.isValidOffsetForDataOfSize(Offset, 4)) 57360784Sdim return createStringError(std::errc::io_error, 58360784Sdim "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset); 59360784Sdim const uint32_t InfoLength = Data.getU32(&Offset); 60360784Sdim if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength)) 61360784Sdim return createStringError(std::errc::io_error, 62360784Sdim "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u", 63360784Sdim Offset, IT); 64360784Sdim DataExtractor InfoData(Data.getData().substr(Offset, InfoLength), 65360784Sdim Data.isLittleEndian(), 66360784Sdim Data.getAddressSize()); 67360784Sdim switch (IT) { 68360784Sdim case InfoType::EndOfList: 69360784Sdim Done = true; 70360784Sdim break; 71360784Sdim 72360784Sdim case InfoType::LineTableInfo: 73360784Sdim if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr)) 74360784Sdim FI.OptLineTable = std::move(LT.get()); 75360784Sdim else 76360784Sdim return LT.takeError(); 77360784Sdim break; 78360784Sdim 79360784Sdim case InfoType::InlineInfo: 80360784Sdim if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr)) 81360784Sdim FI.Inline = std::move(II.get()); 82360784Sdim else 83360784Sdim return II.takeError(); 84360784Sdim break; 85360784Sdim 86360784Sdim default: 87360784Sdim return createStringError(std::errc::io_error, 88360784Sdim "0x%8.8" PRIx64 ": unsupported InfoType %u", 89360784Sdim Offset-8, IT); 90360784Sdim } 91360784Sdim Offset += InfoLength; 92360784Sdim } 93360784Sdim return std::move(FI); 94360784Sdim} 95360784Sdim 96360784Sdimllvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { 97360784Sdim if (!isValid()) 98360784Sdim return createStringError(std::errc::invalid_argument, 99360784Sdim "attempted to encode invalid FunctionInfo object"); 100360784Sdim // Align FunctionInfo data to a 4 byte alignment. 101360784Sdim O.alignTo(4); 102360784Sdim const uint64_t FuncInfoOffset = O.tell(); 103360784Sdim // Write the size in bytes of this function as a uint32_t. This can be zero 104360784Sdim // if we just have a symbol from a symbol table and that symbol has no size. 105360784Sdim O.writeU32(size()); 106360784Sdim // Write the name of this function as a uint32_t string table offset. 107360784Sdim O.writeU32(Name); 108360784Sdim 109360784Sdim if (OptLineTable.hasValue()) { 110360784Sdim O.writeU32(InfoType::LineTableInfo); 111360784Sdim // Write a uint32_t length as zero for now, we will fix this up after 112360784Sdim // writing the LineTable out with the number of bytes that were written. 113360784Sdim O.writeU32(0); 114360784Sdim const auto StartOffset = O.tell(); 115360784Sdim llvm::Error err = OptLineTable->encode(O, Range.Start); 116360784Sdim if (err) 117360784Sdim return std::move(err); 118360784Sdim const auto Length = O.tell() - StartOffset; 119360784Sdim if (Length > UINT32_MAX) 120360784Sdim return createStringError(std::errc::invalid_argument, 121360784Sdim "LineTable length is greater than UINT32_MAX"); 122360784Sdim // Fixup the size of the LineTable data with the correct size. 123360784Sdim O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4); 124360784Sdim } 125360784Sdim 126360784Sdim // Write out the inline function info if we have any and if it is valid. 127360784Sdim if (Inline.hasValue()) { 128360784Sdim O.writeU32(InfoType::InlineInfo); 129360784Sdim // Write a uint32_t length as zero for now, we will fix this up after 130360784Sdim // writing the LineTable out with the number of bytes that were written. 131360784Sdim O.writeU32(0); 132360784Sdim const auto StartOffset = O.tell(); 133360784Sdim llvm::Error err = Inline->encode(O, Range.Start); 134360784Sdim if (err) 135360784Sdim return std::move(err); 136360784Sdim const auto Length = O.tell() - StartOffset; 137360784Sdim if (Length > UINT32_MAX) 138360784Sdim return createStringError(std::errc::invalid_argument, 139360784Sdim "InlineInfo length is greater than UINT32_MAX"); 140360784Sdim // Fixup the size of the InlineInfo data with the correct size. 141360784Sdim O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4); 142360784Sdim } 143360784Sdim 144360784Sdim // Terminate the data chunks with and end of list with zero size 145360784Sdim O.writeU32(InfoType::EndOfList); 146360784Sdim O.writeU32(0); 147360784Sdim return FuncInfoOffset; 148360784Sdim} 149360784Sdim 150360784Sdim 151360784Sdimllvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data, 152360784Sdim const GsymReader &GR, 153360784Sdim uint64_t FuncAddr, 154360784Sdim uint64_t Addr) { 155360784Sdim LookupResult LR; 156360784Sdim LR.LookupAddr = Addr; 157360784Sdim LR.FuncRange.Start = FuncAddr; 158360784Sdim uint64_t Offset = 0; 159360784Sdim LR.FuncRange.End = FuncAddr + Data.getU32(&Offset); 160360784Sdim uint32_t NameOffset = Data.getU32(&Offset); 161360784Sdim // The "lookup" functions doesn't report errors as accurately as the "decode" 162360784Sdim // function as it is meant to be fast. For more accurage errors we could call 163360784Sdim // "decode". 164360784Sdim if (!Data.isValidOffset(Offset)) 165360784Sdim return createStringError(std::errc::io_error, 166360784Sdim "FunctionInfo data is truncated"); 167360784Sdim // This function will be called with the result of a binary search of the 168360784Sdim // address table, we must still make sure the address does not fall into a 169360784Sdim // gap between functions are after the last function. 170360784Sdim if (Addr >= LR.FuncRange.End) 171360784Sdim return createStringError(std::errc::io_error, 172360784Sdim "address 0x%" PRIx64 " is not in GSYM", Addr); 173360784Sdim 174360784Sdim if (NameOffset == 0) 175360784Sdim return createStringError(std::errc::io_error, 176360784Sdim "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000", 177360784Sdim Offset - 4); 178360784Sdim LR.FuncName = GR.getString(NameOffset); 179360784Sdim bool Done = false; 180360784Sdim Optional<LineEntry> LineEntry; 181360784Sdim Optional<DataExtractor> InlineInfoData; 182360784Sdim while (!Done) { 183360784Sdim if (!Data.isValidOffsetForDataOfSize(Offset, 8)) 184360784Sdim return createStringError(std::errc::io_error, 185360784Sdim "FunctionInfo data is truncated"); 186360784Sdim const uint32_t IT = Data.getU32(&Offset); 187360784Sdim const uint32_t InfoLength = Data.getU32(&Offset); 188360784Sdim const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength); 189360784Sdim if (InfoLength != InfoBytes.size()) 190360784Sdim return createStringError(std::errc::io_error, 191360784Sdim "FunctionInfo data is truncated"); 192360784Sdim DataExtractor InfoData(InfoBytes, Data.isLittleEndian(), 193360784Sdim Data.getAddressSize()); 194360784Sdim switch (IT) { 195360784Sdim case InfoType::EndOfList: 196360784Sdim Done = true; 197360784Sdim break; 198360784Sdim 199360784Sdim case InfoType::LineTableInfo: 200360784Sdim if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr)) 201360784Sdim LineEntry = ExpectedLE.get(); 202360784Sdim else 203360784Sdim return ExpectedLE.takeError(); 204360784Sdim break; 205360784Sdim 206360784Sdim case InfoType::InlineInfo: 207360784Sdim // We will parse the inline info after our line table, but only if 208360784Sdim // we have a line entry. 209360784Sdim InlineInfoData = InfoData; 210360784Sdim break; 211360784Sdim 212360784Sdim default: 213360784Sdim break; 214360784Sdim } 215360784Sdim Offset += InfoLength; 216360784Sdim } 217360784Sdim 218360784Sdim if (!LineEntry) { 219360784Sdim // We don't have a valid line entry for our address, fill in our source 220360784Sdim // location as best we can and return. 221360784Sdim SourceLocation SrcLoc; 222360784Sdim SrcLoc.Name = LR.FuncName; 223360784Sdim LR.Locations.push_back(SrcLoc); 224360784Sdim return LR; 225360784Sdim } 226360784Sdim 227360784Sdim Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File); 228360784Sdim if (!LineEntryFile) 229360784Sdim return createStringError(std::errc::invalid_argument, 230360784Sdim "failed to extract file[%" PRIu32 "]", 231360784Sdim LineEntry->File); 232360784Sdim 233360784Sdim SourceLocation SrcLoc; 234360784Sdim SrcLoc.Name = LR.FuncName; 235360784Sdim SrcLoc.Dir = GR.getString(LineEntryFile->Dir); 236360784Sdim SrcLoc.Base = GR.getString(LineEntryFile->Base); 237360784Sdim SrcLoc.Line = LineEntry->Line; 238360784Sdim LR.Locations.push_back(SrcLoc); 239360784Sdim // If we don't have inline information, we are done. 240360784Sdim if (!InlineInfoData) 241360784Sdim return LR; 242360784Sdim // We have inline information. Try to augment the lookup result with this 243360784Sdim // data. 244360784Sdim llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr, 245360784Sdim LR.Locations); 246360784Sdim if (Err) 247360784Sdim return std::move(Err); 248360784Sdim return LR; 249360784Sdim} 250