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 
adjustInstSize(uint64_t & Size,uint8_t byte0,uint8_t byte1)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 
buildLineCache()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 
dumpSrcLine(const vector<string> & LineCache,const string & FileName,uint32_t Line,uint32_t & CurrentSrcLine,llvm::raw_ostream & os)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 
getDebugSections(StringMap<std::unique_ptr<MemoryBuffer>> & DebugSections)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 
dump()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