1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
|
//===- 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 << FI.Range << ": " << "Name=" << HEX32(FI.Name) << '\n';
if (FI.OptLineTable)
OS << FI.OptLineTable << '\n';
if (FI.Inline)
OS << FI.Inline << '\n';
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 (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))
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;
SrcLoc.Offset = Addr - FuncAddr;
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.Offset = Addr - FuncAddr;
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;
}
|