diff --git a/llvm/include/llvm/DebugInfo/BTF/BTFContext.h b/llvm/include/llvm/DebugInfo/BTF/BTFContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/BTF/BTFContext.h @@ -0,0 +1,46 @@ +#ifndef LLVM_DEBUGINFO_BPF_BPFCONTEXT_H +#define LLVM_DEBUGINFO_BPF_BPFCONTEXT_H + +#include "llvm/DebugInfo/DIContext.h" +#include + +namespace llvm { + +struct BTFContextImpl; + +// BTFContext +class BTFContext : public DIContext { + std::unique_ptr Impl; + +public: + BTFContext(BTFContextImpl *Impl); + ~BTFContext(); + + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) override; + + bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) override; + + DILineInfo getLineInfoForAddress( + object::SectionedAddress Address, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + DILineInfo + getLineInfoForDataAddress(object::SectionedAddress Address) override; + + DILineInfoTable getLineInfoForAddressRange( + object::SectionedAddress Address, uint64_t Size, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + DIInliningInfo getInliningInfoForAddress( + object::SectionedAddress Address, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + std::vector + getLocalsForAddress(object::SectionedAddress Address) override; + + static std::unique_ptr create(const object::ObjectFile &Obj); +}; + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_BPF_BPFCONTEXT_H diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -38,6 +38,7 @@ std::string FunctionName; std::string StartFileName; std::optional Source; + std::optional LineSource; uint32_t Line = 0; uint32_t Column = 0; uint32_t StartLine = 0; @@ -228,7 +229,7 @@ class DIContext { public: - enum DIContextKind { CK_DWARF, CK_PDB }; + enum DIContextKind { CK_DWARF, CK_PDB, CK_BTF }; DIContext(DIContextKind K) : Kind(K) {} virtual ~DIContext() = default; diff --git a/llvm/lib/DebugInfo/BTF/BTFContext.cpp b/llvm/lib/DebugInfo/BTF/BTFContext.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/BTF/BTFContext.cpp @@ -0,0 +1,292 @@ +#include "llvm/DebugInfo/BTF/BTFContext.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "debug-info-bpf" + +struct BTFLine { + // Offset of instruction from beginning of section (in bytes) + uint32_t InsnOff; + // Offsets in strings table (in bytes) + uint32_t FileNameOff; + uint32_t LineOff; + // Combined line and column number information + uint32_t LineCol; + + uint32_t getLine() { return LineCol >> 10; } + uint32_t getCol() { return LineCol & 0x3ff; } +}; + +// The vector is sorted by InsnOff for fast lookups +using SecInfo = std::vector; + +struct llvm::BTFContextImpl { + StringRef Strings; + std::map> Sections; +}; + +static Expected getStringsSection(const object::ObjectFile &Obj, + object::SectionRef BTF) { + auto Contents = BTF.getContents(); + if (!Contents) + return Contents.takeError(); + + StringRef SecData = Contents.get(); + DataExtractor Extractor(SecData, Obj.isLittleEndian(), + Obj.getBytesInAddress()); + DataExtractor::Cursor C = DataExtractor::Cursor(0); + + Extractor.getU16(C); // magic + Extractor.getU8(C); // version + Extractor.getU8(C); // flags + auto HdrLen = Extractor.getU32(C); // hdr_len + + Extractor.getU32(C); // type_off + Extractor.getU32(C); // type_len + auto StrOff = Extractor.getU32(C); + auto StrLen = Extractor.getU32(C); + auto StrStart = HdrLen + StrOff; + + if (!C) + return C.takeError(); + + return SecData.slice(StrStart, StrStart + StrLen); +} + +static StringRef getCStringAtOffset(StringRef Data, uint32_t Offset) { + return Data.slice(Offset, Data.find(0, Offset)); +} + +static std::unique_ptr +makeContextImpl(const object::ObjectFile &Obj) { + std::unique_ptr Impl = std::make_unique(); + std::optional BTF; + std::optional BTFExt; + std::map SectionNumbers; + + LLVM_DEBUG({ llvm::dbgs() << "makeContextImpl:\n"; }); + + for (object::SectionRef Sec : Obj.sections()) { + Expected MaybeName = Sec.getName(); + if (!MaybeName) + continue; + SectionNumbers[*MaybeName] = Sec.getIndex(); + LLVM_DEBUG({ + llvm::dbgs() << " Sec Name = '" << *MaybeName << "'\n"; + llvm::dbgs() << " Sec Idx = " << Sec.getIndex() << "\n"; + llvm::dbgs() << " Sec Off = " << Sec.getAddress() << "\n"; + llvm::dbgs() << " Sec Size = " << Sec.getSize() << "\n"; + }); + if (*MaybeName == ".BTF.ext") + BTFExt = Sec; + if (*MaybeName == ".BTF") + BTF = Sec; + } + + if (!BTF) { + LLVM_DEBUG({ + dbgs() << "Can't find .BTF section" + << "\n"; + }); + return Impl; + } + + if (!BTFExt) { + LLVM_DEBUG({ + dbgs() << "Can't find .BTF.ext section" + << "\n"; + }); + return Impl; + } + + auto Strings = getStringsSection(Obj, *BTF); + if (!Strings) { + handleAllErrors(Strings.takeError(), [&](const ErrorInfoBase &E) { + LLVM_DEBUG({ + dbgs() << "Error while parsing BTF strings: " << E.message() << "\n"; + }); + }); + return Impl; + } + + Impl->Strings = Strings.get(); + + auto BTFContents = BTFExt->getContents(); + if (auto Err = BTFContents.takeError()) { + dbgs() << "makeContextImpl() can't get BTFExt section content: " << Err + << "\n"; + return Impl; + } + StringRef SecData = BTFContents.get(); + DataExtractor Extractor(SecData, Obj.isLittleEndian(), + Obj.getBytesInAddress()); + DataExtractor::Cursor C = DataExtractor::Cursor(0); + + Extractor.getU16(C); // magic + Extractor.getU8(C); // version + Extractor.getU8(C); // flags + auto HdrLen = Extractor.getU32(C); + + Extractor.getU32(C); // func_info_off + Extractor.getU32(C); // func_info_len + auto LineInfoOff = Extractor.getU32(C); + auto LineInfoLen = Extractor.getU32(C); + + auto LineInfoStart = HdrLen + LineInfoOff; + auto LineInfoEnd = LineInfoStart + LineInfoLen; + + LLVM_DEBUG({ + llvm::dbgs() << " HdrLen = " << HdrLen << "\n"; + llvm::dbgs() << " LineInfoOff = " << LineInfoOff << "\n"; + llvm::dbgs() << " LineInfoLen = " << LineInfoLen << "\n"; + llvm::dbgs() << " LineInfoStart = " << LineInfoStart << "\n"; + llvm::dbgs() << " LineInfoEnd = " << LineInfoEnd << "\n"; + }); + + C.seek(LineInfoStart); + auto RecSize = Extractor.getU32(C); + while (C && C.tell() < LineInfoEnd) { + std::unique_ptr Sec(new SecInfo()); + + auto SecNameOff = Extractor.getU32(C); + auto NumInfo = Extractor.getU32(C); + + LLVM_DEBUG({ + llvm::dbgs() << " C.tell() = " << C.tell() << "\n"; + llvm::dbgs() << " SecNameOff = " << SecNameOff << "\n"; + llvm::dbgs() << " NumInfo = " << NumInfo << "\n"; + }); + + for (uint32_t I = 0; I < NumInfo && C; ++I) { + auto RecStart = C.tell(); + + auto InsnOff = Extractor.getU32(C); + auto FileNameOff = Extractor.getU32(C); + auto LineOff = Extractor.getU32(C); + auto LineCol = Extractor.getU32(C); + + LLVM_DEBUG({ + llvm::dbgs() << " InsnOff = " << InsnOff << "\n"; + llvm::dbgs() << " FileNameOff = " << FileNameOff << "\n"; + llvm::dbgs() << " LineOff = " << LineOff << "\n"; + llvm::dbgs() << " LineCol = " << LineCol << "\n"; + }); + + BTFLine Line = {InsnOff, FileNameOff, LineOff, LineCol}; + Sec->push_back(Line); + + C.seek(RecStart + RecSize); + } + + std::sort(Sec->begin(), Sec->end(), [](const BTFLine &L, const BTFLine &R) { + return L.InsnOff < R.InsnOff; + }); + + auto SecName = getCStringAtOffset(Impl->Strings, SecNameOff); + auto SecIdx = SectionNumbers.find(SecName); + LLVM_DEBUG({ + dbgs() << " saving section " << SecName << ", " << SecIdx->second + << "\n"; + }); + if (SecIdx != SectionNumbers.end()) + Impl->Sections[SecIdx->second] = std::move(Sec); + + C.seek(LineInfoStart + NumInfo * RecSize); + } + + handleAllErrors(C.takeError(), [&](const ErrorInfoBase &E) { + LLVM_DEBUG( + { dbgs() << "Error while parsing .BTF.ext: " << E.message() << "\n"; }); + }); + + return Impl; +} + +BTFContext::BTFContext(BTFContextImpl *Impl) : DIContext(CK_BTF), Impl(Impl) {} + +BTFContext::~BTFContext() {} + +void BTFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + // TODO +} + +bool BTFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { + // TODO + return true; +} + +DILineInfo BTFContext::getLineInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Specifier) { + auto MaybeSecInfo = Impl->Sections.find(Address.SectionIndex); + if (MaybeSecInfo == Impl->Sections.end()) { + LLVM_DEBUG({ + llvm::dbgs() << "Can't find SecInfo for section " << Address.SectionIndex + << "\n"; + }); + return {}; + } + + auto &SecInfo = MaybeSecInfo->second; + auto LineInfo = std::lower_bound( + SecInfo->begin(), SecInfo->end(), Address.Address, + [](BTFLine &Line, uint64_t Addr) { return Line.InsnOff < Addr; }); + if (LineInfo == SecInfo->end() || LineInfo->InsnOff != Address.Address) { + LLVM_DEBUG({ + llvm::dbgs() << "Can't find line info for instruction " << Address + << "\n"; + }); + return {}; + } + + DILineInfo Result; + Result.LineSource = getCStringAtOffset(Impl->Strings, LineInfo->LineOff); + Result.FileName = getCStringAtOffset(Impl->Strings, LineInfo->FileNameOff); + Result.Line = LineInfo->getLine(); + Result.Column = LineInfo->getCol(); + return Result; +} + +DILineInfo +BTFContext::getLineInfoForDataAddress(object::SectionedAddress Address) { + LLVM_DEBUG({ + dbgs() << "BPFContext::getLineInfoForDataAddress: Address = " << Address + << "\n"; + }); + return DILineInfo(); +} + +DILineInfoTable +BTFContext::getLineInfoForAddressRange(object::SectionedAddress Address, + uint64_t Size, + DILineInfoSpecifier Specifier) { + LLVM_DEBUG({ + dbgs() << "BPFContext::getLineInfoForAddressRange:\n"; + dbgs() << " Address = " << Address << "\n"; + dbgs() << " Size = " << Size << "\n"; + }); + return DILineInfoTable(); +} + +DIInliningInfo +BTFContext::getInliningInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Specifier) { + return DIInliningInfo(); +} + +std::vector +BTFContext::getLocalsForAddress(object::SectionedAddress Address) { + return std::vector(); +} + +std::unique_ptr BTFContext::create(const object::ObjectFile &Obj) { + return std::make_unique(makeContextImpl(Obj).release()); +} diff --git a/llvm/lib/DebugInfo/BTF/CMakeLists.txt b/llvm/lib/DebugInfo/BTF/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/BTF/CMakeLists.txt @@ -0,0 +1,8 @@ +add_llvm_component_library(LLVMDebugInfoBTF + BTFContext.cpp + ADDITIONAL_HEADER_DIRS + "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/BTF" + + LINK_COMPONENTS + Support + ) diff --git a/llvm/lib/DebugInfo/CMakeLists.txt b/llvm/lib/DebugInfo/CMakeLists.txt --- a/llvm/lib/DebugInfo/CMakeLists.txt +++ b/llvm/lib/DebugInfo/CMakeLists.txt @@ -5,3 +5,4 @@ add_subdirectory(CodeView) add_subdirectory(PDB) add_subdirectory(Symbolize) +add_subdirectory(BTF) diff --git a/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt b/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt --- a/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt +++ b/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt @@ -11,6 +11,7 @@ LINK_COMPONENTS DebugInfoDWARF DebugInfoPDB + DebugInfoBTF Object Support Demangle diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/BTF/BTFContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBContext.h" @@ -615,6 +616,27 @@ return ModuleOrErr; } +// .BTF.ext section contains line numbers information, use it if +// regular DWARF is not available (e.g. for stripped binary). +static bool useBPFDebugInfo(const ObjectFile &Obj) { + bool HasBPFExt = false; + bool HasDebugInfo = false; + + auto Arch = Obj.getArch(); + if (Arch != Triple::ArchType::bpfel && Arch != Triple::ArchType::bpfeb) + return false; + + for (SectionRef Sec : Obj.sections()) { + Expected MaybeName = Sec.getName(); + if (MaybeName && MaybeName.get() == ".BTF.ext") + HasBPFExt = true; + if (Sec.isDebugSection()) + HasDebugInfo = true; + } + + return HasBPFExt && !HasDebugInfo; +} + Expected LLVMSymbolizer::getOrCreateModuleInfo(const ObjectFile &Obj) { StringRef ObjName = Obj.getFileName(); @@ -622,7 +644,11 @@ if (I != Modules.end()) return I->second.get(); - std::unique_ptr Context = DWARFContext::create(Obj); + std::unique_ptr Context; + if (useBPFDebugInfo(Obj)) + Context = BTFContext::create(Obj); + else + Context = DWARFContext::create(Obj); // FIXME: handle COFF object with PDB info to use PDBContext return createModuleInfo(&Obj, std::move(Context), ObjName); } diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h --- a/llvm/tools/llvm-objdump/SourcePrinter.h +++ b/llvm/tools/llvm-objdump/SourcePrinter.h @@ -151,6 +151,9 @@ StringRef ObjectFilename, StringRef Delimiter, LiveVariablePrinter &LVP); + std::optional getLine(const DILineInfo &LineInfo, + StringRef ObjectFilename); + public: SourcePrinter() = default; SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch); diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp --- a/llvm/tools/llvm-objdump/SourcePrinter.cpp +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -452,6 +452,31 @@ } } +std::optional SourcePrinter::getLine(const DILineInfo &LineInfo, + StringRef ObjectFilename) { + if (LineInfo.LineSource) + return LineInfo.LineSource; + + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return std::nullopt; + + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer == LineCache.end()) + return std::nullopt; + + if (LineInfo.Line > LineBuffer->second.size()) { + reportWarning( + formatv("debug info line number {0} exceeds the number of lines in {1}", + LineInfo.Line, LineInfo.FileName), + ObjectFilename); + return std::nullopt; + } + + // Vector begins at 0, line numbers are non-zero + return LineBuffer->second[LineInfo.Line - 1]; +} + void SourcePrinter::printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, StringRef ObjectFilename, StringRef Delimiter, @@ -461,21 +486,8 @@ OldLineInfo.FileName == LineInfo.FileName)) return; - if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo)) - return; - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) { - reportWarning( - formatv( - "debug info line number {0} exceeds the number of lines in {1}", - LineInfo.Line, LineInfo.FileName), - ObjectFilename); - return; - } - // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; + if (auto Line = getLine(LineInfo, ObjectFilename)) { + OS << Delimiter << Line; LVP.printBetweenInsts(OS, true); } }