• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015 PLUMgrid, 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 <algorithm>
20 #include <fcntl.h>
21 #include <ftw.h>
22 #include <map>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/utsname.h>
29 #include <unistd.h>
30 #include <utility>
31 #include <vector>
32 #include <iostream>
33 #include <linux/bpf.h>
34 
35 #include <clang/Basic/FileManager.h>
36 #include <clang/Basic/TargetInfo.h>
37 #include <clang/CodeGen/BackendUtil.h>
38 #include <clang/CodeGen/CodeGenAction.h>
39 #include <clang/Driver/Compilation.h>
40 #include <clang/Driver/Driver.h>
41 #include <clang/Driver/Job.h>
42 #include <clang/Driver/Tool.h>
43 #include <clang/Frontend/CompilerInstance.h>
44 #include <clang/Frontend/CompilerInvocation.h>
45 #include <clang/Frontend/FrontendActions.h>
46 #include <clang/Frontend/FrontendDiagnostic.h>
47 #include <clang/Frontend/TextDiagnosticPrinter.h>
48 #include <clang/FrontendTool/Utils.h>
49 #include <clang/Lex/PreprocessorOptions.h>
50 
51 #include <llvm/IR/Module.h>
52 
53 #include "bcc_exception.h"
54 #include "bpf_module.h"
55 #include "exported_files.h"
56 #include "kbuild_helper.h"
57 #include "b_frontend_action.h"
58 #include "tp_frontend_action.h"
59 #include "loader.h"
60 #include "arch_helper.h"
61 
62 using std::map;
63 using std::string;
64 using std::unique_ptr;
65 using std::vector;
66 
67 namespace ebpf {
68 
ClangLoader(llvm::LLVMContext * ctx,unsigned flags)69 ClangLoader::ClangLoader(llvm::LLVMContext *ctx, unsigned flags)
70     : ctx_(ctx), flags_(flags)
71 {
72   for (auto f : ExportedFiles::headers())
73     remapped_headers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second);
74   for (auto f : ExportedFiles::footers())
75     remapped_footers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second);
76 }
77 
~ClangLoader()78 ClangLoader::~ClangLoader() {}
79 
80 namespace
81 {
82 
is_dir(const string & path)83 bool is_dir(const string& path)
84 {
85   struct stat buf;
86 
87   if (::stat (path.c_str (), &buf) < 0)
88     return false;
89 
90   return S_ISDIR(buf.st_mode);
91 }
92 
get_kernel_path_info(const string kdir)93 std::pair<bool, string> get_kernel_path_info(const string kdir)
94 {
95   if (is_dir(kdir + "/build") && is_dir(kdir + "/source"))
96     return std::make_pair (true, "source");
97 
98   const char* suffix_from_env = ::getenv("BCC_KERNEL_MODULES_SUFFIX");
99   if (suffix_from_env)
100     return std::make_pair(false, string(suffix_from_env));
101 
102   return std::make_pair(false, "build");
103 }
104 
105 }
106 
parse(unique_ptr<llvm::Module> * mod,TableStorage & ts,const string & file,bool in_memory,const char * cflags[],int ncflags,const std::string & id,FuncSource & func_src,std::string & mod_src,const std::string & maps_ns)107 int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts,
108                        const string &file, bool in_memory, const char *cflags[],
109                        int ncflags, const std::string &id, FuncSource &func_src,
110                        std::string &mod_src,
111                        const std::string &maps_ns) {
112   string main_path = "/virtual/main.c";
113   unique_ptr<llvm::MemoryBuffer> main_buf;
114   struct utsname un;
115   uname(&un);
116   string kdir, kpath;
117   const char *kpath_env = ::getenv("BCC_KERNEL_SOURCE");
118   const char *version_override = ::getenv("BCC_LINUX_VERSION_CODE");
119   bool has_kpath_source = false;
120   string vmacro;
121 
122   if (kpath_env) {
123     kpath = string(kpath_env);
124   } else {
125     kdir = string(KERNEL_MODULES_DIR) + "/" + un.release;
126     auto kernel_path_info = get_kernel_path_info(kdir);
127     has_kpath_source = kernel_path_info.first;
128     kpath = kdir + "/" + kernel_path_info.second;
129   }
130 
131   if (flags_ & DEBUG_PREPROCESSOR)
132     std::cout << "Running from kernel directory at: " << kpath.c_str() << "\n";
133 
134   // clang needs to run inside the kernel dir
135   DirStack dstack(kpath);
136   if (!dstack.ok())
137     return -1;
138 
139   string abs_file;
140   if (in_memory) {
141     abs_file = main_path;
142     main_buf = llvm::MemoryBuffer::getMemBuffer(file);
143   } else {
144     if (file.substr(0, 1) == "/")
145       abs_file = file;
146     else
147       abs_file = string(dstack.cwd()) + "/" + file;
148   }
149 
150   // -fno-color-diagnostics: this is a workaround for a bug in llvm terminalHasColors() as of
151   // 22 Jul 2016. Also see bcc #615.
152   // Enable -O2 for clang. In clang 5.0, -O0 may result in function marking as
153   // noinline and optnone (if not always inlining).
154   // Note that first argument is ignored in clang compilation invocation.
155   // "-D __BPF_TRACING__" below is added to suppress a warning in 4.17+.
156   // It can be removed once clang supports asm-goto or the kernel removes
157   // the warning.
158   vector<const char *> flags_cstr({"-O0", "-O2", "-emit-llvm", "-I", dstack.cwd(),
159                                    "-D", "__BPF_TRACING__",
160                                    "-Wno-deprecated-declarations",
161                                    "-Wno-gnu-variable-sized-type-not-at-end",
162                                    "-Wno-pragma-once-outside-header",
163                                    "-Wno-address-of-packed-member",
164                                    "-Wno-unknown-warning-option",
165                                    "-fno-color-diagnostics",
166                                    "-fno-unwind-tables",
167                                    "-fno-asynchronous-unwind-tables",
168                                    "-x", "c", "-c", abs_file.c_str()});
169 
170   KBuildHelper kbuild_helper(kpath_env ? kpath : kdir, has_kpath_source);
171 
172   vector<string> kflags;
173   if (kbuild_helper.get_flags(un.machine, &kflags))
174     return -1;
175   if (flags_ & DEBUG_SOURCE)
176     flags_cstr.push_back("-g");
177   for (auto it = kflags.begin(); it != kflags.end(); ++it)
178     flags_cstr.push_back(it->c_str());
179 
180   vector<const char *> flags_cstr_rem;
181 
182   if (version_override) {
183     vmacro = "-DLINUX_VERSION_CODE_OVERRIDE=" + string(version_override);
184 
185     std::cout << "WARNING: Linux version for eBPF program is being overridden with: " << version_override << "\n";
186     std::cout << "WARNING: Due to this, the results of the program may be unpredictable\n";
187     flags_cstr_rem.push_back(vmacro.c_str());
188   }
189 
190   flags_cstr_rem.push_back("-include");
191   flags_cstr_rem.push_back("/virtual/include/bcc/helpers.h");
192   flags_cstr_rem.push_back("-isystem");
193   flags_cstr_rem.push_back("/virtual/include");
194   if (cflags) {
195     for (auto i = 0; i < ncflags; ++i)
196       flags_cstr_rem.push_back(cflags[i]);
197   }
198 #ifdef CUR_CPU_IDENTIFIER
199   string cur_cpu_flag = string("-DCUR_CPU_IDENTIFIER=") + CUR_CPU_IDENTIFIER;
200   flags_cstr_rem.push_back(cur_cpu_flag.c_str());
201 #endif
202 
203   if (do_compile(mod, ts, in_memory, flags_cstr, flags_cstr_rem, main_path,
204                  main_buf, id, func_src, mod_src, true, maps_ns)) {
205 #if BCC_BACKUP_COMPILE != 1
206     return -1;
207 #else
208     // try one more time to compile with system bpf.h
209     llvm::errs() << "WARNING: compilation failure, trying with system bpf.h\n";
210 
211     ts.DeletePrefix(Path({id}));
212     func_src.clear();
213     mod_src.clear();
214     if (do_compile(mod, ts, in_memory, flags_cstr, flags_cstr_rem, main_path,
215                    main_buf, id, func_src, mod_src, false, maps_ns))
216       return -1;
217 #endif
218   }
219 
220   return 0;
221 }
222 
get_clang_target_cb(bcc_arch_t arch)223 void *get_clang_target_cb(bcc_arch_t arch)
224 {
225   const char *ret;
226 
227   switch(arch) {
228     case BCC_ARCH_PPC_LE:
229       ret = "powerpc64le-unknown-linux-gnu";
230       break;
231     case BCC_ARCH_PPC:
232       ret = "powerpc64-unknown-linux-gnu";
233       break;
234     case BCC_ARCH_S390X:
235       ret = "s390x-ibm-linux-gnu";
236       break;
237     case BCC_ARCH_ARM64:
238       ret = "aarch64-unknown-linux-gnu";
239       break;
240     default:
241       ret = "x86_64-unknown-linux-gnu";
242   }
243 
244   return (void *)ret;
245 }
246 
get_clang_target(void)247 string get_clang_target(void) {
248   const char *ret;
249 
250   ret = (const char *)run_arch_callback(get_clang_target_cb);
251   return string(ret);
252 }
253 
do_compile(unique_ptr<llvm::Module> * mod,TableStorage & ts,bool in_memory,const vector<const char * > & flags_cstr_in,const vector<const char * > & flags_cstr_rem,const std::string & main_path,const unique_ptr<llvm::MemoryBuffer> & main_buf,const std::string & id,FuncSource & func_src,std::string & mod_src,bool use_internal_bpfh,const std::string & maps_ns)254 int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts,
255                             bool in_memory,
256                             const vector<const char *> &flags_cstr_in,
257                             const vector<const char *> &flags_cstr_rem,
258                             const std::string &main_path,
259                             const unique_ptr<llvm::MemoryBuffer> &main_buf,
260                             const std::string &id, FuncSource &func_src,
261                             std::string &mod_src, bool use_internal_bpfh,
262                             const std::string &maps_ns) {
263   using namespace clang;
264 
265   vector<const char *> flags_cstr = flags_cstr_in;
266   if (use_internal_bpfh) {
267     flags_cstr.push_back("-include");
268     flags_cstr.push_back("/virtual/include/bcc/bpf.h");
269   }
270   flags_cstr.insert(flags_cstr.end(), flags_cstr_rem.begin(),
271                     flags_cstr_rem.end());
272 
273   // set up the error reporting class
274   IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
275   auto diag_client = new TextDiagnosticPrinter(llvm::errs(), &*diag_opts);
276 
277   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
278   DiagnosticsEngine diags(DiagID, &*diag_opts, diag_client);
279 
280   // set up the command line argument wrapper
281 
282   string target_triple = get_clang_target();
283   driver::Driver drv("", target_triple, diags);
284 
285   drv.setTitle("bcc-clang-driver");
286   drv.setCheckInputsExist(false);
287 
288   unique_ptr<driver::Compilation> compilation(drv.BuildCompilation(flags_cstr));
289   if (!compilation)
290     return -1;
291 
292   // expect exactly 1 job, otherwise error
293   const driver::JobList &jobs = compilation->getJobs();
294   if (jobs.size() != 1 || !isa<driver::Command>(*jobs.begin())) {
295     SmallString<256> msg;
296     llvm::raw_svector_ostream os(msg);
297     jobs.Print(os, "; ", true);
298     diags.Report(diag::err_fe_expected_compiler_job) << os.str();
299     return -1;
300   }
301 
302   const driver::Command &cmd = cast<driver::Command>(*jobs.begin());
303   if (llvm::StringRef(cmd.getCreator().getName()) != "clang") {
304     diags.Report(diag::err_fe_expected_clang_command);
305     return -1;
306   }
307 
308   // Initialize a compiler invocation object from the clang (-cc1) arguments.
309   const llvm::opt::ArgStringList &ccargs = cmd.getArguments();
310 
311   if (flags_ & DEBUG_PREPROCESSOR) {
312     llvm::errs() << "clang";
313     for (auto arg : ccargs)
314       llvm::errs() << " " << arg;
315     llvm::errs() << "\n";
316   }
317 
318   // pre-compilation pass for generating tracepoint structures
319   CompilerInstance compiler0;
320   CompilerInvocation &invocation0 = compiler0.getInvocation();
321   if (!CompilerInvocation::CreateFromArgs(
322           invocation0, const_cast<const char **>(ccargs.data()),
323           const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
324     return -1;
325 
326   invocation0.getPreprocessorOpts().RetainRemappedFileBuffers = true;
327   for (const auto &f : remapped_headers_)
328     invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
329   for (const auto &f : remapped_footers_)
330     invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
331 
332   if (in_memory) {
333     invocation0.getPreprocessorOpts().addRemappedFile(main_path, &*main_buf);
334     invocation0.getFrontendOpts().Inputs.clear();
335     invocation0.getFrontendOpts().Inputs.push_back(FrontendInputFile(
336         main_path, FrontendOptions::getInputKindForExtension("c")));
337   }
338   invocation0.getFrontendOpts().DisableFree = false;
339 
340   compiler0.createDiagnostics(new IgnoringDiagConsumer());
341 
342   // capture the rewritten c file
343   string out_str;
344   llvm::raw_string_ostream os(out_str);
345   TracepointFrontendAction tpact(os);
346   compiler0.ExecuteAction(tpact); // ignore errors, they will be reported later
347   unique_ptr<llvm::MemoryBuffer> out_buf = llvm::MemoryBuffer::getMemBuffer(out_str);
348 
349   // first pass
350   CompilerInstance compiler1;
351   CompilerInvocation &invocation1 = compiler1.getInvocation();
352   if (!CompilerInvocation::CreateFromArgs(
353           invocation1, const_cast<const char **>(ccargs.data()),
354           const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
355     return -1;
356 
357   // This option instructs clang whether or not to free the file buffers that we
358   // give to it. Since the embedded header files should be copied fewer times
359   // and reused if possible, set this flag to true.
360   invocation1.getPreprocessorOpts().RetainRemappedFileBuffers = true;
361   for (const auto &f : remapped_headers_)
362     invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
363   for (const auto &f : remapped_footers_)
364     invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
365   invocation1.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf);
366   invocation1.getFrontendOpts().Inputs.clear();
367   invocation1.getFrontendOpts().Inputs.push_back(FrontendInputFile(
368       main_path, FrontendOptions::getInputKindForExtension("c")));
369   invocation1.getFrontendOpts().DisableFree = false;
370 
371   compiler1.createDiagnostics();
372 
373   // capture the rewritten c file
374   string out_str1;
375   llvm::raw_string_ostream os1(out_str1);
376   BFrontendAction bact(os1, flags_, ts, id, main_path, func_src, mod_src, maps_ns);
377   if (!compiler1.ExecuteAction(bact))
378     return -1;
379   unique_ptr<llvm::MemoryBuffer> out_buf1 = llvm::MemoryBuffer::getMemBuffer(out_str1);
380 
381   // second pass, clear input and take rewrite buffer
382   CompilerInstance compiler2;
383   CompilerInvocation &invocation2 = compiler2.getInvocation();
384   if (!CompilerInvocation::CreateFromArgs(
385           invocation2, const_cast<const char **>(ccargs.data()),
386           const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
387     return -1;
388   invocation2.getPreprocessorOpts().RetainRemappedFileBuffers = true;
389   for (const auto &f : remapped_headers_)
390     invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
391   for (const auto &f : remapped_footers_)
392     invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
393   invocation2.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf1);
394   invocation2.getFrontendOpts().Inputs.clear();
395   invocation2.getFrontendOpts().Inputs.push_back(FrontendInputFile(
396       main_path, FrontendOptions::getInputKindForExtension("c")));
397   invocation2.getFrontendOpts().DisableFree = false;
398   invocation2.getCodeGenOpts().DisableFree = false;
399   // Resort to normal inlining. In -O0 the default is OnlyAlwaysInlining and
400   // clang might add noinline attribute even for functions with inline hint.
401   invocation2.getCodeGenOpts().setInlining(CodeGenOptions::NormalInlining);
402   // suppress warnings in the 2nd pass, but bail out on errors (our fault)
403   invocation2.getDiagnosticOpts().IgnoreWarnings = true;
404   compiler2.createDiagnostics();
405 
406   EmitLLVMOnlyAction ir_act(&*ctx_);
407   if (!compiler2.ExecuteAction(ir_act))
408     return -1;
409   *mod = ir_act.takeModule();
410 
411   return 0;
412 }
413 
src(const std::string & name)414 const char * FuncSource::src(const std::string& name) {
415   auto src = funcs_.find(name);
416   if (src == funcs_.end())
417     return "";
418   return src->second.src_.data();
419 }
420 
src_rewritten(const std::string & name)421 const char * FuncSource::src_rewritten(const std::string& name) {
422   auto src = funcs_.find(name);
423   if (src == funcs_.end())
424     return "";
425   return src->second.src_rewritten_.data();
426 }
427 
set_src(const std::string & name,const std::string & src)428 void FuncSource::set_src(const std::string& name, const std::string& src) {
429   funcs_[name].src_ = src;
430 }
431 
set_src_rewritten(const std::string & name,const std::string & src)432 void FuncSource::set_src_rewritten(const std::string& name, const std::string& src) {
433   funcs_[name].src_rewritten_ = src;
434 }
435 
436 }  // namespace ebpf
437