Prevent malloc from being linked into boot_stage2 Prevents Bazel from ever trying to link malloc into the boot_stage2 binary.
diff --git a/bazel/platform/BUILD.bazel b/bazel/platform/BUILD.bazel index 467c0b1..ca9b829 100644 --- a/bazel/platform/BUILD.bazel +++ b/bazel/platform/BUILD.bazel
@@ -7,3 +7,9 @@ "@platforms//cpu:armv6-m", # This is just FYI. ], ) + +# For now, the bootloader is effectively the same platform as the rp2040. +platform( + name = "rp2040_bootloader", + parents = [":rp2040"], +)
diff --git a/bazel/toolchain/objcopy.bzl b/bazel/toolchain/objcopy.bzl index 153b69b..c1ba06a 100644 --- a/bazel/toolchain/objcopy.bzl +++ b/bazel/toolchain/objcopy.bzl
@@ -1,5 +1,5 @@ -load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain", "use_cc_toolchain") load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "OBJ_COPY_ACTION_NAME") +load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain", "use_cc_toolchain") def _objcopy_to_bin_impl(ctx): cc_toolchain = find_cpp_toolchain(ctx)
diff --git a/bazel/util/BUILD.bazel b/bazel/util/BUILD.bazel new file mode 100644 index 0000000..ffd0fb0 --- /dev/null +++ b/bazel/util/BUILD.bazel
@@ -0,0 +1 @@ +package(default_visibility = ["//visibility:public"])
diff --git a/bazel/util/transition.bzl b/bazel/util/transition.bzl new file mode 100644 index 0000000..ee5947d --- /dev/null +++ b/bazel/util/transition.bzl
@@ -0,0 +1,76 @@ +# A transition in Bazel is a way to force changes to the way the build is +# evaluated for all dependencies of a given rule. +# +# Imagine the following simple dependency graph: +# +# ->: depends on +# a -> b -> c +# +# Normally, if you set `defines` on a, they couldn't apply to b or c because +# they are dependencies of a. There's no way for b or c to know about a's +# settings, because they don't even know a exists! +# +# We can fix this via a transition! If we put a transition in front of `a` +# that sets --copts=-DFOO=42, we're telling Bazel to build a and all of its +# dependencies under that configuration. +# +# Note: Flags must be referenced as e.g. `//command_line_option:copt` in +# transitions. +# +# `declare_transition()` eliminates the frustrating amount of boilerplate. All +# you need to do is provide a set of attrs, and then a `flag_overrides` +# dictionary that tells `declare_transition()` which attrs to pull flag values +# from. The common `src` attr tells the transition which build rule to apply +# the transition to. +def declare_transtion(attrs, flag_overrides, executable = True): + def _flag_override_impl(settings, attrs): + return { + key: str(getattr(attrs, value)) + for key, value in flag_overrides.items() + } + + _transition = transition( + implementation = _flag_override_impl, + inputs = [], + outputs = flag_overrides.keys(), + ) + + def _symlink_artifact_impl(ctx): + out = ctx.actions.declare_file(ctx.label.name) + if executable: + ctx.actions.symlink(output = out, target_file = ctx.executable.src) + return [DefaultInfo(files = depset([out]), executable = out)] + + ctx.actions.symlink( + output = out, + target_file = ctx.attr.src[0][DefaultInfo].files.to_list()[0], + ) + return [DefaultInfo(files = depset([out]))] + + return rule( + implementation = _symlink_artifact_impl, + executable = executable, + attrs = { + "src": attr.label( + cfg = _transition, + executable = executable, + mandatory = True, + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + } | attrs, + ) + +rp2040_bootloader_binary = declare_transtion( + attrs = { + "_malloc": attr.label(default = "//src/rp2_common/boot_stage2:no_malloc"), + "_bootloader_platform": attr.label(default = "//bazel/platform:rp2040_bootloader"), + }, + flag_overrides = { + "//command_line_option:platforms": "_bootloader_platform", + # We don't want --custom_malloc to ever apply to the bootloader, so + # always explicitly override it here. + "//command_line_option:custom_malloc": "_malloc", + }, +)
diff --git a/src/rp2_common/boot_stage2/BUILD.bazel b/src/rp2_common/boot_stage2/BUILD.bazel index e6c175d..df6a041 100644 --- a/src/rp2_common/boot_stage2/BUILD.bazel +++ b/src/rp2_common/boot_stage2/BUILD.bazel
@@ -1,6 +1,7 @@ load("@bazel_skylib//rules:copy_file.bzl", "copy_file") load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("//bazel/toolchain:objcopy.bzl", "objcopy_to_bin") +load("//bazel/util:transition.bzl", "rp2040_bootloader_binary") package(default_visibility = ["//visibility:private"]) @@ -32,7 +33,7 @@ ) cc_binary( - name = "boot_stage2_elf", + name = "boot_stage2_elf_actual", srcs = ["compile_time_choice.S"], copts = ["-fPIC"], linkopts = [ @@ -40,7 +41,10 @@ "-nostartfiles", "-T$(location boot_stage2.ld)", ], + # this does nothing if someone passes --custom_malloc, so the + # rp2040_bootloader_binary transition forcibly clobbers --custom_malloc. malloc = ":no_malloc", + tags = ["manual"], deps = [ "boot_stage2.ld", ":config", @@ -49,6 +53,12 @@ ], ) +# Always build the bootloader with the bootloader-specific platform. +rp2040_bootloader_binary( + name = "boot_stage2_elf", + src = "boot_stage2_elf_actual", +) + objcopy_to_bin( name = "boot_stage2_bin", src = ":boot_stage2_elf",