loader.cc 14.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (c) 2015 PLUMgrid, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

17 18 19 20 21 22
#include <map>
#include <string>
#include <algorithm>
#include <fcntl.h>
#include <ftw.h>
#include <map>
23
#include <stdlib.h>
24 25 26
#include <stdio.h>
#include <string>
#include <sys/stat.h>
27
#include <sys/types.h>
28 29
#include <sys/utsname.h>
#include <unistd.h>
30
#include <utility>
31
#include <vector>
32
#include <iostream>
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
#include <linux/bpf.h>

#include <clang/Basic/FileManager.h>
#include <clang/Basic/TargetInfo.h>
#include <clang/CodeGen/BackendUtil.h>
#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Driver/Compilation.h>
#include <clang/Driver/Driver.h>
#include <clang/Driver/Job.h>
#include <clang/Driver/Tool.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Frontend/FrontendDiagnostic.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/FrontendTool/Utils.h>
49
#include <clang/Lex/PreprocessorOptions.h>
50 51 52

#include <llvm/IR/Module.h>

53
#include "bcc_exception.h"
54
#include "bpf_module.h"
55
#include "exported_files.h"
56 57
#include "kbuild_helper.h"
#include "b_frontend_action.h"
58
#include "tp_frontend_action.h"
59
#include "loader.h"
60
#include "arch_helper.h"
61 62 63 64 65 66

using std::map;
using std::string;
using std::unique_ptr;
using std::vector;

67 68
namespace ebpf {

69 70
ClangLoader::ClangLoader(llvm::LLVMContext *ctx, unsigned flags)
    : ctx_(ctx), flags_(flags)
71
{
72
  for (auto f : ExportedFiles::headers())
73 74 75
    remapped_headers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second);
  for (auto f : ExportedFiles::footers())
    remapped_footers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second);
76
}
77 78 79

ClangLoader::~ClangLoader() {}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
namespace
{

bool is_dir(const string& path)
{
  struct stat buf;

  if (::stat (path.c_str (), &buf) < 0)
    return false;

  return S_ISDIR(buf.st_mode);
}

std::pair<bool, string> get_kernel_path_info(const string kdir)
{
  if (is_dir(kdir + "/build") && is_dir(kdir + "/source"))
    return std::make_pair (true, "source");

  const char* suffix_from_env = ::getenv("BCC_KERNEL_MODULES_SUFFIX");
  if (suffix_from_env)
    return std::make_pair(false, string(suffix_from_env));

  return std::make_pair(false, "build");
}

}

107 108 109
int ClangLoader::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,
110 111
                       std::string &mod_src,
                       const std::string &maps_ns) {
112 113
  string main_path = "/virtual/main.c";
  unique_ptr<llvm::MemoryBuffer> main_buf;
114 115
  struct utsname un;
  uname(&un);
116 117
  string kdir, kpath;
  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 123 124 125 126 127 128 129 130 131 132

  if (kpath_env) {
    kpath = string(kpath_env);
  } else {
    kdir = string(KERNEL_MODULES_DIR) + "/" + un.release;
    auto kernel_path_info = get_kernel_path_info(kdir);
    has_kpath_source = kernel_path_info.first;
    kpath = kdir + "/" + kernel_path_info.second;
  }

  if (flags_ & DEBUG_PREPROCESSOR)
    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 137 138 139 140
  if (!dstack.ok())
    return -1;

  string abs_file;
  if (in_memory) {
141 142
    abs_file = main_path;
    main_buf = llvm::MemoryBuffer::getMemBuffer(file);
143 144 145 146 147 148 149
  } else {
    if (file.substr(0, 1) == "/")
      abs_file = file;
    else
      abs_file = string(dstack.cwd()) + "/" + file;
  }

chantra's avatar
chantra committed
150 151
  // -fno-color-diagnostics: this is a workaround for a bug in llvm terminalHasColors() as of
  // 22 Jul 2016. Also see bcc #615.
Edward Betts's avatar
Edward Betts committed
152
  // Enable -O2 for clang. In clang 5.0, -O0 may result in function marking as
153 154
  // noinline and optnone (if not always inlining).
  // Note that first argument is ignored in clang compilation invocation.
155 156 157
  // "-D __BPF_TRACING__" below is added to suppress a warning in 4.17+.
  // It can be removed once clang supports asm-goto or the kernel removes
  // 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 164
                                   "-Wno-address-of-packed-member",
                                   "-Wno-unknown-warning-option",
chantra's avatar
chantra committed
165
                                   "-fno-color-diagnostics",
166 167
                                   "-fno-unwind-tables",
                                   "-fno-asynchronous-unwind-tables",
168 169
                                   "-x", "c", "-c", abs_file.c_str()});

170 171
  KBuildHelper kbuild_helper(kpath_env ? kpath : kdir, has_kpath_source);

172
  vector<string> kflags;
173
  if (kbuild_helper.get_flags(un.machine, &kflags))
174
    return -1;
