| # Copyright 2020 The Fuchsia Authors |
| # |
| # Use of this source code is governed by a MIT-style |
| # license that can be found in the LICENSE file or at |
| # https://opensource.org/licenses/MIT |
| |
| import("//build/unification/global_variables.gni") |
| import("//build/zircon/c_utils.gni") |
| |
| ## This file provides the templates needed to extract code-patching directives |
| ## stored in a special ELF section, and needed to specify code patching |
| ## alternatives and the functions to be later patched by those alternatives. |
| ## |
| ## Example usage: |
| ## ``` |
| ## # Two possible patches for memset. |
| ## code_patching_hermetic_alternative("memset_slow") { |
| ## sources = ["memset_slow.c"] |
| ## } |
| ## |
| ## code_patching_hermetic_alternative("memset_fast") { |
| ## sources = ["memset_fast.S"] |
| ## } |
| ## |
| ## # Generates an object file that defines a stub `memset` - requiring later |
| ## # patching - filled with traps and just large enough to fit any of the |
| ## # associated alternatives. |
| ## code_patching_hermetic_stub("memset") { |
| ## |
| ## # Aggregates metadata on alternatives. |
| ## deps = [":memset_slow", ":memset_fast"] |
| ## } |
| ## ``` |
| ## |
| |
| # Extracts the content of the ".code-patches" sections from each of the |
| # executables in $deps. |
| # |
| # Parameters |
| # |
| # * deps |
| # - Required: Dependencies, from which all transitively dependent |
| # executables will have their section content extracted. |
| # - Type: list(label) |
| # |
| # * testonly, visibility, metadata |
| # - See action(). |
| # |
| template("code_patches") { |
| assert(defined(invoker.deps), "`deps` is a required parameter") |
| |
| main_target = target_name |
| output_file = "$target_out_dir/$target_name.bin" |
| rspfile_target = "_code_patches.rsp.${target_name}" |
| rspfile = "${target_gen_dir}/${target_name}.code_patches.rsp" |
| |
| link_output_rspfile(rspfile_target) { |
| visibility = [ ":$main_target" ] |
| outputs = [ rspfile ] |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "deps", |
| ]) |
| } |
| |
| toolchain_utils_action(main_target) { |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| "metadata", |
| ]) |
| outputs = [ output_file ] |
| |
| utils = [ "objcopy" ] |
| script = true |
| args = [ |
| "--dump-section=.code-patches=" + rebase_path(outputs[0], root_build_dir), |
| "@" + rebase_path(rspfile, root_build_dir), |
| "/dev/null", |
| ] |
| |
| deps = [ ":$rspfile_target" ] |
| } |
| } |
| |
| # Defines a "hermetic leaf" code patching alternative, which is defined by the |
| # following properties (enforced at link-time): |
| # |
| # * the named function (`entrypoint`) is first in the link order and all of the |
| # sources/deps together form a closed set that is collectively hermetic; |
| # * no non-code (rodata or writable data) sections; |
| # * no dynamic relocations. |
| # |
| # Used in conjunction with code_patching_hermetic_stub() (see below), several |
| # alternatives may be defined so that code patching logic can make a runtime |
| # decision of which one of these should be used. |
| # |
| # It produces a raw binary content of a position-indepdendent executable, |
| # output to `target_out_dir` with an extension of "bin". |
| # |
| # Parameters |
| # |
| # * entrypoint |
| # - Optional: The name of the symbol (or C function) providing this |
| # alternative and defined in `sources`. |
| # - Type: string |
| # - Default: $target_name. |
| # |
| # Other parameters are as for executable(), except for the `output_*` |
| # parameters. |
| # |
| template("code_patching_hermetic_alternative") { |
| image_target = target_name |
| executable_target = |
| "_code_patching_hermetic_alternative.$target_name.executable" |
| |
| if (defined(invoker.entrypoint)) { |
| entrypoint = invoker.entrypoint |
| } else { |
| entrypoint = target_name |
| } |
| |
| executable(executable_target) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| visibility = [ ":*" ] |
| |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "output_name", |
| "output_dir", |
| "output_extension", |
| "entrypoint", |
| "visibility", |
| "testonly", |
| ]) |
| |
| output_name = entrypoint |
| output_dir = target_gen_dir |
| |
| configs += [ "//zircon/kernel/lib/code-patching:hermetic-leaf.config" ] |
| configs += [ "//build/config/zircon:static-pie-link" ] |
| configs -= [ "//build/config/zircon:static-pie-link" ] |
| |
| ldflags = [ "-Wl,-defsym,HERMETIC_LEAF_ENTRY=$entrypoint" ] |
| } |
| |
| image_binary(image_target) { |
| forward_variables_from(invoker, |
| [ |
| "visibility", |
| "testonly", |
| ]) |
| deps = [ ":$executable_target" ] |
| output_name = entrypoint |
| output_dir = target_out_dir |
| output_extension = "bin" |
| |
| # We expect no dynamic relocations. |
| pure = true |
| |
| metadata = { |
| code_patching_hermetic_alternative_barrier = [] |
| code_patching_hermetic_alternatives = [ |
| { |
| label = get_label_info(":$image_target", "label_with_toolchain") |
| name = entrypoint |
| path = rebase_path("$output_dir/$output_name.$output_extension", |
| root_build_dir) |
| }, |
| ] |
| } |
| } |
| } |
| |
| # Defines a function stub that is intended to be linked against and patched |
| # later. Specifically, it is indended that it will be patched by one of the |
| # code_patching_hermetic_alternative() targets in its dependency graph. The |
| # function will have a size equal to the largest of its associated alternatives |
| # and will initially be filled with trap instructions. |
| # |
| # This target should be regarded as an opaque source_set() that defines this |
| # function alone. |
| # |
| # Parameters |
| # |
| # * output_name |
| # - Optional: The name of the function stub to generate, which must be a |
| # valid C identifier. |
| # - Type: string |
| # - Default: $target_name. |
| # |
| # * deps |
| # - Required: Dependencies to reach all |
| # code_patching_hermetic_alternative() targets the stub may resolve to at |
| # runtime. |
| # - Type: list(label) |
| # |
| # * case_id_header |
| # - Optional: a header path that defines patch case IDs. These IDs are |
| # expected to be of the form "CASE_ID_${OUTPUT_NAME}", where |
| # "${OUTPUT_NAME}" is the upper-cased transformation of "${output_name}". |
| # This parameter is only expected to be explicitly set for tests that |
| # wish to supply their own case IDs. |
| # - Type: string |
| # - Default: "arch/code-patches/case-id.h" |
| # |
| # * aliases |
| # - Optional: A list of weak symbols to define as aliases to the associated |
| # function. |
| # - Type: list(string) |
| # |
| # * default_alternative |
| # - Optional: the name of an alternative to default to instead of a trap |
| # fill. |
| # TODO(fxbug.dev/67615): Remove this option once code patching happens |
| # within physboot; at this point, pre-patched, trap-filled code will not |
| # be executed before it is patched. |
| # - Type: string |
| # |
| # Other parameters are as for source_set(). |
| # |
| template("code_patching_hermetic_stub") { |
| main_target = target_name |
| gen_target = "_code_patching_hermetic_stub.gen.$main_target" |
| metadata_target = "_code_patching_hermetic_stub.json.$main_target" |
| |
| gen_file = "$target_gen_dir/$main_target.S" |
| metadata_file = "$target_gen_dir/$main_target.json" |
| |
| source_set(main_target) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "visibility", |
| "testonly", |
| "case_id_header", |
| "output_name", |
| "deps", |
| "aliases", |
| "default_alternative", |
| ]) |
| forward_variables_from(invoker, |
| [ |
| "visibility", |
| "testonly", |
| ]) |
| |
| sources = [ gen_file ] |
| deps = [ |
| ":$gen_target", |
| "//zircon/kernel/lib/arch:headers", |
| "//zircon/kernel/lib/code-patching:headers", |
| ] |
| } |
| |
| generated_file(metadata_target) { |
| visibility = [ ":$gen_target" ] |
| outputs = [ metadata_file ] |
| |
| output_conversion = "json" |
| data_keys = [ "code_patching_hermetic_alternatives" ] |
| walk_keys = [ "code_patching_hermetic_alternative_barrier" ] |
| |
| forward_variables_from(invoker, |
| [ |
| "deps", |
| "testonly", |
| ]) |
| } |
| |
| action(gen_target) { |
| visibility = [ ":$main_target" ] |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "output_name", |
| "case_id_header", |
| "aliases", |
| "default_alternative", |
| ]) |
| if (!defined(output_name)) { |
| output_name = main_target |
| } |
| |
| if (!defined(case_id_header)) { |
| case_id_header = "arch/code-patches/case-id.h" |
| |
| # Public to propagate to :main_target. |
| public_deps = [ "//zircon/kernel/arch/$zircon_cpu/code-patches:headers" ] |
| } |
| |
| outputs = [ gen_file ] |
| sources = [ metadata_file ] |
| deps = [ ":$metadata_target" ] |
| |
| depfile = "$gen_file.d" |
| script = "//zircon/kernel/lib/code-patching/hermetic-stub.py" |
| args = [ |
| "--name", |
| output_name, |
| "--header", |
| case_id_header, |
| "--metadata-file", |
| rebase_path(metadata_file, root_build_dir), |
| "--depfile", |
| rebase_path(depfile, root_build_dir), |
| "--outfile", |
| rebase_path(outputs[0], root_build_dir), |
| ] |
| |
| if (defined(aliases)) { |
| foreach(alias, aliases) { |
| args += [ |
| "--aliases", |
| alias, |
| ] |
| } |
| } |
| |
| if (defined(default_alternative)) { |
| args += [ |
| "--default", |
| default_alternative, |
| ] |
| } |
| } |
| } |
| |
| # Providing a way of transporting the binary content of |
| # code_patching_hermetic_alternative() targets, this template creates a |
| # source_set() that defines the following function, which may be used to |
| # access the instructions comprising a given alternativate: |
| # ``` |
| # std::span<const std::byte> GetPatchAlternative(std::string_view name); |
| # ``` |
| # It returns an empty span if the name is not recognized. |
| # |
| # TODO(68585): This means of transportation is purely a stop-gap; eventually |
| # we will use a STORAGE_KERNEL ZBI item to supply code patching alternatives. |
| # |
| # Parameters |
| # |
| # * deps |
| # - Required: The usual meaning, but with the added importance of |
| # transitively aggregating code_patching_hermetic_alternative() targets, |
| # whose metadata feeds into the generation logic. |
| # - Type: list(label) |
| # |
| # * testonly, visibility |
| # - See source_set(). |
| # |
| template("code_patching_hermetic_embedding") { |
| main_target = target_name |
| gen_target = "_code_patching_hermetic_embedding.gen.$main_target" |
| metadata_target = "_code_patching_hermetic_embedding.json.$main_target" |
| asm_gen_file = "$target_gen_dir/$main_target.S" |
| cpp_gen_file = "$target_gen_dir/$main_target.cc" |
| metadata_file = "$target_gen_dir/$main_target.json" |
| |
| source_set(main_target) { |
| forward_variables_from(invoker, |
| [ |
| "visibility", |
| "testonly", |
| ]) |
| sources = [ |
| asm_gen_file, |
| cpp_gen_file, |
| ] |
| deps = [ |
| ":$gen_target", |
| "//zircon/kernel/lib/arch:headers", |
| ] |
| |
| metadata = { |
| code_patching_hermetic_alternative_barrier = [] |
| } |
| } |
| |
| generated_file(metadata_target) { |
| visibility = [ ":$gen_target" ] |
| outputs = [ metadata_file ] |
| |
| output_conversion = "json" |
| data_keys = [ "code_patching_hermetic_alternatives" ] |
| walk_keys = [ "code_patching_hermetic_alternative_barrier" ] |
| |
| forward_variables_from(invoker, |
| [ |
| "deps", |
| "testonly", |
| ]) |
| } |
| |
| action(gen_target) { |
| visibility = [ ":$main_target" ] |
| forward_variables_from(invoker, |
| [ |
| "function_name", |
| "testonly", |
| ]) |
| |
| sources = [ metadata_file ] |
| outputs = [ |
| asm_gen_file, |
| cpp_gen_file, |
| ] |
| deps = [ ":$metadata_target" ] |
| depfile = "$target_gen_dir/$main_target.d" |
| script = "//zircon/kernel/lib/code-patching/hermetic-embedding.py" |
| args = [ |
| "--metadata-file", |
| rebase_path(sources[0], root_build_dir), |
| "--depfile", |
| rebase_path(depfile, root_build_dir), |
| "--asm-outfile", |
| rebase_path(outputs[0], root_build_dir), |
| "--cpp-outfile", |
| rebase_path(outputs[1], root_build_dir), |
| ] |
| } |
| } |