Home | History | Annotate | Download | only in clang
      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 
     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 
     78 ClangLoader::~ClangLoader() {}
     79 
     80 namespace
     81 {
     82 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    428 void FuncSource::set_src(const std::string& name, const std::string& src) {
    429   funcs_[name].src_ = src;
    430 }
    431 
    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