175 176
  if (flags_ & DEBUG_SOURCE)
    flags_cstr.push_back("-g");
177 178
  for (auto it = kflags.begin(); it != kflags.end(); ++it)
    flags_cstr.push_back(it->c_str());
179 180

  vector<const char *> flags_cstr_rem;
181 182 183 184 185 186 187 188 189

  if (version_override) {
    vmacro = "-DLINUX_VERSION_CODE_OVERRIDE=" + string(version_override);

    std::cout << "WARNING: Linux version for eBPF program is being overridden with: " << version_override << "\n";
    std::cout << "WARNING: Due to this, the results of the program may be unpredictable\n";
    flags_cstr_rem.push_back(vmacro.c_str());
  }

190 191 192 193
  flags_cstr_rem.push_back("-include");
  flags_cstr_rem.push_back("/virtual/include/bcc/helpers.h");
  flags_cstr_rem.push_back("-isystem");
  flags_cstr_rem.push_back("/virtual/include");
194 195
  if (cflags) {
    for (auto i = 0; i < ncflags; ++i)
196
      flags_cstr_rem.push_back(cflags[i]);
197
  }
198 199
#ifdef CUR_CPU_IDENTIFIER
  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 206 207 208 209 210 211 212 213 214
#if BCC_BACKUP_COMPILE != 1
    return -1;
#else
    // try one more time to compile with system bpf.h
    llvm::errs() << "WARNING: compilation failure, trying with system bpf.h\n";

    ts.DeletePrefix(Path({id}));
    func_src.clear();
    mod_src.clear();
    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 217 218 219 220 221 222
      return -1;
#endif
  }

  return 0;
}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
void *get_clang_target_cb(bcc_arch_t arch)
{
  const char *ret;

  switch(arch) {
    case BCC_ARCH_PPC_LE:
      ret = "powerpc64le-unknown-linux-gnu";
      break;
    case BCC_ARCH_PPC:
      ret = "powerpc64-unknown-linux-gnu";
      break;
    case BCC_ARCH_S390X:
      ret = "s390x-ibm-linux-gnu";
      break;
    case BCC_ARCH_ARM64:
      ret = "aarch64-unknown-linux-gnu";
      break;
    default:
      ret = "x86_64-unknown-linux-gnu";
  }

  return (void *)ret;
}

string get_clang_target(void) {
  const char *ret;

  ret = (const char *)run_arch_callback(get_clang_target_cb);
  return string(ret);
}

