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