• 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 #include <fcntl.h>
17 #include <map>
18 #include <string>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <vector>
22 #include <linux/bpf.h>
23 
24 #include <llvm/ExecutionEngine/MCJIT.h>
25 #include <llvm/ExecutionEngine/SectionMemoryManager.h>
26 #include <llvm/IR/IRPrintingPasses.h>
27 #include <llvm/IR/LegacyPassManager.h>
28 #include <llvm/IR/LLVMContext.h>
29 #include <llvm/IR/Module.h>
30 #include <llvm/IR/Verifier.h>
31 #include <llvm/Support/TargetSelect.h>
32 #include <llvm/Transforms/IPO.h>
33 #include <llvm/Transforms/IPO/PassManagerBuilder.h>
34 #include <llvm-c/Transforms/IPO.h>
35 
36 #include "common.h"
37 #include "bcc_debug.h"
38 #include "frontends/b/loader.h"
39 #include "frontends/clang/loader.h"
40 #include "frontends/clang/b_frontend_action.h"
41 #include "bpf_module.h"
42 #include "exported_files.h"
43 #include "libbpf.h"
44 
45 namespace ebpf {
46 
47 using std::get;
48 using std::make_tuple;
49 using std::map;
50 using std::move;
51 using std::string;
52 using std::tuple;
53 using std::unique_ptr;
54 using std::vector;
55 using namespace llvm;
56 
57 const string BPFModule::FN_PREFIX = BPF_FN_PREFIX;
58 
59 // Snooping class to remember the sections as the JIT creates them
60 class MyMemoryManager : public SectionMemoryManager {
61  public:
62 
MyMemoryManager(map<string,tuple<uint8_t *,uintptr_t>> * sections)63   explicit MyMemoryManager(map<string, tuple<uint8_t *, uintptr_t>> *sections)
64       : sections_(sections) {
65   }
66 
~MyMemoryManager()67   virtual ~MyMemoryManager() {}
allocateCodeSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName)68   uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
69                                unsigned SectionID,
70                                StringRef SectionName) override {
71     uint8_t *Addr = SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID, SectionName);
72     //printf("allocateCodeSection: %s Addr %p Size %ld Alignment %d SectionID %d\n",
73     //       SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID);
74     (*sections_)[SectionName.str()] = make_tuple(Addr, Size);
75     return Addr;
76   }
allocateDataSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName,bool isReadOnly)77   uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
78                                unsigned SectionID, StringRef SectionName,
79                                bool isReadOnly) override {
80     uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, isReadOnly);
81     //printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d RO %d\n",
82     //       SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID, isReadOnly);
83     (*sections_)[SectionName.str()] = make_tuple(Addr, Size);
84     return Addr;
85   }
86   map<string, tuple<uint8_t *, uintptr_t>> *sections_;
87 };
88 
BPFModule(unsigned flags,TableStorage * ts,bool rw_engine_enabled,const std::string & maps_ns)89 BPFModule::BPFModule(unsigned flags, TableStorage *ts, bool rw_engine_enabled,
90                      const std::string &maps_ns)
91     : flags_(flags),
92       rw_engine_enabled_(rw_engine_enabled && bpf_module_rw_engine_enabled()),
93       used_b_loader_(false),
94       ctx_(new LLVMContext),
95       id_(std::to_string((uintptr_t)this)),
96       maps_ns_(maps_ns),
97       ts_(ts) {
98   initialize_rw_engine();
99   LLVMInitializeBPFTarget();
100   LLVMInitializeBPFTargetMC();
101   LLVMInitializeBPFTargetInfo();
102   LLVMInitializeBPFAsmPrinter();
103 #if LLVM_MAJOR_VERSION >= 6
104   LLVMInitializeBPFAsmParser();
105   if (flags & DEBUG_SOURCE)
106     LLVMInitializeBPFDisassembler();
107 #endif
108   LLVMLinkInMCJIT(); /* call empty function to force linking of MCJIT */
109   if (!ts_) {
110     local_ts_ = createSharedTableStorage();
111     ts_ = &*local_ts_;
112   }
113   func_src_ = ebpf::make_unique<FuncSource>();
114 }
115 
unimplemented_sscanf(const char *,void *)116 static StatusTuple unimplemented_sscanf(const char *, void *) {
117   return StatusTuple(-1, "sscanf unimplemented");
118 }
unimplemented_snprintf(char *,size_t,const void *)119 static StatusTuple unimplemented_snprintf(char *, size_t, const void *) {
120   return StatusTuple(-1, "snprintf unimplemented");
121 }
122 
~BPFModule()123 BPFModule::~BPFModule() {
124   for (auto &v : tables_) {
125     v->key_sscanf = unimplemented_sscanf;
126     v->leaf_sscanf = unimplemented_sscanf;
127     v->key_snprintf = unimplemented_snprintf;
128     v->leaf_snprintf = unimplemented_snprintf;
129   }
130 
131   if (!rw_engine_enabled_) {
132     for (auto section : sections_)
133       delete[] get<0>(section.second);
134   }
135 
136   engine_.reset();
137   cleanup_rw_engine();
138   ctx_.reset();
139   func_src_.reset();
140 
141   ts_->DeletePrefix(Path({id_}));
142 }
143 
144 // load an entire c file as a module
load_cfile(const string & file,bool in_memory,const char * cflags[],int ncflags)145 int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) {
146   ClangLoader clang_loader(&*ctx_, flags_);
147   if (clang_loader.parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_,
148                          *func_src_, mod_src_, maps_ns_))
149     return -1;
150   return 0;
151 }
152 
153 // NOTE: this is a duplicate of the above, but planning to deprecate if we
154 // settle on clang as the frontend
155 
156 // Load in a pre-built list of functions into the initial Module object, then
157 // build an ExecutionEngine.
load_includes(const string & text)158 int BPFModule::load_includes(const string &text) {
159   ClangLoader clang_loader(&*ctx_, flags_);
160   if (clang_loader.parse(&mod_, *ts_, text, true, nullptr, 0, "", *func_src_,
161                          mod_src_, ""))
162     return -1;
163   return 0;
164 }
165 
annotate_light()166 void BPFModule::annotate_light() {
167   for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn)
168     if (!fn->hasFnAttribute(Attribute::NoInline))
169       fn->addFnAttr(Attribute::AlwaysInline);
170 
171   size_t id = 0;
172   Path path({id_});
173   for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) {
174     TableDesc &table = it->second;
175     tables_.push_back(&it->second);
176     table_names_[table.name] = id++;
177   }
178 }
179 
dump_ir(Module & mod)180 void BPFModule::dump_ir(Module &mod) {
181   legacy::PassManager PM;
182   PM.add(createPrintModulePass(errs()));
183   PM.run(mod);
184 }
185 
run_pass_manager(Module & mod)186 int BPFModule::run_pass_manager(Module &mod) {
187   if (verifyModule(mod, &errs())) {
188     if (flags_ & DEBUG_LLVM_IR)
189       dump_ir(mod);
190     return -1;
191   }
192 
193   legacy::PassManager PM;
194   PassManagerBuilder PMB;
195   PMB.OptLevel = 3;
196   PM.add(createFunctionInliningPass());
197   /*
198    * llvm < 4.0 needs
199    * PM.add(createAlwaysInlinerPass());
200    * llvm >= 4.0 needs
201    * PM.add(createAlwaysInlinerLegacyPass());
202    * use below 'stable' workaround
203    */
204   LLVMAddAlwaysInlinerPass(reinterpret_cast<LLVMPassManagerRef>(&PM));
205   PMB.populateModulePassManager(PM);
206   if (flags_ & DEBUG_LLVM_IR)
207     PM.add(createPrintModulePass(outs()));
208   PM.run(mod);
209   return 0;
210 }
211 
finalize()212 int BPFModule::finalize() {
213   Module *mod = &*mod_;
214   std::map<std::string, std::tuple<uint8_t *, uintptr_t>> tmp_sections,
215       *sections_p;
216 
217   mod->setTargetTriple("bpf-pc-linux");
218 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
219   mod->setDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128");
220 #else
221   mod->setDataLayout("E-m:e-p:64:64-i64:64-n32:64-S128");
222 #endif
223   sections_p = rw_engine_enabled_ ? &sections_ : &tmp_sections;
224 
225   string err;
226   EngineBuilder builder(move(mod_));
227   builder.setErrorStr(&err);
228   builder.setMCJITMemoryManager(ebpf::make_unique<MyMemoryManager>(sections_p));
229   builder.setMArch("bpf");
230   builder.setUseOrcMCJITReplacement(false);
231   engine_ = unique_ptr<ExecutionEngine>(builder.create());
232   if (!engine_) {
233     fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str());
234     return -1;
235   }
236 
237   if (flags_ & DEBUG_SOURCE)
238     engine_->setProcessAllSections(true);
239 
240   if (int rc = run_pass_manager(*mod))
241     return rc;
242 
243   engine_->finalizeObject();
244 
245   if (flags_ & DEBUG_SOURCE) {
246     SourceDebugger src_debugger(mod, *sections_p, FN_PREFIX, mod_src_,
247                                 src_dbg_fmap_);
248     src_debugger.dump();
249   }
250 
251   if (!rw_engine_enabled_) {
252     // Setup sections_ correctly and then free llvm internal memory
253     for (auto section : tmp_sections) {
254       auto fname = section.first;
255       uintptr_t size = get<1>(section.second);
256       uint8_t *tmp_p = NULL;
257       // Only copy data for non-map sections
258       if (strncmp("maps/", section.first.c_str(), 5)) {
259         uint8_t *addr = get<0>(section.second);
260         tmp_p = new uint8_t[size];
261         memcpy(tmp_p, addr, size);
262       }
263       sections_[fname] = make_tuple(tmp_p, size);
264     }
265     engine_.reset();
266     ctx_.reset();
267   }
268 
269   // give functions an id
270   for (auto section : sections_)
271     if (!strncmp(FN_PREFIX.c_str(), section.first.c_str(), FN_PREFIX.size()))
272       function_names_.push_back(section.first);
273 
274   return 0;
275 }
276 
num_functions() const277 size_t BPFModule::num_functions() const {
278   return function_names_.size();
279 }
280 
function_name(size_t id) const281 const char * BPFModule::function_name(size_t id) const {
282   if (id >= function_names_.size())
283     return nullptr;
284   return function_names_[id].c_str() + FN_PREFIX.size();
285 }
286 
function_start(size_t id) const287 uint8_t * BPFModule::function_start(size_t id) const {
288   if (id >= function_names_.size())
289     return nullptr;
290   auto section = sections_.find(function_names_[id]);
291   if (section == sections_.end())
292     return nullptr;
293   return get<0>(section->second);
294 }
295 
function_start(const string & name) const296 uint8_t * BPFModule::function_start(const string &name) const {
297   auto section = sections_.find(FN_PREFIX + name);
298   if (section == sections_.end())
299     return nullptr;
300 
301   return get<0>(section->second);
302 }
303 
function_source(const string & name) const304 const char * BPFModule::function_source(const string &name) const {
305   return func_src_->src(name);
306 }
307 
function_source_rewritten(const string & name) const308 const char * BPFModule::function_source_rewritten(const string &name) const {
309   return func_src_->src_rewritten(name);
310 }
311 
annotate_prog_tag(const string & name,int prog_fd,struct bpf_insn * insns,int prog_len)312 int BPFModule::annotate_prog_tag(const string &name, int prog_fd,
313                                  struct bpf_insn *insns, int prog_len) {
314   unsigned long long tag1, tag2;
315   int err;
316 
317   err = bpf_prog_compute_tag(insns, prog_len, &tag1);
318   if (err)
319     return err;
320   err = bpf_prog_get_tag(prog_fd, &tag2);
321   if (err)
322     return err;
323   if (tag1 != tag2) {
324     fprintf(stderr, "prog tag mismatch %llx %llx\n", tag1, tag2);
325     return -1;
326   }
327 
328   err = mkdir(BCC_PROG_TAG_DIR, 0777);
329   if (err && errno != EEXIST) {
330     fprintf(stderr, "cannot create " BCC_PROG_TAG_DIR "\n");
331     return -1;
332   }
333 
334   char buf[128];
335   ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx", tag1);
336   err = mkdir(buf, 0777);
337   if (err && errno != EEXIST) {
338     fprintf(stderr, "cannot create %s\n", buf);
339     return -1;
340   }
341 
342   ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.c",
343              tag1, name.data());
344   FileDesc fd(open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644));
345   if (fd < 0) {
346     fprintf(stderr, "cannot create %s\n", buf);
347     return -1;
348   }
349 
350   const char *src = function_source(name);
351   write(fd, src, strlen(src));
352 
353   ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.rewritten.c",
354              tag1, name.data());
355   fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644);
356   if (fd < 0) {
357     fprintf(stderr, "cannot create %s\n", buf);
358     return -1;
359   }
360 
361   src = function_source_rewritten(name);
362   write(fd, src, strlen(src));
363 
364   if (!src_dbg_fmap_[name].empty()) {
365     ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.dis.txt",
366                tag1, name.data());
367     fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644);
368     if (fd < 0) {
369       fprintf(stderr, "cannot create %s\n", buf);
370       return -1;
371     }
372 
373     const char *src = src_dbg_fmap_[name].c_str();
374     write(fd, src, strlen(src));
375   }
376 
377   return 0;
378 }
379 
function_size(size_t id) const380 size_t BPFModule::function_size(size_t id) const {
381   if (id >= function_names_.size())
382     return 0;
383   auto section = sections_.find(function_names_[id]);
384   if (section == sections_.end())
385     return 0;
386   return get<1>(section->second);
387 }
388 
function_size(const string & name) const389 size_t BPFModule::function_size(const string &name) const {
390   auto section = sections_.find(FN_PREFIX + name);
391   if (section == sections_.end())
392     return 0;
393 
394   return get<1>(section->second);
395 }
396 
license() const397 char * BPFModule::license() const {
398   auto section = sections_.find("license");
399   if (section == sections_.end())
400     return nullptr;
401 
402   return (char *)get<0>(section->second);
403 }
404 
kern_version() const405 unsigned BPFModule::kern_version() const {
406   auto section = sections_.find("version");
407   if (section == sections_.end())
408     return 0;
409 
410   return *(unsigned *)get<0>(section->second);
411 }
412 
num_tables() const413 size_t BPFModule::num_tables() const { return tables_.size(); }
414 
table_id(const string & name) const415 size_t BPFModule::table_id(const string &name) const {
416   auto it = table_names_.find(name);
417   if (it == table_names_.end()) return ~0ull;
418   return it->second;
419 }
420 
table_fd(const string & name) const421 int BPFModule::table_fd(const string &name) const {
422   return table_fd(table_id(name));
423 }
424 
table_fd(size_t id) const425 int BPFModule::table_fd(size_t id) const {
426   if (id >= tables_.size())
427     return -1;
428   return tables_[id]->fd;
429 }
430 
table_type(const string & name) const431 int BPFModule::table_type(const string &name) const {
432   return table_type(table_id(name));
433 }
434 
table_type(size_t id) const435 int BPFModule::table_type(size_t id) const {
436   if (id >= tables_.size())
437     return -1;
438   return tables_[id]->type;
439 }
440 
table_max_entries(const string & name) const441 size_t BPFModule::table_max_entries(const string &name) const {
442   return table_max_entries(table_id(name));
443 }
444 
table_max_entries(size_t id) const445 size_t BPFModule::table_max_entries(size_t id) const {
446   if (id >= tables_.size())
447     return 0;
448   return tables_[id]->max_entries;
449 }
450 
table_flags(const string & name) const451 int BPFModule::table_flags(const string &name) const {
452   return table_flags(table_id(name));
453 }
454 
table_flags(size_t id) const455 int BPFModule::table_flags(size_t id) const {
456   if (id >= tables_.size())
457     return -1;
458   return tables_[id]->flags;
459 }
460 
table_name(size_t id) const461 const char * BPFModule::table_name(size_t id) const {
462   if (id >= tables_.size())
463     return nullptr;
464   return tables_[id]->name.c_str();
465 }
466 
table_key_desc(size_t id) const467 const char * BPFModule::table_key_desc(size_t id) const {
468   if (used_b_loader_) return nullptr;
469   if (id >= tables_.size())
470     return nullptr;
471   return tables_[id]->key_desc.c_str();
472 }
473 
table_key_desc(const string & name) const474 const char * BPFModule::table_key_desc(const string &name) const {
475   return table_key_desc(table_id(name));
476 }
477 
table_leaf_desc(size_t id) const478 const char * BPFModule::table_leaf_desc(size_t id) const {
479   if (used_b_loader_) return nullptr;
480   if (id >= tables_.size())
481     return nullptr;
482   return tables_[id]->leaf_desc.c_str();
483 }
484 
table_leaf_desc(const string & name) const485 const char * BPFModule::table_leaf_desc(const string &name) const {
486   return table_leaf_desc(table_id(name));
487 }
table_key_size(size_t id) const488 size_t BPFModule::table_key_size(size_t id) const {
489   if (id >= tables_.size())
490     return 0;
491   return tables_[id]->key_size;
492 }
table_key_size(const string & name) const493 size_t BPFModule::table_key_size(const string &name) const {
494   return table_key_size(table_id(name));
495 }
496 
table_leaf_size(size_t id) const497 size_t BPFModule::table_leaf_size(size_t id) const {
498   if (id >= tables_.size())
499     return 0;
500   return tables_[id]->leaf_size;
501 }
table_leaf_size(const string & name) const502 size_t BPFModule::table_leaf_size(const string &name) const {
503   return table_leaf_size(table_id(name));
504 }
505 
506 struct TableIterator {
TableIteratorebpf::TableIterator507   TableIterator(size_t key_size, size_t leaf_size)
508       : key(new uint8_t[key_size]), leaf(new uint8_t[leaf_size]) {
509   }
510   unique_ptr<uint8_t[]> key;
511   unique_ptr<uint8_t[]> leaf;
512   uint8_t keyb[512];
513 };
514 
table_key_printf(size_t id,char * buf,size_t buflen,const void * key)515 int BPFModule::table_key_printf(size_t id, char *buf, size_t buflen, const void *key) {
516   if (id >= tables_.size())
517     return -1;
518   const TableDesc &desc = *tables_[id];
519   StatusTuple rc = desc.key_snprintf(buf, buflen, key);
520   if (rc.code() < 0) {
521     fprintf(stderr, "%s\n", rc.msg().c_str());
522     return -1;
523   }
524   return 0;
525 }
526 
table_leaf_printf(size_t id,char * buf,size_t buflen,const void * leaf)527 int BPFModule::table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf) {
528   if (id >= tables_.size())
529     return -1;
530   const TableDesc &desc = *tables_[id];
531   StatusTuple rc = desc.leaf_snprintf(buf, buflen, leaf);
532   if (rc.code() < 0) {
533     fprintf(stderr, "%s\n", rc.msg().c_str());
534     return -1;
535   }
536   return 0;
537 }
538 
table_key_scanf(size_t id,const char * key_str,void * key)539 int BPFModule::table_key_scanf(size_t id, const char *key_str, void *key) {
540   if (id >= tables_.size())
541     return -1;
542   const TableDesc &desc = *tables_[id];
543   StatusTuple rc = desc.key_sscanf(key_str, key);
544   if (rc.code() < 0) {
545     fprintf(stderr, "%s\n", rc.msg().c_str());
546     return -1;
547   }
548   return 0;
549 }
550 
table_leaf_scanf(size_t id,const char * leaf_str,void * leaf)551 int BPFModule::table_leaf_scanf(size_t id, const char *leaf_str, void *leaf) {
552   if (id >= tables_.size())
553     return -1;
554   const TableDesc &desc = *tables_[id];
555   StatusTuple rc = desc.leaf_sscanf(leaf_str, leaf);
556   if (rc.code() < 0) {
557     fprintf(stderr, "%s\n", rc.msg().c_str());
558     return -1;
559   }
560   return 0;
561 }
562 
563 // load a B file, which comes in two parts
load_b(const string & filename,const string & proto_filename)564 int BPFModule::load_b(const string &filename, const string &proto_filename) {
565   if (!sections_.empty()) {
566     fprintf(stderr, "Program already initialized\n");
567     return -1;
568   }
569   if (filename.empty() || proto_filename.empty()) {
570     fprintf(stderr, "Invalid filenames\n");
571     return -1;
572   }
573 
574   // Helpers are inlined in the following file (C). Load the definitions and
575   // pass the partially compiled module to the B frontend to continue with.
576   auto helpers_h = ExportedFiles::headers().find("/virtual/include/bcc/helpers.h");
577   if (helpers_h == ExportedFiles::headers().end()) {
578     fprintf(stderr, "Internal error: missing bcc/helpers.h");
579     return -1;
580   }
581   if (int rc = load_includes(helpers_h->second))
582     return rc;
583 
584   BLoader b_loader(flags_);
585   used_b_loader_ = true;
586   if (int rc = b_loader.parse(&*mod_, filename, proto_filename, *ts_, id_,
587                               maps_ns_))
588     return rc;
589   if (rw_engine_enabled_) {
590     if (int rc = annotate())
591       return rc;
592   } else {
593     annotate_light();
594   }
595   if (int rc = finalize())
596     return rc;
597   return 0;
598 }
599 
600 // load a C file
load_c(const string & filename,const char * cflags[],int ncflags)601 int BPFModule::load_c(const string &filename, const char *cflags[], int ncflags) {
602   if (!sections_.empty()) {
603     fprintf(stderr, "Program already initialized\n");
604     return -1;
605   }
606   if (filename.empty()) {
607     fprintf(stderr, "Invalid filename\n");
608     return -1;
609   }
610   if (int rc = load_cfile(filename, false, cflags, ncflags))
611     return rc;
612   if (rw_engine_enabled_) {
613     if (int rc = annotate())
614       return rc;
615   } else {
616     annotate_light();
617   }
618   if (int rc = finalize())
619     return rc;
620   return 0;
621 }
622 
623 // load a C text string
load_string(const string & text,const char * cflags[],int ncflags)624 int BPFModule::load_string(const string &text, const char *cflags[], int ncflags) {
625   if (!sections_.empty()) {
626     fprintf(stderr, "Program already initialized\n");
627     return -1;
628   }
629   if (int rc = load_cfile(text, true, cflags, ncflags))
630     return rc;
631   if (rw_engine_enabled_) {
632     if (int rc = annotate())
633       return rc;
634   } else {
635     annotate_light();
636   }
637 
638   if (int rc = finalize())
639     return rc;
640   return 0;
641 }
642 
643 } // namespace ebpf
644