1616
1717load ("@rules_cc//cc:cc_binary.bzl" , "cc_binary" )
1818load ("@rules_cc//cc:cc_library.bzl" , "cc_library" )
19+ load ("@rules_cc//cc:cc_test.bzl" , "cc_test" )
1920load ("//sandboxed_api/bazel:build_defs.bzl" , "sapi_platform_copts" )
2021load ("//sandboxed_api/bazel:embed_data.bzl" , "sapi_cc_embed_data" )
2122load (
@@ -72,6 +73,42 @@ def sort_deps(deps):
7273 other_deps = [x for x in deps if not x .startswith (":" )]
7374 return sorted (colon_deps ) + sorted (other_deps )
7475
76+ def _clang_generator_flags (cc_ctx , cpp_toolchain ):
77+ flags = []
78+
79+ # TODO(cblichmann): Get language standard from the toolchain
80+ flags .append ("--extra-arg=-std=c++17" )
81+
82+ # Disable warnings in parsed code
83+ flags .append ("--extra-arg=-Wno-everything" )
84+ flags += ["--extra-arg=-D{}" .format (d ) for d in cc_ctx .defines .to_list ()]
85+ flags += ["--extra-arg=-isystem{}" .format (i ) for i in cc_ctx .system_includes .to_list ()]
86+ flags += ["--extra-arg=-iquote{}" .format (i ) for i in cc_ctx .quote_includes .to_list ()]
87+ flags += ["--extra-arg=-I{}" .format (d ) for d in cc_ctx .includes .to_list ()]
88+ return flags
89+
90+ def _lib_direct_headers (lib , cc_ctx ):
91+ headers = []
92+ for h in cc_ctx .direct_headers :
93+ if h .extension != "h" or "/PROTECTED/" in h .path :
94+ continue
95+
96+ # Include only headers coming from the target
97+ # not ones that it depends on by comparing the label packages.
98+ if (h .owner .package == lib .label .package ):
99+ headers .append (h .path )
100+
101+ return headers
102+
103+ def _clang_format_file (src , out , ** kwargs ):
104+ native .genrule (
105+ name = "_format_" + out ,
106+ srcs = [":" + src ],
107+ outs = [out ],
108+ cmd = "cp $< $@" ,
109+ ** kwargs
110+ )
111+
75112def _sapi_interface_impl (ctx ):
76113 cpp_toolchain = find_cpp_toolchain (ctx )
77114 generator = select_generator (ctx )
@@ -113,16 +150,7 @@ def _sapi_interface_impl(ctx):
113150
114151 if use_clang_generator :
115152 input_files += cpp_toolchain .all_files .to_list ()
116-
117- # TODO(cblichmann): Get language standard from the toolchain
118- extra_flags .append ("--extra-arg=-std=c++17" )
119-
120- # Disable warnings in parsed code
121- extra_flags .append ("--extra-arg=-Wno-everything" )
122- extra_flags += ["--extra-arg=-D{}" .format (d ) for d in cc_ctx .defines .to_list ()]
123- extra_flags += ["--extra-arg=-isystem{}" .format (i ) for i in cc_ctx .system_includes .to_list ()]
124- extra_flags += ["--extra-arg=-iquote{}" .format (i ) for i in cc_ctx .quote_includes .to_list ()]
125- extra_flags += ["--extra-arg=-I{}" .format (d ) for d in cc_ctx .includes .to_list ()]
153+ extra_flags += _clang_generator_flags (cc_ctx , cpp_toolchain )
126154 else :
127155 append_all (extra_flags , "-D" , cc_ctx .defines .to_list ())
128156 append_all (extra_flags , "-isystem" , cc_ctx .system_includes .to_list ())
@@ -135,14 +163,7 @@ def _sapi_interface_impl(ctx):
135163 input_files_paths .append (f .path )
136164 else :
137165 # Try to find files automatically
138- for h in cc_ctx .direct_headers :
139- if h .extension != "h" or "/PROTECTED/" in h .path :
140- continue
141-
142- # Include only headers coming from the target
143- # not ones that it depends on by comparing the label packages.
144- if (h .owner .package == ctx .attr .lib .label .package ):
145- input_files_paths .append (h .path )
166+ input_files_paths += _lib_direct_headers (ctx .attr .lib , cc_ctx )
146167
147168 if use_clang_generator :
148169 args += extra_flags + input_files_paths
@@ -231,6 +252,16 @@ def symbol_list_gen(name, lib, out, **kwargs):
231252 ** kwargs
232253 )
233254
255+ def _common_kwargs (tags , visibility , compatible_with ):
256+ common = {
257+ "tags" : tags ,
258+ }
259+ if visibility :
260+ common ["visibility" ] = visibility
261+ if compatible_with != None :
262+ common ["compatible_with" ] = compatible_with
263+ return common
264+
234265def sapi_library (
235266 name ,
236267 lib ,
@@ -298,15 +329,7 @@ def sapi_library(
298329 exec_properties: Dict of executable properties to be passed to the generated binary targets.
299330 """
300331
301- common = {
302- "tags" : tags ,
303- }
304- if visibility :
305- common ["visibility" ] = visibility
306-
307- if compatible_with != None :
308- common ["compatible_with" ] = compatible_with
309-
332+ common = _common_kwargs (tags , visibility , compatible_with )
310333 generated_header = name + ".sapi.h"
311334
312335 # Reference (pull into the archive) required functions only. If the functions'
@@ -350,7 +373,7 @@ def sapi_library(
350373 ** common
351374 )
352375
353- native . cc_binary (
376+ cc_binary (
354377 name = name + ".bin" ,
355378 linkopts = [
356379 "-ldl" , # For dlopen(), dlsym()
@@ -393,7 +416,7 @@ def sapi_library(
393416 lib = lib ,
394417 functions = functions ,
395418 input_files = input_files ,
396- out = generated_header ,
419+ out = generated_header + ".unformatted" ,
397420 embed_name = embed_name ,
398421 embed_dir = embed_dir ,
399422 namespace = namespace ,
@@ -402,3 +425,210 @@ def sapi_library(
402425 limit_scan_depth = limit_scan_depth ,
403426 ** common
404427 )
428+
429+ _clang_format_file (generated_header + ".unformatted" , generated_header , ** common )
430+
431+ def cc_sandboxed_library (
432+ name ,
433+ lib ,
434+ tags = [],
435+ visibility = None ,
436+ compatible_with = None ):
437+ """Creates a sandboxed drop-in replacement cc_library.
438+
439+ NOTE: this functionality is experimental and may change in the future.
440+
441+ The resulting library can be used instead of the original cc_library
442+ as dependency for other targets. The behavior is supposed to be identical
443+ to the original library, except that the library is sandboxed with a single
444+ global sandbox instance.
445+
446+ Only limited set of types is supported in signatures of the library public
447+ functions. Any unsupported types will cause build failure.
448+
449+ Any crashes or violations in the sandbox process crash the host process.
450+
451+ Args:
452+ name: Name of the target
453+ lib: Label of the cc_library target to sandbox
454+ tags: Same as cc_library.tags
455+ visibility: Same as cc_library.visibility
456+ compatible_with: Same as cc_library.compatible_with
457+ """
458+
459+ # Implementation outline:
460+ # 1. Run clang generator tool in sandboxed library mode.
461+ # In this mode it takes all functions declared in the library public headers,
462+ # and generates 3 files:
463+ # - Sandboxee header file with wrappers for the public library functions.
464+ # These wrappers have similar signature to the original functions,
465+ # but also have some differences. For example, absl::string_view would be passed
466+ # as a (const char*, size_t) pair of arguments so that SAPI supports it.
467+ # - Sandboxee source file with implementation of the wrappers that call original functions.
468+ # These wrappers will construct the string_view back from the pair of arguments.
469+ # - Host source file with implementation of the public library functions.
470+ # These implementations use SAPI sandbox to call the generated sandboxee wrappers
471+ # in the sandbox, and will deconstruct string_view into the pair of arguments.
472+ # 2. Build cc_library with the sandboxee header and source files
473+ # and dependency on the original library.
474+ # 3. Create a sapi_library for the sandboxee library created at step 2.
475+ # This library also links in the generated host source file,
476+ # so that it implements the original library interface verbatim.
477+ # 4. Create a transparent replacement rule that pretends to be a cc_library
478+ # by assembling CcInfo from compilation context of the original library
479+ # and linking context of the sapi_library created at step 3.
480+ # Using the compilation context of the original library ensures that during
481+ # compilation the replacement library "looks" exactly as the original library
482+ # (this includes any use of defines/includes and any other cc_library attributes).
483+
484+ # Unique prefix for things generated by sapi_library (e.g. FooSandbox class name).
485+ # TODO(dvyukov): add hash/flattening of the full library /path:name, just the name is not
486+ # necessarily globally unique.
487+ wrapper_name = "Sapi" + name
488+ common = _common_kwargs (tags , visibility , compatible_with )
489+
490+ _sandboxed_library_gen (
491+ name = "_sandboxed_library_gen_" + name ,
492+ lib = lib ,
493+ lib_name = wrapper_name ,
494+ sandboxee_hdr_out = name + ".sapi.sandboxee.h.unformatted" ,
495+ sandboxee_src_out = name + ".sapi.sandboxee.cc.unformatted" ,
496+ host_src_out = name + ".sapi.host.cc.unformatted" ,
497+ sapi_hdr = native .package_name () + "/_sapi_" + name + ".sapi.h" ,
498+ ** common
499+ )
500+
501+ _clang_format_file (name + ".sapi.sandboxee.h.unformatted" , name + ".sapi.sandboxee.h" , ** common )
502+ _clang_format_file (name + ".sapi.sandboxee.cc.unformatted" , name + ".sapi.sandboxee.cc" , ** common )
503+ _clang_format_file (name + ".sapi.host.cc.unformatted" , name + ".sapi.host.cc" , ** common )
504+
505+ cc_library (
506+ name = "_sapi_sandboxee_" + name ,
507+ hdrs = [":" + name + ".sapi.sandboxee.h" ],
508+ srcs = [":" + name + ".sapi.sandboxee.cc" ],
509+ # Work-around broken global sapi_library mode (when functions are empty).
510+ # When functions are not empty, sapi_library adds -Wl,-u linker flags
511+ # that force linking of the sandboxee library. In global mode, it won't be linked.
512+ alwayslink = 1 ,
513+ deps = [
514+ lib ,
515+ "//sandboxed_api:lenval_core" ,
516+ ],
517+ ** common
518+ )
519+
520+ sapi_library (
521+ name = "_sapi_" + name ,
522+ lib = ":_sapi_sandboxee_" + name ,
523+ lib_name = wrapper_name ,
524+ srcs = [name + ".sapi.host.cc" ],
525+ generator_version = 2 ,
526+ deps = [
527+ "//sandboxed_api:lenval_core" ,
528+ "@abseil-cpp//absl/log:check" ,
529+ ],
530+ ** common
531+ )
532+
533+ _sandboxed_library (
534+ name = name ,
535+ lib = lib ,
536+ sapi = ":_sapi_" + name ,
537+ ** common
538+ )
539+
540+ _sandboxed_library = rule (
541+ provides = [CcInfo ],
542+ attrs = {
543+ "lib" : attr .label (providers = [CcInfo ]),
544+ "sapi" : attr .label (providers = [CcInfo ]),
545+ },
546+ implementation = lambda ctx : [CcInfo (
547+ compilation_context = ctx .attr .lib [CcInfo ].compilation_context ,
548+ linking_context = ctx .attr .sapi [CcInfo ].linking_context ,
549+ )],
550+ )
551+
552+ def cc_sandboxed_library_test (
553+ name ,
554+ lib ,
555+ sandboxed_lib ,
556+ deps = [],
557+ ** kwargs ):
558+ """Creates a pair of sandboxed/unsandboxed cc_test's for cc_sandboxed_library.
559+
560+ NOTE: this functionality is experimental and may change in the future.
561+
562+ This rule is supposed to be a replacement for any cc_test's for a library
563+ that is used with cc_sandboxed_library. It creates a pair of cc_test's
564+ that test both sandboxed and unsandboxed versions of the library.
565+
566+ Args:
567+ name: Name of the target
568+ lib: Label of the normal unsandboxed cc_library target
569+ sandboxed_lib: Label of the cc_sandboxed_library target for the lib
570+ deps: Same as cc_library.deps, must not include lib/sandboxed_lib
571+ **kwargs: Passed to resulting cc_test's
572+ """
573+
574+ cc_test (
575+ name = name + "_unsandboxed" ,
576+ deps = deps + [lib ],
577+ ** kwargs
578+ )
579+
580+ cc_test (
581+ name = name + "_sandboxed" ,
582+ deps = deps + [sandboxed_lib ],
583+ ** kwargs
584+ )
585+
586+ native .test_suite (
587+ name = name ,
588+ tests = [
589+ ":" + name + "_unsandboxed" ,
590+ ":" + name + "_sandboxed" ,
591+ ],
592+ )
593+
594+ def _sandboxed_library_gen_impl (ctx ):
595+ cpp_toolchain = find_cpp_toolchain (ctx )
596+ cc_ctx = ctx .attr .lib [CcInfo ].compilation_context
597+
598+ args = []
599+ args .append ("--sandboxed_library_gen" )
600+ args .append ("--sapi_name={}" .format (ctx .attr .lib_name ))
601+ args .append ("--sandboxee_hdr_out={}" .format (ctx .outputs .sandboxee_hdr_out .path ))
602+ args .append ("--sandboxee_src_out={}" .format (ctx .outputs .sandboxee_src_out .path ))
603+ args .append ("--host_src_out={}" .format (ctx .outputs .host_src_out .path ))
604+ args .append ("--sapi_out={}" .format (ctx .attr .sapi_hdr ))
605+ args .append ("--sapi_limit_scan_depth" )
606+ args += _clang_generator_flags (cc_ctx , cpp_toolchain )
607+ args += _lib_direct_headers (ctx .attr .lib , cc_ctx )
608+
609+ progress_msg = "Generating sandboxed library {}." .format (ctx .attr .lib_name )
610+ ctx .actions .run (
611+ inputs = cc_ctx .headers .to_list () + cpp_toolchain .all_files .to_list (),
612+ outputs = [ctx .outputs .sandboxee_hdr_out , ctx .outputs .sandboxee_src_out , ctx .outputs .host_src_out ],
613+ arguments = args ,
614+ mnemonic = "SandboxedLibraryGen" ,
615+ progress_message = progress_msg ,
616+ executable = ctx .executable ._generator ,
617+ )
618+
619+ # Build rule that generates SAPI interface.
620+ _sandboxed_library_gen = rule (
621+ implementation = _sandboxed_library_gen_impl ,
622+ attrs = {
623+ "lib" : attr .label (providers = [CcInfo ]),
624+ "lib_name" : attr .string (),
625+ "sandboxee_hdr_out" : attr .output (),
626+ "sandboxee_src_out" : attr .output (),
627+ "host_src_out" : attr .output (),
628+ "sapi_hdr" : attr .string (),
629+ "_generator" : make_exec_label (
630+ "//sandboxed_api/tools/clang_generator:generator_tool" ,
631+ ),
632+ },
633+ toolchains = use_cpp_toolchain (),
634+ )
0 commit comments