#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;
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;
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 = {BaseAddr, BaseAddr + 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");
O.alignTo(4);
const uint64_t FuncInfoOffset = O.tell();
O.writeU32(size());
O.writeU32(Name);
if (OptLineTable) {
O.writeU32(InfoType::LineTableInfo);
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");
O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
}
if (Inline) {
O.writeU32(InfoType::InlineInfo);
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");
O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
}
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;
uint64_t Offset = 0;
LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};
uint32_t NameOffset = Data.getU32(&Offset);
if (!Data.isValidOffset(Offset))
return createStringError(std::errc::io_error,
"FunctionInfo data is truncated");
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:
InlineInfoData = InfoData;
break;
default:
break;
}
Offset += InfoLength;
}
if (!LineEntry) {
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 (!InlineInfoData)
return LR;
llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
LR.Locations);
if (Err)
return std::move(Err);
return LR;
}