• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if LLVM_MAJOR_VERSION >= 15
23 #include <llvm/DebugInfo/DWARF/DWARFCompileUnit.h>
24 #endif
25 #include <llvm/DebugInfo/DWARF/DWARFContext.h>
26 #include <llvm/DebugInfo/DWARF/DWARFDebugLine.h>
27 #include <llvm/IR/Module.h>
28 #include <llvm/MC/MCAsmInfo.h>
29 #include <llvm/MC/MCContext.h>
30 #include <llvm/MC/MCDisassembler/MCDisassembler.h>
31 #include <llvm/MC/MCInstPrinter.h>
32 #include <llvm/MC/MCInstrInfo.h>
33 #include <llvm/MC/MCObjectFileInfo.h>
34 #include <llvm/MC/MCRegisterInfo.h>
35 #if LLVM_MAJOR_VERSION >= 15
36 #include <llvm/MC/MCSubtargetInfo.h>
37 #endif
38 #if LLVM_MAJOR_VERSION >= 14
39 #include <llvm/MC/TargetRegistry.h>
40 #else
41 #include <llvm/Support/TargetRegistry.h>
42 #endif
43 
44 #include "bcc_debug.h"
45 
46 namespace ebpf {
47 
48 // ld_pseudo can only be disassembled properly
49 // in llvm 6.0, so having this workaround now
50 // until disto llvm versions catch up
51 #define WORKAROUND_FOR_LD_PSEUDO
52 
53 using std::get;
54 using std::map;
55 using std::string;
56 using std::tuple;
57 using std::vector;
58 using namespace llvm;
59 using DWARFLineTable = DWARFDebugLine::LineTable;
60 
adjustInstSize(uint64_t & Size,uint8_t byte0,uint8_t byte1)61 void SourceDebugger::adjustInstSize(uint64_t &Size, uint8_t byte0,
62                                     uint8_t byte1) {
63 #ifdef WORKAROUND_FOR_LD_PSEUDO
64   bool isLittleEndian = mod_->getDataLayout().isLittleEndian();
65   if (byte0 == 0x18 && ((isLittleEndian && (byte1 & 0xf) == 0x1) ||
66                         (!isLittleEndian && (byte1 & 0xf0) == 0x10)))
67     Size = 16;
68 #endif
69 }
70 
buildLineCache()71 vector<string> SourceDebugger::buildLineCache() {
72   vector<string> LineCache;
73   size_t FileBufSize = mod_src_.size();
74 
75   for (uint32_t start = 0, end = start; end < FileBufSize; end++)
76     if (mod_src_[end] == '\n' || end == FileBufSize - 1 ||
77         (mod_src_[end] == '\r' && mod_src_[end + 1] == '\n')) {
78       // Not including the endline
79       LineCache.push_back(string(mod_src_.substr(start, end - start)));
80       if (mod_src_[end] == '\r')
81         end++;
82       start = end + 1;
83     }
84 
85   return LineCache;
86 }
87 
dumpSrcLine(const vector<string> & LineCache,const string & FileName,uint32_t Line,uint32_t & CurrentSrcLine,llvm::raw_ostream & os)88 void SourceDebugger::dumpSrcLine(const vector<string> &LineCache,
89                                  const string &FileName, uint32_t Line,
90                                  uint32_t &CurrentSrcLine,
91                                  llvm::raw_ostream &os) {
92   if (Line != 0 && Line != CurrentSrcLine && Line < LineCache.size() &&
93       FileName == mod_->getSourceFileName()) {
94     os << "; " << StringRef(LineCache[Line - 1]).ltrim()
95        << format(
96               " // Line"
97               "%4" PRIu64 "\n",
98               Line);
99     CurrentSrcLine = Line;
100   }
101 }
102 
getDebugSections(StringMap<std::unique_ptr<MemoryBuffer>> & DebugSections)103 void SourceDebugger::getDebugSections(
104     StringMap<std::unique_ptr<MemoryBuffer>> &DebugSections) {
105   for (auto section : sections_) {
106     if (strncmp(section.first.c_str(), ".debug", 6) == 0) {
107       StringRef SecData(reinterpret_cast<const char *>(get<0>(section.second)),
108                         get<1>(section.second));
109       DebugSections[section.first.substr(1)] =
110           MemoryBuffer::getMemBufferCopy(SecData);
111     }
112   }
113 }
114 
dump()115 void SourceDebugger::dump() {
116   string Error;
117   string TripleStr(mod_->getTargetTriple());
118   Triple TheTriple(TripleStr);
119   const Target *T = TargetRegistry::lookupTarget(TripleStr, Error);
120   if (!T) {
121     errs() << "Debug Error: cannot get target\n";
122     return;
123   }
124 
125   std::unique_ptr<MCRegisterInfo> MRI(T->createMCRegInfo(TripleStr));
126   if (!MRI) {
127     errs() << "Debug Error: cannot get register info\n";
128     return;
129   }
130 #if LLVM_MAJOR_VERSION >= 10
131   MCTargetOptions MCOptions;
132   std::unique_ptr<MCAsmInfo> MAI(T->createMCAsmInfo(*MRI, TripleStr, MCOptions));
133 #else
134   std::unique_ptr<MCAsmInfo> MAI(T->createMCAsmInfo(*MRI, TripleStr));
135 #endif
136   if (!MAI) {
137     errs() << "Debug Error: cannot get assembly info\n";
138     return;
139   }
140 
141   std::unique_ptr<MCSubtargetInfo> STI(
142       T->createMCSubtargetInfo(TripleStr, "", ""));
143   MCObjectFileInfo MOFI;
144 #if LLVM_MAJOR_VERSION >= 13
145   MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), nullptr);
146   Ctx.setObjectFileInfo(&MOFI);
147   MOFI.initMCObjectFileInfo(Ctx, false, false);
148 #else
149   MCContext Ctx(MAI.get(), MRI.get(), &MOFI, nullptr);
150   MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx, false);
151 #endif
152 
153   std::unique_ptr<MCInstrInfo> MCII(T->createMCInstrInfo());
154   MCInstPrinter *IP = T->createMCInstPrinter(TheTriple, 0, *MAI, *MCII, *MRI);
155   if (!IP) {
156     errs() << "Debug Error: unable to create instruction printer\n";
157     return;
158   }
159 
160   std::unique_ptr<const MCDisassembler> DisAsm(
161       T->createMCDisassembler(*STI, Ctx));
162   if (!DisAsm) {
163     errs() << "Debug Error: no disassembler\n";
164     return;
165   }
166 
167   // Set up the dwarf debug context
168   StringMap<std::unique_ptr<MemoryBuffer>> DebugSections;
169   getDebugSections(DebugSections);
170   std::unique_ptr<DWARFContext> DwarfCtx =
171       DWARFContext::create(DebugSections, 8);
172   if (!DwarfCtx) {
173     errs() << "Debug Error: dwarf context creation failed\n";
174     return;
175   }
176 
177   // bcc has only one compilation unit
178   // getCompileUnitAtIndex() was gone in llvm 8.0 (https://reviews.llvm.org/D49741)
179 #if LLVM_MAJOR_VERSION >= 8
180   DWARFCompileUnit *CU = cast<DWARFCompileUnit>(DwarfCtx->getUnitAtIndex(0));
181 #else
182   DWARFCompileUnit *CU = DwarfCtx->getCompileUnitAtIndex(0);
183 #endif
184   if (!CU) {
185     errs() << "Debug Error: dwarf context failed to get compile unit\n";
186     return;
187   }
188 
189   const DWARFLineTable *LineTable = DwarfCtx->getLineTableForUnit(CU);
190   if (!LineTable) {
191     errs() << "Debug Error: dwarf context failed to get line table\n";
192     return;
193   }
194 
195   // Build LineCache for later source code printing
196   vector<string> LineCache = buildLineCache();
197 
198   // Start to disassemble with source code annotation section by section
199   prog_func_info_.for_each_func([&](std::string func_name, FuncInfo &info) {
200     MCDisassembler::DecodeStatus S;
201     MCInst Inst;
202     uint64_t Size;
203     uint8_t *FuncStart = info.start_;
204     uint64_t FuncSize = info.size_;
205 #if LLVM_MAJOR_VERSION >= 9
206     auto section = sections_.find(info.section_);
207     if (section == sections_.end()) {
208       errs() << "Debug Error: no section entry for section " << info.section_
209              << '\n';
210       return;
211     }
212     unsigned SectionID = get<2>(section->second);
213 #endif
214     ArrayRef<uint8_t> Data(FuncStart, FuncSize);
215     uint32_t CurrentSrcLine = 0;
216 
217     errs() << "Disassembly of function " << func_name << "\n";
218 
219     string src_dbg_str;
220     llvm::raw_string_ostream os(src_dbg_str);
221     for (uint64_t Index = 0; Index < FuncSize; Index += Size) {
222 #if LLVM_MAJOR_VERSION >= 10
223       S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index, nulls());
224 #else
225       S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index, nulls(),
226                                  nulls());
227 #endif
228       if (S != MCDisassembler::Success) {
229         os << "Debug Error: disassembler failed: " << std::to_string(S) << '\n';
230         break;
231       } else {
232         DILineInfo LineInfo;
233 
234         LineTable->getFileLineInfoForAddress(
235 #if LLVM_MAJOR_VERSION >= 9
236             {(uint64_t)FuncStart + Index, SectionID},
237 #else
238             (uint64_t)FuncStart + Index,
239 #endif
240             CU->getCompilationDir(),
241             DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, LineInfo);
242 
243         adjustInstSize(Size, Data[Index], Data[Index + 1]);
244         dumpSrcLine(LineCache, LineInfo.FileName, LineInfo.Line, CurrentSrcLine,
245                     os);
246         os << format("%4" PRIu64 ":", Index >> 3) << '\t';
247         dumpBytes(Data.slice(Index, Size), os);
248 #if LLVM_MAJOR_VERSION >= 10
249         IP->printInst(&Inst, 0, "", *STI, os);
250 #else
251         IP->printInst(&Inst, os, "", *STI);
252 #endif
253         os << '\n';
254       }
255     }
256     os.flush();
257     errs() << src_dbg_str << '\n';
258     src_dbg_fmap_[func_name] = src_dbg_str;
259   });
260 }
261 
262 }  // namespace ebpf
263