diff --git a/kraft/app/app.py b/kraft/app/app.py index cb5df0a9..ccbed8af 100644 --- a/kraft/app/app.py +++ b/kraft/app/app.py @@ -91,6 +91,16 @@ class Application(Component): + # path of file where the names of functions called via gate are collected + VMEPT_FUNC_LIST_PATH = "/tmp/flexos_vmept_rpc_id_data" + VMEPT_FUNC_FUNC_WLIST_PATH = "/tmp/flexos_vmept_wl" + + # name and path of auto-generated header file containing the function addresses + VMEPT_ADDR_TABLE_NAME = "vmept_addr_table" + + # where to put auto-generated header files (relative to the unikraft build directory) + VMEPT_AUTOGEN_INCLUDE_PATH = "lib/flexos-core/include/flexos/impl" + _type = ComponentType.APP _config = None @@ -605,6 +615,31 @@ def fetch(ctx, self, verbose=False): return self.make(extra, verbose) + # generate a header containing an array with the addresses of functions called via EPT-gate + def vmept_gen_address_table_header(self, func_names, header_name, addr_map = {}, write_to_file = True, backuppath = None): + include_guard = "%s_H" % header_name.upper() + header_code = "#ifndef %s\n" % include_guard + header_code += "#define %s\n\n" % include_guard + header_code += 'static const void* flexos_vmept_addr_table[] __attribute__((section (".rodata"))) = {\n' + num_entries = len(func_names) + i = 0 + for fname in func_names: + i += 1 + sep = "," if i < num_entries else " " + addr = addr_map[fname] if fname in addr_map else 0 + header_code += "\t(void*) 0x%016x%s /* %s */\n" % (addr, sep, fname) + header_code += "};\n\n" + header_code += "#endif /* %s */" % include_guard + if write_to_file: + addr_table_path = os.path.join(self.unikraft.localdir, self.VMEPT_AUTOGEN_INCLUDE_PATH) + addr_table_path = os.path.join(addr_table_path, self.VMEPT_ADDR_TABLE_NAME + ".h") + with open(addr_table_path, "w") as addr_table_file: + addr_table_file.write(header_code) + if backuppath is not None: + with open(backuppath, "w") as backupfile: + backupfile.write(header_code) + return header_code + @click.pass_context def compartmentalize(ctx, self): self.find_files() @@ -792,29 +827,72 @@ def simple_replace(template_path, path, marker, shstack_enabled=True): cmd = ["diff", "-urNp", backup_src, filep] subprocess.call(cmd, stdout=fulldiff, stderr=subprocess.STDOUT) + is_ept = type(self.compartments[0].mechanism.driver) == VMEPTDriver + if is_ept and not os.path.isfile(self.VMEPT_FUNC_LIST_PATH): + # make sure this file exists + rpc_id_file = open(self.VMEPT_FUNC_LIST_PATH, "w") + rpc_id_file.close() + # now do library-specific rewrites + #TODO remove + logger.debug("## libraries ##") for lib in self.libraries: + # TODO remove + logger.debug(lib.name) + # first add per-library linker scripts if (not lib.compartment.default): add_local_linkerscript(lib, fulldiff=fulldiff) # then generate cocci files dynamically from the template - gr_rule_template = get_sec_rule("gatereplacer.cocci.in") + if is_ept: + gr_rule_template = get_sec_rule("gatereplacer_ept.cocci.in") + else: + gr_rule_template = get_sec_rule("gatereplacer.cocci.in") if FCALLS_enabled: cb_rule_template = get_sec_rule("rmcallbacks.cocci.in") + elif is_ept: + cb_rule_template = get_sec_rule("callbackreplacer_ept.cocci.in") else: cb_rule_template = get_sec_rule("callbackreplacer.cocci.in") + gr_rule_template = gr_rule_template.replace("{{ comp_cur_nb }}", str(lib.compartment.number)) + ept_rpc_id_prefix = "_RPC_ID_" if is_ept else "" cb_rule_template = cb_rule_template.replace("{{ comp_cur_nb }}", str(lib.compartment.number)) gr_rule = "" + if is_ept: + + logger.debug("%s compartments" % str(len(self.compartments))) + rpc_id_gen_template = get_sec_rule("rpc_id_gen.cocci.in") + + rpc_id_gen_template = rpc_id_gen_template.replace("{{ comp_cnt }}", + str(len(self.compartments))) + rpc_id_gen_template = rpc_id_gen_template.replace("{{ filename }}", + "'" + self.VMEPT_FUNC_LIST_PATH + "'") + rpc_id_gen_template = rpc_id_gen_template.replace("{{ func_wl_base }}", + "'" + self.VMEPT_FUNC_FUNC_WLIST_PATH + "'") + gr_rule = rpc_id_gen_template + "\n" + + # for EPT calls to some libraries are always local + # FIXME some libraries are only temporarily here to build nginx/redis + ept_defaultlibs = [ "libukdebug", "ukdebug" + , "libuklock", "uklock", "liblock" + , "libukswrand", "ukswrand" + , "libukplat", "ukplat" + , "libuksched", "uksched" + , "ukboot", "libukboot" + #, "ukmpi", "libukmpi" + #, "libc" + ] def gr_gen_rule(dest_name, dest_comp): gr_rule = str(gr_rule_template) name = dest_name - if (dest_name != "lname" and not dest_name.startswith("app-")): + if (dest_name != "lname" and not dest_name.startswith("app-") + and not dest_name.startswith("lib") and (dest_name not in ept_defaultlibs)): name = "lib" + name.replace("-", "") gr_rule = gr_rule.replace("{{ lib_dest_name }}", name) @@ -825,6 +903,17 @@ def gr_gen_rule(dest_name, dest_comp): if dest_comp == lib.compartment: # FIXME magic value, put somewhere gr_gate = "flexos_nop_gate" + gr_rule = gr_rule.replace("{{ ept_id_prefix }}", "") + elif is_ept: + if (name in ept_defaultlibs): + # TODO remove + logger.debug("inserted nop_gate for %s (rewriting lib: %s)" % (name, lib.name)) + gr_gate = "flexos_nop_gate" + gr_rule = gr_rule.replace("{{ ept_id_prefix }}", "") + else: + gr_rule = gr_rule.replace("{{ ept_id_prefix }}", ept_rpc_id_prefix) + # TODO remove + logger.debug("inserted vmept_gate for %s (rewriting lib: %s)" % (name, lib.name)) gr_rule = gr_rule.replace("{{ gate }}", gr_gate) gr_rule = gr_rule.replace("{{ gate_r }}", gr_gate + "_r") @@ -841,7 +930,8 @@ def cb_gen_rule(dest_name, dest_comp): name = dest_name.replace("-", "") if (dest_name != "lname" and not dest_name.startswith("app") - and not dest_name.startswith("lib")): + and not dest_name.startswith("lib") + and (dest_name not in ept_defaultlibs)): name = "lib" + name cb_rule = cb_rule.replace("{{ lib_from_name }}", name) @@ -852,6 +942,17 @@ def cb_gen_rule(dest_name, dest_comp): if dest_comp == lib.compartment: # FIXME magic value, put somewhere cb_gate = "flexos_nop_gate" + cb_rule = cb_rule.replace("{{ ept_id_prefix }}", "") + elif is_ept: + if (name in ept_defaultlibs): + # TODO remove + logger.debug("inserted nop_gate for %s (callback, rewriting lib: %s)" % (name, lib.name)) + cb_gate = "flexos_nop_gate" + cb_rule = cb_rule.replace("{{ ept_id_prefix }}", "") + else: + # TODO remove + logger.debug("inserted vmept_gate for %s (callback, rewriting lib: %s)" % (name, lib.name)) + cb_rule = cb_rule.replace("{{ ept_id_prefix }}", ept_rpc_id_prefix) cb_rule = cb_rule.replace("{{ gate }}", cb_gate) cb_rule = cb_rule.replace("{{ gate_r }}", cb_gate + "_r") @@ -868,6 +969,7 @@ def cb_gen_rule(dest_name, dest_comp): # with future upstream releases of Coccinelle, talking about it with Julia whitelisted_libs = ["libvfscore", "libuknetdev", "newlib", "libuksched", "libuksignal", "libukboot"] + for dest_lib in self.libraries: if (not dest_lib.compartment.default) and (dest_lib.name != lib.name): # this library is not in the default compartment, add a specific rule @@ -885,6 +987,10 @@ def cb_gen_rule(dest_name, dest_comp): # default rule, lname will match anything so this rule has to be at the end default_comp = [comp for comp in self.compartments if comp.default][0] + if is_ept: + # this increases the number of rules but is needed for EPT + for libname in ept_defaultlibs: + gr_rule += gr_gen_rule(libname, default_comp) gr_rule += gr_gen_rule("lname", default_comp) # add malloc/free/calloc replacement rule @@ -920,6 +1026,34 @@ def cb_gen_rule(dest_name, dest_comp): coccinelle_rewrite(lib, rule_file, fulldiff) + # generate a header with macros defining IDs for all + # functions given by function_names + def vmept_gen_rpc_id_macro_header(function_names, header_name): + include_guard = "%s_H" % header_name.upper() + header_code = "#ifndef %s\n" % include_guard + header_code += "#define %s\n\n" % include_guard + rpc_id = 0 + for fname in function_names: + header_code += "#define %s%s %d\n" % (ept_rpc_id_prefix, fname, rpc_id) + rpc_id += 1 + header_code += "\n#define FLEXOS_VMEPT_RPCID_CNT %d\n\n" % rpc_id + header_code += "#endif /* %s */" % include_guard + return header_code + + if (is_ept): + rpc_id_file = open(self.VMEPT_FUNC_LIST_PATH, "r") + fnames = list(map(str.strip, rpc_id_file.readlines())) + rpc_id_file.close() + id_header_name = "vmept_rpc_id" + addr_table_name = "flexos_%s" % self.VMEPT_ADDR_TABLE_NAME + id_header_code = vmept_gen_rpc_id_macro_header(fnames, id_header_name) + addr_table_code = self.vmept_gen_address_table_header(fnames, addr_table_name, write_to_file = True) + id_header_path = os.path.join(self.unikraft.localdir, self.VMEPT_AUTOGEN_INCLUDE_PATH) + id_header_path = os.path.join(id_header_path, id_header_name + ".h") + id_header_file = open(id_header_path, "w") + id_header_file.write(id_header_code) + id_header_file.close() + logger.info("Full diff of rewritings: %s" % fulldifff) fulldiff.close() diff --git a/kraft/cmd/build.py b/kraft/cmd/build.py index 9e39c4a9..72737ef6 100644 --- a/kraft/cmd/build.py +++ b/kraft/cmd/build.py @@ -80,10 +80,35 @@ def simple_replace(template_path, marker): stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) appcomp = None + appcompnum = 0 # assume app is in comp0 for lib in app.libraries: + # TODO + logger.info("lib: %s, compartment: %s" % (lib.name, str(lib.compartment.number))) if lib.name.startswith("app"): appcomp = lib.compartment + if appcomp is not None: + appcompnum = appcomp.number + + # get VMEPT config options + max_threads_shift = 8 # max hreads is (1 << max_threads_shift) + with open(os.path.join(app.localdir, ".config")) as conf: + conf_data = conf.read() + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_256=y' in conf_data: + max_threads_shift = 8 + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_128=y' in conf_data: + max_threads_shift = 7 + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_64=y' in conf_data: + max_threads_shift = 6 + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_32=y' in conf_data: + max_threads_shift = 5 + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_16=y' in conf_data: + max_threads_shift = 4 + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_8=y' in conf_data: + max_threads_shift = 3 + if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_4=y' in conf_data: + max_threads_shift = 2 + i = 0 logger.info("Building with VM/EPT: " + str(no_ept) + " images to build.") for comp in app.compartments: @@ -92,7 +117,8 @@ def simple_replace(template_path, marker): # edit build args extra_args = ["CFLAGS_EXTRA=-DFLEXOS_VMEPT_COMP_ID=" + str(i) + " -DFLEXOS_VMEPT_COMP_COUNT=" + str(no_ept) - + " -DFLEXOS_VMEPT_APPCOMP=" + str(appcomp.number)] + + " -DFLEXOS_VMEPT_APPCOMP=" + str(appcompnum) + + " -DFLEXOS_VMEPT_MAX_THREADS_SHIFT=" + str(max_threads_shift)] if fast: extra_args.insert(0, "-j") @@ -111,6 +137,46 @@ def simple_replace(template_path, marker): "of the make command (code " + str(return_code) + ")") return + for target in app.binaries: + # insert correct addresses + with open(Application.VMEPT_FUNC_LIST_PATH, "r") as funcname_file: + fnames = list(map(str.strip, funcname_file.readlines())) + + with open(Application.VMEPT_FUNC_FUNC_WLIST_PATH + str(i), "r") as wl_file: + fnames_wl = list(map(str.strip, wl_file.readlines())) + + addr_map = {} + for fname in fnames: + addr_map[fname] = 0 + + readelf = subprocess.Popen(['readelf', '-sW', target.binary_debug], stdout = subprocess.PIPE) + output = subprocess.check_output(['awk', '{if ($4 == "FUNC") { print $2,$8 } }'], stdin = readelf.stdout).decode() + readelf.wait() + + raw_lines = output.splitlines() + + entries = [] + for line in raw_lines: + fields = line.split() + address = int(fields[0], 16) + name = fields[1] + if name in addr_map and name in fnames_wl: + addr_map[name] = address + + patched_code = app.vmept_gen_address_table_header(fnames, + Application.VMEPT_ADDR_TABLE_NAME, addr_map, + write_to_file = True, backuppath = "/tmp/flexos_vmept_addr_table_" + str(i)) + + # build again + return_code = make_progressbar(app.make_raw(verbose=verbose, + extra=extra_args)) + + if (return_code > 0): + # there was probably an error + logger.error("Aborting build due to probable error during execution " + "of the make command (code " + str(return_code) + ")") + return + # secure image (so that it doesn't get overwritten) for target in app.binaries: os.rename(target.binary, target.binary + ".comp" + str(i)) diff --git a/kraft/sec/__init__.py b/kraft/sec/__init__.py index 17c97c63..3d088432 100644 --- a/kraft/sec/__init__.py +++ b/kraft/sec/__init__.py @@ -150,7 +150,9 @@ def coccinelle_rewrite(lib, rule_file, fulldiff): with open(log, 'wb') as logf: try: - cmd1 = ["spatch", "-j", "6", "-in_place", "-sp_file", rule_file, file] + # we can't use parallel jobs when building for EPT since this leads to + # @finalize scripts being called too early + cmd1 = ["spatch", "-in_place", "-sp_file", rule_file, file] cmd2 = ["diff", "-urNp", backup_src, file] logf.write(bytes("$ " + " ".join(cmd1) + "\n", 'utf-8')) logf.flush() diff --git a/kraft/sec/replacements/ukboot_init_sections.in b/kraft/sec/replacements/ukboot_init_sections.in index 7b593f13..6641c615 100644 --- a/kraft/sec/replacements/ukboot_init_sections.in +++ b/kraft/sec/replacements/ukboot_init_sections.in @@ -2,4 +2,4 @@ (void *) __uk_image_symbol(_ecomp{{ comp_nr }})); PROTECT_SECTION("bss_comp{{ comp_nr }}", {{ comp_nr }}, (void *) __uk_image_symbol(_bss_comp{{ comp_nr }}), (void *) __uk_image_symbol(_ebss_comp{{ comp_nr }})); - ASSIGN_HEAP("comp{{ comp_nr }}", {{ comp_nr }} /* key */, 1000 /* size */, flexos_comp{{ comp_nr }}_alloc); + ASSIGN_HEAP("comp{{ comp_nr }}", {{ comp_nr }} /* key */, CONFIG_LIBFLEXOS_COMP_HEAP_SIZE /* size */, flexos_comp{{ comp_nr }}_alloc); diff --git a/kraft/sec/rules/callbackreplacer_ept.cocci.in b/kraft/sec/rules/callbackreplacer_ept.cocci.in new file mode 100644 index 00000000..7074e6d7 --- /dev/null +++ b/kraft/sec/rules/callbackreplacer_ept.cocci.in @@ -0,0 +1,171 @@ +@cbvoid{{ rule_nr }}@ +identifier func; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +@@ + ++ void nfunc(); +- __attribute__(({{ lib_from_name }}_callback)) void ++ void __attribute__((section(".text_shared"))) +func(void) { ++ {{ gate }}({{ comp_from_nb }}, {{ comp_cur_nb }}, func_id); ++ } ++ ++ void nfunc(void) { +... +} + +@script:python@ +func_name << cbvoid{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) + +@cbvoid_ret_static{{ rule_nr }}@ +identifier func; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +type Tret; +@@ + ++ static Tret nfunc(); +- __attribute__(({{ lib_from_name }}_callback)) static Tret ++ static Tret __attribute__((section(".text_shared"))) +func(void) { ++ Tret ret; ++ {{ gate_r }}({{ comp_from_nb }}, {{ comp_cur_nb }}, ret, func_id); ++ return ret; ++ } ++ ++ static Tret nfunc() { +... +} + +@script:python@ +func_name << cbvoid_ret_static{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) + +@cbvoid_ret{{ rule_nr }}@ +identifier func; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +type Tret; +@@ + ++ Tret nfunc(); +- __attribute__(({{ lib_from_name }}_callback)) Tret ++ Tret __attribute__((section(".text_shared"))) +func(void) { ++ Tret ret; ++ {{ gate_r }}({{ comp_from_nb }}, {{ comp_cur_nb }}, ret, func_id); ++ return ret; ++ } ++ ++ Tret nfunc() { +... +} + +@script:python@ +func_name << cbvoid_ret{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) + +@cb1arg{{ rule_nr }}@ +type T1; +identifier func, I1; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +@@ + ++ void nfunc(T1 I1); +- __attribute__(({{ lib_from_name }}_callback)) void ++ void __attribute__((section(".text_shared"))) +func(T1 I1) { ++ {{ gate }}({{ comp_from_nb }}, {{ comp_cur_nb }}, func_id, I1); ++ } ++ ++ void nfunc(T1 I1) { +... +} + +@script:python@ +func_name << cb1arg{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) + +@cb1arg_ret{{ rule_nr }}@ +type T1; +identifier func, I1; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +type Tret; +@@ + ++ Tret nfunc(T1 I1); +- __attribute__(({{ lib_from_name }}_callback)) Tret ++ Tret __attribute__((section(".text_shared"))) +func(T1 I1) { ++ Tret ret; ++ {{ gate_r }}({{ comp_from_nb }}, {{ comp_cur_nb }}, ret, func_id, I1); ++ return ret; ++ } ++ ++ Tret nfunc(T1 I1) { +... +} + +@script:python@ +func_name << cb1arg_ret{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) + +@cbargs{{ rule_nr }}@ +identifier func; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +parameter list[n={2...100}] PL; +expression list EL; +@@ + ++ void nfunc(PL); +- __attribute__(({{ lib_from_name }}_callback)) void ++ void __attribute__((section(".text_shared"))) +func(PL@EL) { ++ {{ gate }}({{ comp_from_nb }}, {{ comp_cur_nb }}, func_id, EL); ++ } ++ ++ void nfunc(PL) { +... +} + +@script:python@ +func_name << cbargs{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) + +@cbargs_ret{{ rule_nr }}@ +identifier func; +fresh identifier nfunc = "_" ## func; +fresh identifier func_id = "{{ ept_id_prefix }}" ## nfunc; +parameter list[n={2...100}] PL; +expression list EL; +type Tret; +@@ + ++ Tret nfunc(PL); +- __attribute__(({{ lib_from_name }}_callback)) Tret ++ Tret __attribute__((section(".text_shared"))) +func(PL@EL) { ++ Tret ret; ++ {{ gate_r }}({{ comp_from_nb }}, {{ comp_cur_nb }}, ret, func_id, EL); ++ return ret; ++ } ++ ++ Tret nfunc(PL) { +... +} + +@script:python@ +func_name << cbargs_ret{{ rule_nr }}.nfunc; +@@ +add_func(func_name, {{ comp_cur_nb }}) diff --git a/kraft/sec/rules/gatereplacer_ept.cocci.in b/kraft/sec/rules/gatereplacer_ept.cocci.in new file mode 100644 index 00000000..72eede05 --- /dev/null +++ b/kraft/sec/rules/gatereplacer_ept.cocci.in @@ -0,0 +1,53 @@ +@gatereplacer_return0_{{ rule_nr }}@ +identifier func; +expression ret, lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate_r({{ lib_dest_name }}, ret, func); ++ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, func_id); + +@script:python@ +func_name << gatereplacer_return0_{{ rule_nr }}.func; +@@ +add_func(func_name, {{ comp_dest_nb }}) + +@gatereplacer_return{{ rule_nr }}@ +identifier func; +expression list EL; +expression ret, lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate_r({{ lib_dest_name }}, ret, func, EL); ++ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, func_id, EL); + +@script:python@ +func_name << gatereplacer_return{{ rule_nr }}.func; +@@ +add_func(func_name, {{ comp_dest_nb }}) + +@gatereplacer_noreturn0_{{ rule_nr }}@ +identifier func; +expression lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate({{ lib_dest_name }}, func); ++ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, func_id); + +@script:python@ +func_name << gatereplacer_noreturn0_{{ rule_nr }}.func; +@@ +add_func(func_name, {{ comp_dest_nb }}) + +@gatereplacer_noreturn{{ rule_nr }}@ +identifier func; +expression list EL; +expression lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate({{ lib_dest_name }}, func, EL); ++ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, func_id, EL); + +@script:python@ +func_name << gatereplacer_noreturn{{ rule_nr }}.func; +@@ +add_func(func_name, {{ comp_dest_nb }}) diff --git a/kraft/sec/rules/rpc_id_gen.cocci.in b/kraft/sec/rules/rpc_id_gen.cocci.in new file mode 100644 index 00000000..9c0fb05b --- /dev/null +++ b/kraft/sec/rules/rpc_id_gen.cocci.in @@ -0,0 +1,76 @@ +@initialize:python@ @@ +import os +import os.path +import operator + +#TODO remove +tmpout = open("/root/gaterep_info", "a+") +tmpout.write("#### new rewrite starting from here ####\n") + +comp_cnt = {{ comp_cnt }} +filename = {{ filename }} +func_wl_base_name = {{ func_wl_base }} + +entry_cnt = 0 + +def get_func_names(filename_funclist): + global entry_cnt + entries = {} + with open(filename_funclist, "r") as file: + lines = file.readlines() + for l in lines: + entries[l.strip()] = entry_cnt + entry_cnt += 1 + return entries + +def get_whitelist_funcs(filename_wl): + with open(filename_wl, "r") as file: + lines = file.readlines() + wl_funcs = {} + for l in lines: + fname = l.strip() + wl_funcs[fname] = func_entries[fname] + return wl_funcs + + +func_entries = get_func_names(filename) + +# for each compartment maintain the functions called in it +func_whitelists = {} +for c in range(comp_cnt): + wl_filename = func_wl_base_name + str(c) + if os.path.isfile(wl_filename): + func_whitelists[c] = get_whitelist_funcs(wl_filename) + else: + func_whitelists[c] = {} + +def add_func(func, target_comp): + global entry_cnt + global func_whitelists + fname = str(func) + if fname not in func_entries: + func_entries[fname] = entry_cnt + entry_cnt += 1 + target_wl = func_whitelists[target_comp] + if fname not in target_wl: + target_wl[func] = func_entries[fname] + tmpout.write("gatereplacer_return0 %s\n" % fname) + +@finalize:python@ @@ + +#TODO remove +tmpout.close() + +with open(filename, "w") as file: + sorted_entries = sorted(func_entries.items(), key=operator.itemgetter(1)) + for e in sorted_entries: + file.write(e[0] + "\n") + + +for c in range(comp_cnt): + wl = func_whitelists[c] + sorted_wl = sorted(wl.items(), key=operator.itemgetter(1)) + with open(func_wl_base_name + str(c), "w") as file: + for e in sorted_wl: + file.write(e[0] + "\n") +