blob: 7c3dd1f9d86f08795fa41dd2e153cfdbd02a5d5b [file] [log] [blame]
/*
* 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