blob: b96f5729831dceb5efb19d73c526304f44cb46c3 [file] [log] [blame]
# Copyright 2019 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("$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
}
if (output_breakpad_syms) {
breakpad_syms = "-r -n \"zircon.elf\" -o Fuchsia"
}
# 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",
# TODO(44971): Many include/ headers use <ktl/*.h> headers.
"$zx/kernel/lib/ktl:headers.config",
"$zx/kernel/lib/ktl:ktl.config",
# <stdio.h> has #include <lib/io.h>.
"$zx/kernel/lib/io:headers.config",
# <stdlib.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:headers.config",
"$zx/kernel/lib/ktrace:suppress-warning.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",
":retpoline",
":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",
"LK_DEBUGLEVEL=$kernel_debug_level",
]
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" ]
}
if (!is_gcc) {
# Disable the implicit addition of toolchain-provided libraries to
# the link by the compiler driver. No toolchain-provided library is
# compatible with the kernel's internal ABI.
#
# TODO(27356): Clang doesn't have a single straightforward switch to
# disable all such libraries, though it certainly should. It
# provides separate switches to disable the profiling/coverage
# runtime and to disable all the flavors of runtime implied by
# -fsanitize=... switches (including any such defaults). It will
# still provide other incompatible libraries to the link, but they
# won't have any effect since they don't define any symbols the link
# needs. However, this is a fragile situation that could easily
# break.
ldflags += [
"-noprofilelib",
"-fno-sanitize-link-runtime",
]
}
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("retpoline") {
visibility = [ ":*" ]
cflags = []
defines = []
if (current_cpu == "x64") {
if (is_gcc) {
cflags += [
"-mindirect-branch=thunk-inline",
"-mindirect-branch-register",
]
} else {
cflags += [
"-mretpoline",
"-mretpoline-external-thunk",
]
}
defines += [ "KERNEL_RETPOLINE=1" ]
}
}
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 (select_scheduler == "legacy") {
defines += [ "WITH_LEGACY_SCHEDULER=1" ]
} else if (select_scheduler == "fair") {
defines += [ "WITH_FAIR_SCHEDULER=1" ]
} else if (select_scheduler == "unified") {
defines += [ "WITH_UNIFIED_SCHEDULER=1" ]
} else {
assert(false, "Invalid scheduler selection!")
}
defines += [ "SCHEDULER_TRACING_LEVEL=$scheduler_tracing_level" ]
}
# This is the kernel proper, an ELF executable with full symbols.
# It's the file to use with a debugger, for example.
zx_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.
zx_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" ]
}
}