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