| //===- FunctionInfo.cpp ---------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/GSYM/FunctionInfo.h" |
| #include "llvm/DebugInfo/GSYM/FileWriter.h" |
| #include "llvm/DebugInfo/GSYM/GsymReader.h" |
| #include "llvm/DebugInfo/GSYM/LineTable.h" |
| #include "llvm/DebugInfo/GSYM/InlineInfo.h" |
| #include "llvm/Support/DataExtractor.h" |
| |
| using namespace llvm; |
| using namespace gsym; |
| |
| /// FunctionInfo information type that is used to encode the optional data |
| /// that is associated with a FunctionInfo object. |
| enum InfoType : uint32_t { |
| EndOfList = 0u, |
| LineTableInfo = 1u, |
| InlineInfo = 2u |
| }; |
| |
| raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) { |
| OS << '[' << HEX64(FI.Range.Start) << '-' << HEX64(FI.Range.End) << "): " |
| << "Name=" << HEX32(FI.Name) << '\n' << FI.OptLineTable << FI.Inline; |
| return OS; |
| } |
| |
| llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data, |
| uint64_t BaseAddr) { |
| FunctionInfo FI; |
| FI.Range.Start = BaseAddr; |
| uint64_t Offset = 0; |
| if (!Data.isValidOffsetForDataOfSize(Offset, 4)) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset); |
| FI.Range.End = FI.Range.Start + Data.getU32(&Offset); |
| if (!Data.isValidOffsetForDataOfSize(Offset, 4)) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset); |
| FI.Name = Data.getU32(&Offset); |
| if (FI.Name == 0) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x", |
| Offset - 4, FI.Name); |
| bool Done = false; |
| while (!Done) { |
| if (!Data.isValidOffsetForDataOfSize(Offset, 4)) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset); |
| const uint32_t IT = Data.getU32(&Offset); |
| if (!Data.isValidOffsetForDataOfSize(Offset, 4)) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset); |
| const uint32_t InfoLength = Data.getU32(&Offset); |
| if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength)) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u", |
| Offset, IT); |
| DataExtractor InfoData(Data.getData().substr(Offset, InfoLength), |
| Data.isLittleEndian(), |
| Data.getAddressSize()); |
| switch (IT) { |
| case InfoType::EndOfList: |
| Done = true; |
| break; |
| |
| case InfoType::LineTableInfo: |
| if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr)) |
| FI.OptLineTable = std::move(LT.get()); |
| else |
| return LT.takeError(); |
| break; |
| |
| case InfoType::InlineInfo: |
| if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr)) |
| FI.Inline = std::move(II.get()); |
| else |
| return II.takeError(); |
| break; |
| |
| default: |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": unsupported InfoType %u", |
| Offset-8, IT); |
| } |
| Offset += InfoLength; |
| } |
| return std::move(FI); |
| } |
| |
| llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { |
| if (!isValid()) |
| return createStringError(std::errc::invalid_argument, |
| "attempted to encode invalid FunctionInfo object"); |
| // Align FunctionInfo data to a 4 byte alignment. |
| O.alignTo(4); |
| const uint64_t FuncInfoOffset = O.tell(); |
| // Write the size in bytes of this function as a uint32_t. This can be zero |
| // if we just have a symbol from a symbol table and that symbol has no size. |
| O.writeU32(size()); |
| // Write the name of this function as a uint32_t string table offset. |
| O.writeU32(Name); |
| |
| if (OptLineTable.hasValue()) { |
| O.writeU32(InfoType::LineTableInfo); |
| // Write a uint32_t length as zero for now, we will fix this up after |
| // writing the LineTable out with the number of bytes that were written. |
| O.writeU32(0); |
| const auto StartOffset = O.tell(); |
| llvm::Error err = OptLineTable->encode(O, Range.Start); |
| if (err) |
| return std::move(err); |
| const auto Length = O.tell() - StartOffset; |
| if (Length > UINT32_MAX) |
| return createStringError(std::errc::invalid_argument, |
| "LineTable length is greater than UINT32_MAX"); |
| // Fixup the size of the LineTable data with the correct size. |
| O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4); |
| } |
| |
| // Write out the inline function info if we have any and if it is valid. |
| if (Inline.hasValue()) { |
| O.writeU32(InfoType::InlineInfo); |
| // Write a uint32_t length as zero for now, we will fix this up after |
| // writing the LineTable out with the number of bytes that were written. |
| O.writeU32(0); |
| const auto StartOffset = O.tell(); |
| llvm::Error err = Inline->encode(O, Range.Start); |
| if (err) |
| return std::move(err); |
| const auto Length = O.tell() - StartOffset; |
| if (Length > UINT32_MAX) |
| return createStringError(std::errc::invalid_argument, |
| "InlineInfo length is greater than UINT32_MAX"); |
| // Fixup the size of the InlineInfo data with the correct size. |
| O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4); |
| } |
| |
| // Terminate the data chunks with and end of list with zero size |
| O.writeU32(InfoType::EndOfList); |
| O.writeU32(0); |
| return FuncInfoOffset; |
| } |
| |
| |
| llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data, |
| const GsymReader &GR, |
| uint64_t FuncAddr, |
| uint64_t Addr) { |
| LookupResult LR; |
| LR.LookupAddr = Addr; |
| LR.FuncRange.Start = FuncAddr; |
| uint64_t Offset = 0; |
| LR.FuncRange.End = FuncAddr + Data.getU32(&Offset); |
| uint32_t NameOffset = Data.getU32(&Offset); |
| // The "lookup" functions doesn't report errors as accurately as the "decode" |
| // function as it is meant to be fast. For more accurage errors we could call |
| // "decode". |
| if (!Data.isValidOffset(Offset)) |
| return createStringError(std::errc::io_error, |
| "FunctionInfo data is truncated"); |
| // This function will be called with the result of a binary search of the |
| // address table, we must still make sure the address does not fall into a |
| // gap between functions are after the last function. |
| if (Addr >= LR.FuncRange.End) |
| return createStringError(std::errc::io_error, |
| "address 0x%" PRIx64 " is not in GSYM", Addr); |
| |
| if (NameOffset == 0) |
| return createStringError(std::errc::io_error, |
| "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000", |
| Offset - 4); |
| LR.FuncName = GR.getString(NameOffset); |
| bool Done = false; |
| Optional<LineEntry> LineEntry; |
| Optional<DataExtractor> InlineInfoData; |
| while (!Done) { |
| if (!Data.isValidOffsetForDataOfSize(Offset, 8)) |
| return createStringError(std::errc::io_error, |
| "FunctionInfo data is truncated"); |
| const uint32_t IT = Data.getU32(&Offset); |
| const uint32_t InfoLength = Data.getU32(&Offset); |
| const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength); |
| if (InfoLength != InfoBytes.size()) |
| return createStringError(std::errc::io_error, |
| "FunctionInfo data is truncated"); |
| DataExtractor InfoData(InfoBytes, Data.isLittleEndian(), |
| Data.getAddressSize()); |
| switch (IT) { |
| case InfoType::EndOfList: |
| Done = true; |
| break; |
| |
| case InfoType::LineTableInfo: |
| if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr)) |
| LineEntry = ExpectedLE.get(); |
| else |
| return ExpectedLE.takeError(); |
| break; |
| |
| case InfoType::InlineInfo: |
| // We will parse the inline info after our line table, but only if |
| // we have a line entry. |
| InlineInfoData = InfoData; |
| break; |
| |
| default: |
| break; |
| } |
| Offset += InfoLength; |
| } |
| |
| if (!LineEntry) { |
| // We don't have a valid line entry for our address, fill in our source |
| // location as best we can and return. |
| SourceLocation SrcLoc; |
| SrcLoc.Name = LR.FuncName; |
| LR.Locations.push_back(SrcLoc); |
| return LR; |
| } |
| |
| Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File); |
| if (!LineEntryFile) |
| return createStringError(std::errc::invalid_argument, |
| "failed to extract file[%" PRIu32 "]", |
| LineEntry->File); |
| |
| SourceLocation SrcLoc; |
| SrcLoc.Name = LR.FuncName; |
| SrcLoc.Dir = GR.getString(LineEntryFile->Dir); |
| SrcLoc.Base = GR.getString(LineEntryFile->Base); |
| SrcLoc.Line = LineEntry->Line; |
| LR.Locations.push_back(SrcLoc); |
| // If we don't have inline information, we are done. |
| if (!InlineInfoData) |
| return LR; |
| // We have inline information. Try to augment the lookup result with this |
| // data. |
| llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr, |
| LR.Locations); |
| if (Err) |
| return std::move(Err); |
| return LR; |
| } |