| /* |
| * Copyright (c) 2017 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <map> |
| #include <string> |
| #include <tuple> |
| #include <vector> |
| |
| #include <llvm/DebugInfo/DWARF/DWARFContext.h> |
| #include <llvm/DebugInfo/DWARF/DWARFDebugLine.h> |
| #include <llvm/IR/Module.h> |
| #include <llvm/MC/MCAsmInfo.h> |
| #include <llvm/MC/MCContext.h> |
| #include <llvm/MC/MCDisassembler/MCDisassembler.h> |
| #include <llvm/MC/MCInstPrinter.h> |
| #include <llvm/MC/MCInstrInfo.h> |
| #include <llvm/MC/MCObjectFileInfo.h> |
| #include <llvm/MC/MCRegisterInfo.h> |
| #include <llvm/Support/TargetRegistry.h> |
| |
| #include "bcc_debug.h" |
| |
| namespace ebpf { |
| |
| // ld_pseudo can only be disassembled properly |
| // in llvm 6.0, so having this workaround now |
| // until disto llvm versions catch up |
| #define WORKAROUND_FOR_LD_PSEUDO |
| |
| using std::get; |
| using std::map; |
| using std::string; |
| using std::tuple; |
| using std::vector; |
| using namespace llvm; |
| using DWARFLineTable = DWARFDebugLine::LineTable; |
| |
| void SourceDebugger::adjustInstSize(uint64_t &Size, uint8_t byte0, |
| uint8_t byte1) { |
| #ifdef WORKAROUND_FOR_LD_PSEUDO |
| bool isLittleEndian = mod_->getDataLayout().isLittleEndian(); |
| if (byte0 == 0x18 && ((isLittleEndian && (byte1 & 0xf) == 0x1) || |
| (!isLittleEndian && (byte1 & 0xf0) == 0x10))) |
| Size = 16; |
| #endif |
| } |
| |
| vector<string> SourceDebugger::buildLineCache() { |
| vector<string> LineCache; |
| size_t FileBufSize = mod_src_.size(); |
| |
| for (uint32_t start = 0, end = start; end < FileBufSize; end++) |
| if (mod_src_[end] == '\n' || end == FileBufSize - 1 || |
| (mod_src_[end] == '\r' && mod_src_[end + 1] == '\n')) { |
| // Not including the endline |
| LineCache.push_back(string(mod_src_.substr(start, end - start))); |
| if (mod_src_[end] == '\r') |
| end++; |
| start = end + 1; |
| } |
| |
| return LineCache; |
| } |
| |
| void SourceDebugger::dumpSrcLine(const vector<string> &LineCache, |
| const string &FileName, uint32_t Line, |
| uint32_t &CurrentSrcLine, |
| llvm::raw_ostream &os) { |
| if (Line != 0 && Line != CurrentSrcLine && Line < LineCache.size() && |
| FileName == mod_->getSourceFileName()) { |
| os << "; " << StringRef(LineCache[Line - 1]).ltrim() |
| << format( |
| " // Line" |
| "%4" PRIu64 "\n", |
| Line); |
| CurrentSrcLine = Line; |
| } |
| } |
| |
| void SourceDebugger::getDebugSections( |
| StringMap<std::unique_ptr<MemoryBuffer>> &DebugSections) { |
| for (auto section : sections_) { |
| if (strncmp(section.first.c_str(), ".debug", 6) == 0) { |
| StringRef SecData(reinterpret_cast<const char *>(get<0>(section.second)), |
| get<1>(section.second)); |
| DebugSections[section.first.substr(1)] = |
| MemoryBuffer::getMemBufferCopy(SecData); |
| } |
| } |
| } |
| |
| void SourceDebugger::dump() { |
| string Error; |
| string TripleStr(mod_->getTargetTriple()); |
| Triple TheTriple(TripleStr); |
| const Target *T = TargetRegistry::lookupTarget(TripleStr, Error); |
| if (!T) { |
| errs() << "Debug Error: cannot get target\n"; |
| return; |
| } |
| |
| std::unique_ptr<MCRegisterInfo> MRI(T->createMCRegInfo(TripleStr)); |
| if (!MRI) { |
| errs() << "Debug Error: cannot get register info\n"; |
| return; |
| } |
| std::unique_ptr<MCAsmInfo> MAI(T->createMCAsmInfo(*MRI, TripleStr)); |
| if (!MAI) { |
| errs() << "Debug Error: cannot get assembly info\n"; |
| return; |
| } |
| |
| MCObjectFileInfo MOFI; |
| MCContext Ctx(MAI.get(), MRI.get(), &MOFI, nullptr); |
| MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx, false); |
| std::unique_ptr<MCSubtargetInfo> STI( |
| T->createMCSubtargetInfo(TripleStr, "", "")); |
| |
| std::unique_ptr<MCInstrInfo> MCII(T->createMCInstrInfo()); |
| MCInstPrinter *IP = T->createMCInstPrinter(TheTriple, 0, *MAI, *MCII, *MRI); |
| if (!IP) { |
| errs() << "Debug Error: unable to create instruction printer\n"; |
| return; |
| } |
| |
| std::unique_ptr<const MCDisassembler> DisAsm( |
| T->createMCDisassembler(*STI, Ctx)); |
| if (!DisAsm) { |
| errs() << "Debug Error: no disassembler\n"; |
| return; |
| } |
| |
| // Set up the dwarf debug context |
| StringMap<std::unique_ptr<MemoryBuffer>> DebugSections; |
| getDebugSections(DebugSections); |
| std::unique_ptr<DWARFContext> DwarfCtx = |
| DWARFContext::create(DebugSections, 8); |
| if (!DwarfCtx) { |
| errs() << "Debug Error: dwarf context creation failed\n"; |
| return; |
| } |
| |
| // bcc has only one compilation unit |
| DWARFCompileUnit *CU = DwarfCtx->getCompileUnitAtIndex(0); |
| if (!CU) { |
| errs() << "Debug Error: dwarf context failed to get compile unit\n"; |
| return; |
| } |
| |
| const DWARFLineTable *LineTable = DwarfCtx->getLineTableForUnit(CU); |
| if (!LineTable) { |
| errs() << "Debug Error: dwarf context failed to get line table\n"; |
| return; |
| } |
| |
| // Build LineCache for later source code printing |
| vector<string> LineCache = buildLineCache(); |
| |
| // Start to disassemble with source code annotation section by section |
| for (auto section : sections_) |
| if (!strncmp(fn_prefix_.c_str(), section.first.c_str(), |
| fn_prefix_.size())) { |
| MCDisassembler::DecodeStatus S; |
| MCInst Inst; |
| uint64_t Size; |
| uint8_t *FuncStart = get<0>(section.second); |
| uint64_t FuncSize = get<1>(section.second); |
| ArrayRef<uint8_t> Data(FuncStart, FuncSize); |
| uint32_t CurrentSrcLine = 0; |
| string func_name = section.first.substr(fn_prefix_.size()); |
| |
| errs() << "Disassembly of section " << section.first << ":\n" |
| << func_name << ":\n"; |
| |
| string src_dbg_str; |
| llvm::raw_string_ostream os(src_dbg_str); |
| for (uint64_t Index = 0; Index < FuncSize; Index += Size) { |
| S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index, |
| nulls(), nulls()); |
| if (S != MCDisassembler::Success) { |
| os << "Debug Error: disassembler failed: " << std::to_string(S) |
| << '\n'; |
| break; |
| } else { |
| DILineInfo LineInfo; |
| LineTable->getFileLineInfoForAddress( |
| (uint64_t)FuncStart + Index, CU->getCompilationDir(), |
| DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, |
| LineInfo); |
| |
| adjustInstSize(Size, Data[Index], Data[Index + 1]); |
| dumpSrcLine(LineCache, LineInfo.FileName, LineInfo.Line, |
| CurrentSrcLine, os); |
| os << format("%4" PRIu64 ":", Index >> 3) << '\t'; |
| dumpBytes(Data.slice(Index, Size), os); |
| IP->printInst(&Inst, os, "", *STI); |
| os << '\n'; |
| } |
| } |
| os.flush(); |
| errs() << src_dbg_str << '\n'; |
| src_dbg_fmap_[func_name] = src_dbg_str; |
| } |
| } |
| |
| } // namespace ebpf |