| # 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/dist/distribution_manifest.gni") |
| import("//build/zircon/c_utils.gni") |
| import("//build/zircon/hermetic_code_blob.gni") |
| import("//build/zircon/zircon_cpu.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 the linking |
| # target reached by in $deps. |
| # |
| # There is no particular output file this target produces as its API. |
| # Instead, having this target in $deps of a kernel_package() ensures the |
| # file "code-patches.bin" appears in the package file tree. |
| # |
| # Parameters |
| # |
| # * deps |
| # - Required: Must reach exactly one linking target as per metadata |
| # barriers used by link_output_rspfile(), which see. The code |
| # consuming the "code-patches.bin" file from the kernel package is |
| # expected to know what single ELF file the address constants in that |
| # file refer to, so it doesn't make sense to have more than one |
| # linkable target that might have ".code-patches" section data to |
| # extract. |
| # - Type: list(label) |
| # |
| # * data_deps |
| # - Optional: This can be used to reach other targets whose metadata |
| # should travel along with the output of this code_patches() target, |
| # such as code_patching_hermetic_alternative() targets. Any |
| # `distribution_entries` metadata (from resource() et al) in $deps |
| # is blocked from being reached via this target, but $data_deps is not. |
| # - Type: list(label) |
| # |
| # * metadata, testonly, visibility |
| # - 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, |
| [ |
| "deps", |
| "testonly", |
| ]) |
| } |
| |
| toolchain_utils_action(main_target) { |
| forward_variables_from(invoker, |
| [ |
| "data_deps", |
| "visibility", |
| "testonly", |
| ]) |
| outputs = [ output_file ] |
| |
| # Nothing prevents link_output_rspfile() from collecting multiple |
| # binary files here if $deps reaches more than one without barrier. |
| # This is verboten and will just make objcopy die from having too many |
| # positional arguments after @rspfile expansion, so there's no need to |
| # check for it otherwise (which would require a wrapper script). |
| utils = [ "objcopy" ] |
| script = true |
| args = [ |
| "--dump-section=.code-patches=" + rebase_path(outputs[0], root_build_dir), |
| "@" + rebase_path(rspfile, root_build_dir), |
| "/dev/null", |
| ] |
| |
| sources = [ rspfile ] |
| deps = [ ":$rspfile_target" ] |
| |
| metadata = { |
| # Behaves like a resource() target, for aggregation within a kernel |
| # package. |
| distribution_entries_barrier = [] |
| distribution_entries = [] |
| |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| if (defined(invoker.data_deps)) { |
| distribution_entries_barrier += invoker.data_deps |
| } |
| |
| distribution_entries += [ |
| { |
| source = rebase_path(outputs[0], root_build_dir) |
| destination = "code-patches.bin" |
| label = get_label_info(":$main_target", "label_with_toolchain") |
| }, |
| ] |
| } |
| } |
| } |
| |
| # 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 are as for hermetic_code_blob(). |
| # |
| template("code_patching_hermetic_alternative") { |
| target_label = get_label_info(":$target_name", "label_with_toolchain") |
| |
| if (defined(invoker.entrypoint)) { |
| blob_entrypoint = invoker.entrypoint |
| } else { |
| blob_entrypoint = target_name |
| } |
| |
| blob_path = |
| rebase_path("$target_out_dir/$blob_entrypoint.bin", root_build_dir) |
| |
| hermetic_code_blob(target_name) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "entrypoint", |
| "metadata", |
| ]) |
| entrypoint = blob_entrypoint |
| metadata = { |
| code_patching_hermetic_alternative_barrier = [] |
| code_patching_hermetic_alternatives = [] |
| distribution_entries_barrier = [] |
| distribution_entries = [] |
| |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| code_patching_hermetic_alternatives += [ |
| { |
| label = target_label |
| name = blob_entrypoint |
| path = blob_path |
| }, |
| ] |
| |
| # Behaves like a resource() target, for aggregation within a kernel |
| # package. |
| distribution_entries += [ |
| { |
| source = blob_path |
| destination = "code-patches/$entrypoint" |
| label = target_label |
| }, |
| ] |
| } |
| } |
| } |
| |
| # 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) |
| # |
| # 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, |
| "*", |
| [ |
| "aliases", |
| "case_id_header", |
| "deps", |
| "output_name", |
| "testonly", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| |
| sources = [ gen_file ] |
| deps = [ |
| ":$gen_target", |
| "//zircon/kernel/lib/arch:headers", |
| "//zircon/kernel/lib/code-patching", |
| ] |
| } |
| |
| 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, |
| [ |
| "aliases", |
| "case_id_header", |
| "output_name", |
| "testonly", |
| ]) |
| if (!defined(output_name)) { |
| output_name = main_target |
| } |
| |
| if (!defined(case_id_header)) { |
| case_id_header = "arch/code-patches/case-id-asm.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, |
| ] |
| } |
| } |
| } |
| } |