1 /* 2 * Copyright (c) 2017 Facebook, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <map> 18 #include <string> 19 #include <tuple> 20 #include <vector> 21 22 #include <llvm/DebugInfo/DWARF/DWARFContext.h> 23 #include <llvm/DebugInfo/DWARF/DWARFDebugLine.h> 24 #include <llvm/IR/Module.h> 25 #include <llvm/MC/MCAsmInfo.h> 26 #include <llvm/MC/MCContext.h> 27 #include <llvm/MC/MCDisassembler/MCDisassembler.h> 28 #include <llvm/MC/MCInstPrinter.h> 29 #include <llvm/MC/MCInstrInfo.h> 30 #include <llvm/MC/MCObjectFileInfo.h> 31 #include <llvm/MC/MCRegisterInfo.h> 32 #include <llvm/Support/TargetRegistry.h> 33 34 #include "bcc_debug.h" 35 36 namespace ebpf { 37 38 // ld_pseudo can only be disassembled properly 39 // in llvm 6.0, so having this workaround now 40 // until disto llvm versions catch up 41 #define WORKAROUND_FOR_LD_PSEUDO 42 43 using std::get; 44 using std::map; 45 using std::string; 46 using std::tuple; 47 using std::vector; 48 using namespace llvm; 49 using DWARFLineTable = DWARFDebugLine::LineTable; 50 51 void SourceDebugger::adjustInstSize(uint64_t &Size, uint8_t byte0, 52 uint8_t byte1) { 53 #ifdef WORKAROUND_FOR_LD_PSEUDO 54 bool isLittleEndian = mod_->getDataLayout().isLittleEndian(); 55 if (byte0 == 0x18 && ((isLittleEndian && (byte1 & 0xf) == 0x1) || 56 (!isLittleEndian && (byte1 & 0xf0) == 0x10))) 57 Size = 16; 58 #endif 59 } 60 61 vector<string> SourceDebugger::buildLineCache() { 62 vector<string> LineCache; 63 size_t FileBufSize = mod_src_.size(); 64 65 for (uint32_t start = 0, end = start; end < FileBufSize; end++) 66 if (mod_src_[end] == '\n' || end == FileBufSize - 1 || 67 (mod_src_[end] == '\r' && mod_src_[end + 1] == '\n')) { 68 // Not including the endline 69 LineCache.push_back(string(mod_src_.substr(start, end - start))); 70 if (mod_src_[end] == '\r') 71 end++; 72 start = end + 1; 73 } 74 75 return LineCache; 76 } 77 78 void SourceDebugger::dumpSrcLine(const vector<string> &LineCache, 79 const string &FileName, uint32_t Line, 80 uint32_t &CurrentSrcLine, 81 llvm::raw_ostream &os) { 82 if (Line != 0 && Line != CurrentSrcLine && Line < LineCache.size() && 83 FileName == mod_->getSourceFileName()) { 84 os << "; " << StringRef(LineCache[Line - 1]).ltrim() 85 << format( 86 " // Line" 87 "%4" PRIu64 "\n", 88 Line); 89 CurrentSrcLine = Line; 90 } 91 } 92 93 void SourceDebugger::getDebugSections( 94 StringMap<std::unique_ptr<MemoryBuffer>> &DebugSections) { 95 for (auto section : sections_) { 96 if (strncmp(section.first.c_str(), ".debug", 6) == 0) { 97 StringRef SecData(reinterpret_cast<const char *>(get<0>(section.second)), 98 get<1>(section.second)); 99 DebugSections[section.first.substr(1)] = 100 MemoryBuffer::getMemBufferCopy(SecData); 101 } 102 } 103 } 104 105 void SourceDebugger::dump() { 106 string Error; 107 string TripleStr(mod_->getTargetTriple()); 108 Triple TheTriple(TripleStr); 109 const Target *T = TargetRegistry::lookupTarget(TripleStr, Error); 110 if (!T) { 111 errs() << "Debug Error: cannot get target\n"; 112 return; 113 } 114 115 std::unique_ptr<MCRegisterInfo> MRI(T->createMCRegInfo(TripleStr)); 116 if (!MRI) { 117 errs() << "Debug Error: cannot get register info\n"; 118 return; 119 } 120 std::unique_ptr<MCAsmInfo> MAI(T->createMCAsmInfo(*MRI, TripleStr)); 121 if (!MAI) { 122 errs() << "Debug Error: cannot get assembly info\n"; 123 return; 124 } 125 126 MCObjectFileInfo MOFI; 127 MCContext Ctx(MAI.get(), MRI.get(), &MOFI, nullptr); 128 MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx, false); 129 std::unique_ptr<MCSubtargetInfo> STI( 130 T->createMCSubtargetInfo(TripleStr, "", "")); 131 132 std::unique_ptr<MCInstrInfo> MCII(T->createMCInstrInfo()); 133 MCInstPrinter *IP = T->createMCInstPrinter(TheTriple, 0, *MAI, *MCII, *MRI); 134 if (!IP) { 135 errs() << "Debug Error: unable to create instruction printer\n"; 136 return; 137 } 138 139 std::unique_ptr<const MCDisassembler> DisAsm( 140 T->createMCDisassembler(*STI, Ctx)); 141 if (!DisAsm) { 142 errs() << "Debug Error: no disassembler\n"; 143 return; 144 } 145 146 // Set up the dwarf debug context 147 StringMap<std::unique_ptr<MemoryBuffer>> DebugSections; 148 getDebugSections(DebugSections); 149 std::unique_ptr<DWARFContext> DwarfCtx = 150 DWARFContext::create(DebugSections, 8); 151 if (!DwarfCtx) { 152 errs() << "Debug Error: dwarf context creation failed\n"; 153 return; 154 } 155 156 // bcc has only one compilation unit 157 // getCompileUnitAtIndex() was gone in llvm 8.0 (https://reviews.llvm.org/D49741) 158 #if LLVM_MAJOR_VERSION >= 8 159 DWARFCompileUnit *CU = cast<DWARFCompileUnit>(DwarfCtx->getUnitAtIndex(0)); 160 #else 161 DWARFCompileUnit *CU = DwarfCtx->getCompileUnitAtIndex(0); 162 #endif 163 if (!CU) { 164 errs() << "Debug Error: dwarf context failed to get compile unit\n"; 165 return; 166 } 167 168 const DWARFLineTable *LineTable = DwarfCtx->getLineTableForUnit(CU); 169 if (!LineTable) { 170 errs() << "Debug Error: dwarf context failed to get line table\n"; 171 return; 172 } 173 174 // Build LineCache for later source code printing 175 vector<string> LineCache = buildLineCache(); 176 177 // Start to disassemble with source code annotation section by section 178 for (auto section : sections_) 179 if (!strncmp(fn_prefix_.c_str(), section.first.c_str(), 180 fn_prefix_.size())) { 181 MCDisassembler::DecodeStatus S; 182 MCInst Inst; 183 uint64_t Size; 184 uint8_t *FuncStart = get<0>(section.second); 185 uint64_t FuncSize = get<1>(section.second); 186 ArrayRef<uint8_t> Data(FuncStart, FuncSize); 187 uint32_t CurrentSrcLine = 0; 188 string func_name = section.first.substr(fn_prefix_.size()); 189 190 errs() << "Disassembly of section " << section.first << ":\n" 191 << func_name << ":\n"; 192 193 string src_dbg_str; 194 llvm::raw_string_ostream os(src_dbg_str); 195 for (uint64_t Index = 0; Index < FuncSize; Index += Size) { 196 S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index, 197 nulls(), nulls()); 198 if (S != MCDisassembler::Success) { 199 os << "Debug Error: disassembler failed: " << std::to_string(S) 200 << '\n'; 201 break; 202 } else { 203 DILineInfo LineInfo; 204 LineTable->getFileLineInfoForAddress( 205 (uint64_t)FuncStart + Index, CU->getCompilationDir(), 206 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, 207 LineInfo); 208 209 adjustInstSize(Size, Data[Index], Data[Index + 1]); 210 dumpSrcLine(LineCache, LineInfo.FileName, LineInfo.Line, 211 CurrentSrcLine, os); 212 os << format("%4" PRIu64 ":", Index >> 3) << '\t'; 213 dumpBytes(Data.slice(Index, Size), os); 214 IP->printInst(&Inst, os, "", *STI); 215 os << '\n'; 216 } 217 } 218 os.flush(); 219 errs() << src_dbg_str << '\n'; 220 src_dbg_fmap_[func_name] = src_dbg_str; 221 } 222 } 223 224 } // namespace ebpf 225