254 255 256 257 258 259 260
int ClangLoader::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,
261 262
                            std::string &mod_src, bool use_internal_bpfh,
                            const std::string &maps_ns) {
263 264 265 266 267 268 269 270 271 272
  using namespace clang;

  vector<const char *> flags_cstr = flags_cstr_in;
  if (use_internal_bpfh) {
    flags_cstr.push_back("-include");
    flags_cstr.push_back("/virtual/include/bcc/bpf.h");
  }
  flags_cstr.insert(flags_cstr.end(), flags_cstr_rem.begin(),
                    flags_cstr_rem.end());

273 274 275 276 277 278 279 280
  // set up the error reporting class
  IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
  auto diag_client = new TextDiagnosticPrinter(llvm::errs(), &*diag_opts);

  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
  DiagnosticsEngine diags(DiagID, &*diag_opts, diag_client);

  // set up the command line argument wrapper
281 282 283 284

  string target_triple = get_clang_target();
  driver::Driver drv("", target_triple, diags);

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
  drv.setTitle("bcc-clang-driver");
  drv.setCheckInputsExist(false);

  unique_ptr<driver::Compilation> compilation(drv.BuildCompilation(flags_cstr));
  if (!compilation)
    return -1;

  // expect exactly 1 job, otherwise error
  const driver::JobList &jobs = compilation->getJobs();
  if (jobs.size() != 1 || !isa<driver::Command>(*jobs.begin())) {
    SmallString<256> msg;
    llvm::raw_svector_ostream os(msg);
    jobs.Print(os, "; ", true);
    diags.Report(diag::err_fe_expected_compiler_job) << os.str();
    return -1;
  }

  const driver::Command &cmd = cast<driver::Command>(*jobs.begin());
  if (llvm::StringRef(cmd.getCreator().getName()) != "clang") {
    diags.Report(diag::err_fe_expected_clang_command);
    return -1;
  }

  // Initialize a compiler invocation object from the clang (-cc1) arguments.
309
  const llvm::opt::ArgStringList &ccargs = cmd.getArguments();
310

Jean-Tiare Le Bigot's avatar
Jean-Tiare Le Bigot committed
311
  if (flags_ & DEBUG_PREPROCESSOR) {
312 313 314 315 316 317
    llvm::errs() << "clang";
    for (auto arg : ccargs)
      llvm::errs() << " " << arg;
    llvm::errs() << "\n";
  }

318
  // pre-compilation pass for generating tracepoint structures
319 320
  CompilerInstance compiler0;
  CompilerInvocation &invocation0 = compiler0.getInvocation();
Brenden Blanco's avatar
Brenden Blanco committed
321 322 323
  if (!CompilerInvocation::CreateFromArgs(
          invocation0, const_cast<const char **>(ccargs.data()),
          const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
324 325
    return -1;

326
  invocation0.getPreprocessorOpts().RetainRemappedFileBuffers = true;
327 328 329
  for (const auto &f : remapped_headers_)
    invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
  for (const auto &f : remapped_footers_)
330
    invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
331 332

  if (in_memory) {
333 334
    invocation0.getPreprocessorOpts().addRemappedFile(main_path, &*main_buf);
    invocation0.getFrontendOpts().Inputs.clear();
335 336
    invocation0.getFrontendOpts().Inputs.push_back(FrontendInputFile(
        main_path, FrontendOptions::getInputKindForExtension("c")));
337
  }
338
  invocation0.getFrontendOpts().DisableFree = false;
339 340 341 342 343 344 345 346 347 348

  compiler0.createDiagnostics(new IgnoringDiagConsumer());

  // capture the rewritten c file
  string out_str;
  llvm::raw_string_ostream os(out_str);
  TracepointFrontendAction tpact(os);
  compiler0.ExecuteAction(tpact); // ignore errors, they will be reported later
  unique_ptr<llvm::MemoryBuffer> out_buf = llvm::MemoryBuffer::getMemBuffer(out_str);

349
  // first pass
350 351
  CompilerInstance compiler1;
  CompilerInvocation &invocation1 = compiler1.getInvocation();
Brenden Blanco's avatar
Brenden Blanco committed
352 353 354
  if (!CompilerInvocation::CreateFromArgs(
          invocation1, const_cast<const char **>(ccargs.data()),
          const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
355 356
    return -1;

357 358 359
  // This option instructs clang whether or not to free the file buffers that we
  // give to it. Since the embedded header files should be copied fewer times
  // and reused if possible, set this flag to true.
360
  invocation1.getPreprocessorOpts().RetainRemappedFileBuffers = true;
361 362 363
  for (const auto &f : remapped_headers_)
    invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
  for (const auto &f : remapped_footers_)
364 365 366
    invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
  invocation1.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf);
  invocation1.getFrontendOpts().Inputs.clear();
367 368
  invocation1.getFrontendOpts().Inputs.push_back(FrontendInputFile(
      main_path, FrontendOptions::getInputKindForExtension("c")));
369
  invocation1.getFrontendOpts().DisableFree = false;
370 371 372 373

  compiler1.createDiagnostics();

  // capture the rewritten c file
374 375
  string out_str1;
  llvm::raw_string_ostream os1(out_str1);
376
  BFrontendAction bact(os1, flags_, ts, id, main_path, func_src, mod_src, maps_ns);
377 378
  if (!compiler1.ExecuteAction(bact))
    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 383
  CompilerInstance compiler2;
  CompilerInvocation &invocation2 = compiler2.getInvocation();
Brenden Blanco's avatar
Brenden Blanco committed
384 385 386
  if (!CompilerInvocation::CreateFromArgs(
          invocation2, const_cast<const char **>(ccargs.data()),
          const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
387
    return -1;
388
  invocation2.getPreprocessorOpts().RetainRemappedFileBuffers = true;
389 390 391
  for (const auto &f : remapped_headers_)
    invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
  for (const auto &f : remapped_footers_)
392 393 394
    invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
  invocation2.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf1);
  invocation2.getFrontendOpts().Inputs.clear();
395 396
  invocation2.getFrontendOpts().Inputs.push_back(FrontendInputFile(
      main_path, FrontendOptions::getInputKindForExtension("c")));
397
  invocation2.getFrontendOpts().DisableFree = false;
Yonghong Song's avatar
Yonghong Song committed
398
  invocation2.getCodeGenOpts().DisableFree = false;
399 400 401
  // Resort to normal inlining. In -O0 the default is OnlyAlwaysInlining and
  // clang might add noinline attribute even for functions with inline hint.
  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 405 406 407 408 409 410 411 412 413
  compiler2.createDiagnostics();

  EmitLLVMOnlyAction ir_act(&*ctx_);
  if (!compiler2.ExecuteAction(ir_act))
    return -1;
  *mod = ir_act.takeModule();

  return 0;
}

Alexei Starovoitov's avatar
Alexei Starovoitov committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
const char * FuncSource::src(const std::string& name) {
  auto src = funcs_.find(name);
  if (src == funcs_.end())
    return "";
  return src->second.src_.data();
}

const char * FuncSource::src_rewritten(const std::string& name) {
  auto src = funcs_.find(name);
  if (src == funcs_.end())
    return "";
  return src->second.src_rewritten_.data();
}

void FuncSource::set_src(const std::string& name, const std::string& src) {
  funcs_[name].src_ = src;
}

void FuncSource::set_src_rewritten(const std::string& name, const std::string& src) {
  funcs_[name].src_rewritten_ = src;
}
435 436

}  // namespace ebpf