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