| # Copyright 2019 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import("$zx/public/gn/config/levels.gni") |
| import("$zx/public/gn/config/product_parameters.gni") |
| import("$zx/public/gn/config/standard.gni") |
| import("$zx/public/gn/toolchain/c_utils.gni") |
| import("$zx/public/gn/toolchain/environment.gni") |
| import("$zx/public/gn/toolchain/environment_redirect.gni") |
| import("params.gni") |
| |
| if (current_toolchain == default_toolchain) { |
| # In the default toolchain, just define the kernel toolchains. |
| foreach(cpu, standard_fuchsia_cpus) { |
| environment("kernel") { |
| globals = { |
| is_kernel = true |
| } |
| |
| # Put the kernel's include_dirs ahead of the defaults so |
| # it can override libc++ headers. |
| common_configs = configs + standard_fuchsia_configs |
| configs = [] |
| configs = [ |
| ":headers", |
| |
| # <stdio.h> has #include <lib/io.h>. |
| "$zx/kernel/lib/io:headers.config", |
| |
| # <malloc.h> has #include <lib/heap.h>. |
| "$zx/kernel/lib/heap:headers.config", |
| |
| # <kernel/percpu.h> has #include <lib/lazy_init/lazy_init.h>. |
| "$zx/system/ulib/lazy_init:headers.config", |
| |
| # <kernel/spinlock.h> has #include <lockdep/lock_policy.h>. |
| "$zx/system/ulib/lockdep:headers.config", |
| |
| # <kernel/{scheduler.h,scheduler_state.h> has #include <ffl/fixed.h>. |
| "$zx/system/ulib/ffl:headers.config", |
| |
| # <kernel/thread.h> has #include <vm/kstack.h>. |
| "vm:headers.config", |
| |
| # <vm/vm_object.h> has #include <lib/user_copy/user_ptr.h>. |
| "$zx/kernel/lib/user_copy:headers.config", |
| |
| # <lib/ktrace.h> has #include <lib/zircon-internal/ktrace.h>. |
| "$zx/system/ulib/zircon-internal:headers.config", |
| |
| # <lib/ktrace.h> has #include <lib/ktrace/string_ref.h>. |
| "$zx/kernel/lib/ktrace:config", |
| |
| # <kernel/thread.h> has #include <fbl/macros.h> |
| "$zx/system/ulib/fbl:headers.config", |
| |
| # <dev/iommu.h> has #include <fbl/name.h> |
| "lib/fbl:headers.config", |
| ] |
| |
| # The rest of kernel-specific config goes after the defaults |
| # so it can override explicit code-generation switches. |
| configs += common_configs + [ ":kernel_config" ] |
| |
| tags = [ "standalone" ] |
| |
| if (cpu == "x64") { |
| # TODO(TC-598): x86 kernel can't be built without --gc-sections |
| # because of crufty acpica code, and some compiler instrumentation |
| # is broken wrt --gc-sections semantics. |
| exclude_variant_tags = [ "breaks-gc-sections" ] |
| } |
| } |
| } |
| } else if (is_kernel) { |
| # These are needed both in kernel sources (pervasively) and in the linker |
| # scripts. |
| kernel_defines = [ |
| # TODO: should not be needed in C, but is in one place now. |
| "KERNEL_BASE=$kernel_base", |
| |
| "SMP_MAX_CPUS=$smp_max_cpus", |
| ] |
| |
| # This is the top config for all kernel code. |
| config("kernel_config") { |
| configs = [ |
| ":lock_dep", |
| ":scheduler", |
| ":standalone", |
| ":warnings", |
| "arch/$zircon_cpu:kernel", |
| |
| # include/lib/counters.h and kernel.ld depend on -fdata-sections. |
| "$zx/public/gn/config:data_sections", |
| ] |
| |
| defines = kernel_defines + kernel_extra_defines |
| defines += [ |
| "_KERNEL", |
| "LK", |
| "ENABLE_PANIC_SHELL", |
| "WITH_DEBUG_LINEBUFFER", |
| "ZIRCON_TOOLCHAIN", |
| ] |
| |
| if (!enable_user_pci) { |
| defines += [ "WITH_KERNEL_PCIE" ] |
| } |
| |
| cflags = [ "-fpie" ] |
| } |
| |
| config("headers") { |
| include_dirs = [ |
| "include", |
| "lib/libc/include", |
| ] |
| |
| # lib/libc/include is before toolchain headers because it needs to be |
| # able to override some libc++ headers that won't work in the kernel |
| # context. However, lib/libc/include/limits.h punts to the toolchain |
| # via #include_next <limits.h> and the toolchain's limits.h does the |
| # same to get the "system" libc <limits.h>, so we need another include |
| # directory after the toolchain headers that has a limits.h for that to |
| # find, even though in the kernel there is nothing to add to the |
| # toolchain's <limits.h> content. |
| cflags = [ |
| "-idirafter", |
| rebase_path("lib/libc/limits-dummy", root_build_dir), |
| ] |
| } |
| |
| # For any standalone static binary. |
| config("standalone") { |
| ldflags = [ |
| "-nostdlib", |
| "-static", |
| ] |
| |
| cflags = [ |
| "-ffreestanding", |
| "-include", |
| rebase_path("include/hidden.h", root_build_dir), |
| |
| # We want `.debug_frame` for the kernel (ZX-62). And we still want |
| # asynchronous unwind tables. Alas there's (currently) no way to |
| # achieve this with our GCC. At the moment we compile with |
| # `-fno-omit-frame-pointer`, which is good because we link with |
| # `--gc-sections`, which means `.eh_frame` gets discarded so GCC-built |
| # kernels don't have any unwind info (except for assembly)! Assembler |
| # code has its own way of requesting `.debug_frame` vs `.eh_frame` with |
| # the `.cfi_sections` directive. |
| "-fno-unwind-tables", |
| ] |
| |
| cflags_cc = [ |
| # Underlying kernel heap only has default alignment of 8 bytes, so pass |
| # this to the compiler as the default new alignment. |
| "-faligned-new=8", |
| ] |
| |
| if (current_cpu == "x64") { |
| # This only matters in an environment where interrupt handlers might |
| # push trap frames in the same privilege level, like the kernel. |
| # e.g. Multiboot probably doesn't actually need it, but it doesn't hurt. |
| cflags += [ "-mno-red-zone" ] |
| } else if (current_cpu == "arm64") { |
| # This matters if vector registers are not available, e.g. in the kernel |
| # since the they hold unsaved user state, or in the physmem environment |
| # because they might not be enabled in hardware yet. |
| cflags += [ "-mgeneral-regs-only" ] |
| } |
| |
| if (!is_gcc && current_os == "fuchsia") { |
| # In the Fuchsia-target toolchains there's no way to prevent the |
| # compiler driver from passing -pie, so negate it. BFD ld doesn't |
| # have --no-pie, but arm64-elf-gcc doesn't pass -pie either. |
| ldflags += [ "-Wl,--no-pie" ] |
| } |
| |
| configs = [ "$zx/public/gn/config:no_exceptions" ] |
| } |
| |
| config("warnings") { |
| cflags = [ |
| "-Wformat=2", |
| "-Wvla", |
| ] |
| |
| # GCC supports `-Wformat-signedness` but Clang currently does not. |
| if (is_gcc) { |
| cflags += [ "-Wformat-signedness" ] |
| } |
| |
| cflags_c = [ "-Wmissing-prototypes" ] |
| } |
| |
| config("lock_dep") { |
| visibility = [ ":*" ] |
| defines = [] |
| if (enable_lock_dep) { |
| defines += [ |
| "WITH_LOCK_DEP=1", |
| "LOCK_DEP_ENABLE_VALIDATION=1", |
| ] |
| } |
| if (enable_lock_dep_tests) { |
| defines += [ "WITH_LOCK_DEP_TESTS=1" ] |
| } |
| } |
| |
| config("scheduler") { |
| visibility = [ ":*" ] |
| defines = [] |
| if (enable_fair_scheduler) { |
| defines += [ "WITH_FAIR_SCHEDULER=1" ] |
| } |
| if (detailed_scheduler_tracing) { |
| defines += [ "WITH_DETAILED_SCHEDULER_TRACING=1" ] |
| } |
| } |
| |
| # This is the kernel proper, an ELF executable with full symbols. |
| # It's the file to use with a debugger, for example. |
| executable("zircon") { |
| visibility = [ ":*" ] |
| |
| # $zx/scripts/zircon.elf-gdb.py expects kernel symbols in "zircon.elf". |
| output_extension = "elf" |
| |
| ldflags = [ |
| "-Wl,-T," + rebase_path("kernel.ld", root_build_dir), |
| "-Wl,--emit-relocs", |
| ] |
| if (toolchain.tags + [ "lto" ] - [ "lto" ] != toolchain.tags) { |
| # (Thin)LTO linker driver overrides the PIC/PIE metadata embedded in the |
| # IR and will choose the relocation model based on the output which for |
| # the kernel would be static rather than PIE. We need to explicitly |
| # override the relocation via linker flag. |
| ldflags += [ "-Wl,-mllvm,-relocation-model=pic" ] |
| } |
| inputs = [ |
| "kernel.ld", |
| ] |
| configs += [ ":kernel_defsym" ] |
| |
| deps = [ |
| ":test", # TODO: make optional, add testonly taint |
| "top", |
| ] |
| if (current_cpu == "arm64") { |
| deps += [ "platform/generic-arm" ] |
| } else if (current_cpu == "x64") { |
| deps += [ "target/pc" ] |
| } |
| } |
| |
| zircon_elf_rspfile = "$target_gen_dir/zircon.elf.rsp" |
| link_output_rspfile("zircon.elf.rsp") { |
| visibility = [ ":*" ] |
| deps = [ |
| ":zircon", |
| ] |
| outputs = [ |
| zircon_elf_rspfile, |
| ] |
| } |
| |
| # These are needed only in image.S and in the linker scripts. |
| image_defines = [ "BOOT_HEADER_SIZE=0x50" ] |
| |
| # This supplies those variables for use in linker scripts. |
| config("kernel_defsym") { |
| visibility = [ ":*" ] |
| ldflags = [] |
| foreach(assignment, kernel_defines + image_defines) { |
| ldflags += [ "-Wl,-defsym,$assignment" ] |
| } |
| } |
| |
| group("test") { |
| #TODO: testonly = true |
| visibility = [ ":*" ] |
| deps = [ |
| "debugcommands", |
| "tests", |
| ] |
| } |
| |
| # Extract the raw binary image (no ELF headers) of the kernel proper. |
| image_binary("raw") { |
| visibility = [ ":*" ] |
| deps = [ |
| ":zircon", |
| ] |
| output_name = "zircon" |
| output_path = |
| rebase_path("$target_out_dir/$output_name.bin", root_build_dir) |
| metadata = { |
| image_path_defines = [ "#define KERNEL_IMAGE \"${output_path}\"" ] |
| image_path_barrier = [] |
| } |
| |
| # Use the same variant for the extraction that will have built the kernel. |
| variant_target = { |
| match = "executable" |
| label = ":zircon" |
| output_name = "zircon" |
| } |
| } |
| |
| # Use metadata to embed that file name in case it's a variant redirect. |
| kernel_image_h = "$target_gen_dir/kernel_image.h" |
| generated_file("kernel_image.h") { |
| visibility = [ ":*" ] |
| deps = [ |
| ":raw", |
| ] |
| outputs = [ |
| kernel_image_h, |
| ] |
| data_keys = [ "image_path_defines" ] |
| walk_keys = [ "image_path_barrier" ] |
| output_conversion = "list lines" |
| } |
| |
| # Use the --emit-relocs records to extract the fixups needed to relocate |
| # the kernel at boot. This generates the "kernel-fixups.inc" file that's |
| # #include'd by "arch/$zircon_cpu/image.S". |
| toolchain_utils_action("fixups") { |
| visibility = [ ":*" ] |
| deps = [ |
| ":zircon", |
| ":zircon.elf.rsp", |
| ] |
| sources = [ |
| zircon_elf_rspfile, |
| ] |
| outputs = [ |
| "$target_gen_dir/kernel-fixups.inc", |
| ] |
| depfile = "${outputs[0]}.d" |
| |
| # TODO(mcgrathr): Move the script to this dir as it's private to this use. |
| script = "gen-kaslr-fixups.sh" |
| utils = [ |
| "readelf", |
| "objdump", |
| ] |
| args = [ |
| "@" + rebase_path(zircon_elf_rspfile, root_build_dir), |
| rebase_path(outputs[0], root_build_dir), |
| rebase_path(depfile, root_build_dir), |
| ] |
| |
| # Use the same variant for the extraction that will have built the kernel. |
| variant_target = { |
| match = "executable" |
| label = ":zircon" |
| output_name = "zircon" |
| } |
| } |
| |
| # Link the final kernel image layout including the extracted raw binary |
| # and the generated fixups. |
| executable("image") { |
| visibility = [ ":*" ] |
| configs += [ |
| ":image_config", |
| ":kernel_defsym", |
| ] |
| deps = [ |
| ":fixups", |
| ":gdb_extension", |
| ":kernel_image.h", |
| ":raw", |
| ":zircon", |
| ":zircon.elf.rsp", |
| ] |
| sources = [ |
| "arch/$zircon_cpu/image.S", |
| ] |
| |
| # These need to be here rather than in the config() below because they |
| # refer directly to inputs related to deps. |
| ldflags = [ |
| "-Wl,-T," + rebase_path("image.ld", root_build_dir), |
| "-Wl,--just-symbols,@" + rebase_path(zircon_elf_rspfile, root_build_dir), |
| ] |
| inputs = [ |
| "image.ld", |
| zircon_elf_rspfile, |
| ] |
| |
| # If the span of where fixups might be is short enough, the fixup code |
| # itself can be shorter. Assume the kernel will fit under the |
| # threshold (about 1MB) in well-optimized builds. |
| if (opt_level > 1 && toolchain.tags + [ "instrumented" ] - |
| [ "instrumented" ] == toolchain.tags) { |
| defines = [ "TINY" ] |
| } |
| } |
| |
| # Link gdb extension script file needed for kernel debugging. |
| action("gdb_extension") { |
| visibility = [ ":*" ] |
| sources = [ |
| "scripts/zircon.elf-gdb.py", |
| ] |
| outputs = [ |
| "$target_out_dir/zircon.elf-gdb.py", |
| ] |
| script = "/bin/ln" |
| args = [ |
| "-sf", |
| rebase_path("scripts/zircon.elf-gdb.py", target_out_dir), |
| rebase_path(target_out_dir, root_build_dir), |
| ] |
| } |
| |
| # This needs to be in a config() rather than directly in the executable() |
| # so that these switches come after the ones provided by configs. In |
| # some cases, these switches may be overriding settings done by another |
| # config(), e.g. --build-id in the GCC build. |
| config("image_config") { |
| visibility = [ ":*" ] |
| include_dirs = [ target_gen_dir ] |
| defines = image_defines |
| ldflags = [ "-Wl,--build-id=none" ] |
| } |
| |
| # Finally, extract the raw image (no ELF headers), which includes its own |
| # embedded headers to make it a ZBI. |
| image_binary("kernel") { |
| output_dir = root_out_dir |
| output_extension = "zbi" |
| output_path = rebase_path("$output_dir/$target_name.$output_extension", |
| root_build_dir) |
| deps = [ |
| ":image", |
| ] |
| metadata = { |
| # For the //:images build_api_module(). |
| images = [ |
| { |
| label = get_label_info(":$target_name", "label_with_toolchain") |
| name = "kernel" |
| tags = [ "incomplete" ] |
| type = "zbi" |
| path = output_path |
| cpu = current_cpu |
| }, |
| ] |
| |
| # This metadata makes the kernel act as a zbi_input() target so it can |
| # be a dependency of a zbi() target to get into the image. |
| zbi_input_args = [ |
| "--type=container", |
| output_path, |
| ] |
| } |
| |
| # Use the same variant for the extraction that will have built the kernel. |
| variant_target = { |
| match = "executable" |
| label = ":zircon" |
| output_name = "zircon" |
| } |
| } |
| } else { |
| # Redirect to the kernel toolchain. |
| environment_redirect("kernel") { |
| environment_label = ":kernel" |
| direct = true |
| deps = [ |
| ":kernel", |
| ] |
| } |
| } |