[llvm-branch-commits] [llvm] [libc][bazel] Add targets for startup objects on linux (PR #218995)
Jackson Stogel via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Aug 31 18:04:18 PDT 2026
https://github.com/jtstogel updated https://github.com/llvm/llvm-project/pull/218995
>From 8ce949ad3dd8a5e020bead33264c7eede52e7a2c Mon Sep 17 00:00:00 2001
From: jtstogel <jtstogel at gmail.com>
Date: Thu, 16 Jul 2026 14:11:45 -0700
Subject: [PATCH] [libc][bazel] Add targets for startup objects
[libc][bazel] more startup object rules
[libc][bazel] Refactor internal_copts for libc_support_library
---
.../llvm-project-overlay/libc/BUILD.bazel | 1 +
.../libc/libc_build_rules.bzl | 43 ++++-
.../libc/startup/BUILD.bazel | 3 +
.../libc/startup/linux/BUILD.bazel | 103 +++++++++++
.../libc/startup/linux/x86_64/BUILD.bazel | 46 +++++
.../libc/startup/startup_rules.bzl | 173 ++++++++++++++++++
6 files changed, 367 insertions(+), 2 deletions(-)
create mode 100644 utils/bazel/llvm-project-overlay/libc/startup/BUILD.bazel
create mode 100644 utils/bazel/llvm-project-overlay/libc/startup/linux/BUILD.bazel
create mode 100644 utils/bazel/llvm-project-overlay/libc/startup/linux/x86_64/BUILD.bazel
create mode 100644 utils/bazel/llvm-project-overlay/libc/startup/startup_rules.bzl
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index e3110ee92b8cd..625281a61718e 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -4323,6 +4323,7 @@ libc_support_library(
libc_support_library(
name = "__support_threads_linux_thread",
srcs = ["src/__support/threads/linux/thread.cpp"],
+ copt_sets = ["threading"],
target_compatible_with = select({
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
diff --git a/utils/bazel/llvm-project-overlay/libc/libc_build_rules.bzl b/utils/bazel/llvm-project-overlay/libc/libc_build_rules.bzl
index 1cc7d7e31af40..3b01e668b4aa2 100644
--- a/utils/bazel/llvm-project-overlay/libc/libc_build_rules.bzl
+++ b/utils/bazel/llvm-project-overlay/libc/libc_build_rules.bzl
@@ -75,21 +75,46 @@ def libc_release_copts():
})
return copts + platform_copts
-def _libc_library(name, deps = [], **kwargs):
+# Allowlisted sets of copts that may be used for a single libc library target.
+# Adding copts here is discouraged, as it complicates the build.
+_LIBC_LIBRARY_COPT_SETS = {
+ "startup_object": [
+ "-ffreestanding",
+ "-fno-builtin",
+ "-fno-omit-frame-pointer",
+ "-fno-stack-protector",
+ ],
+ "threading": [
+ "-fno-omit-frame-pointer",
+ "-Wno-frame-address",
+ ],
+}
+
+def _libc_library(
+ name,
+ deps = [],
+ copt_sets = [],
+ **kwargs):
"""Internal macro to serve as a base for all other libc library rules.
Args:
name: Target name.
deps: cc_library deps.
+ copt_sets: Which sets of allow-listed copts to include.
**kwargs: All other attributes relevant for the cc_library rule.
"""
for attr in ["copts", "local_defines"]:
if attr in kwargs:
fail("disallowed attribute: '{}' in rule: '{}'".format(attr, name))
+
+ copts = []
+ for feature in copt_sets:
+ copts.extend(_LIBC_LIBRARY_COPT_SETS[feature])
+
cc_library(
name = name,
- copts = libc_common_copts(),
+ copts = libc_common_copts() + copts,
local_defines = LIBC_CONFIGURE_OPTIONS,
deps = deps + libc_common_deps(),
linkstatic = 1,
@@ -102,6 +127,20 @@ def _libc_library(name, deps = [], **kwargs):
def libc_support_library(name, **kwargs):
_libc_library(name = name, **kwargs)
+def libc_startup_library(name, **kwargs):
+ """Add target for a libc startup library.
+
+ Args:
+ name: Target name.
+ **kwargs: Other attributes relevant for a cc_library.
+ """
+
+ _libc_library(
+ name = name,
+ copt_sets = ["startup_object"],
+ **kwargs
+ )
+
def libc_function(name, **kwargs):
"""Add target for a libc function.
diff --git a/utils/bazel/llvm-project-overlay/libc/startup/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/startup/BUILD.bazel
new file mode 100644
index 0000000000000..3b65f08ee74a1
--- /dev/null
+++ b/utils/bazel/llvm-project-overlay/libc/startup/BUILD.bazel
@@ -0,0 +1,3 @@
+# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
diff --git a/utils/bazel/llvm-project-overlay/libc/startup/linux/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/startup/linux/BUILD.bazel
new file mode 100644
index 0000000000000..8895bc1c200c4
--- /dev/null
+++ b/utils/bazel/llvm-project-overlay/libc/startup/linux/BUILD.bazel
@@ -0,0 +1,103 @@
+# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+load("//libc:libc_build_rules.bzl", "libc_startup_library", "libc_support_library")
+load("//libc:platforms.bzl", "PLATFORM_CPU_X86_64")
+load("//libc/startup:startup_rules.bzl", "libc_startup_object", "merge_relocatable_object")
+
+package(
+ default_visibility = ["//visibility:public"],
+)
+
+libc_support_library(
+ name = "gnu_property_section",
+ srcs = ["gnu_property_section.cpp"],
+ hdrs = ["gnu_property_section.h"],
+ deps = [
+ "//libc:__support_cpp_string_view",
+ "//libc:__support_macros_attributes",
+ "//libc:__support_macros_config",
+ "//libc:hdr_elf_proxy",
+ "//libc:hdr_link_macros",
+ "//libc:string_memory_utils",
+ ],
+)
+
+libc_support_library(
+ name = "irelative_hdr",
+ hdrs = ["irelative.h"],
+ deps = [
+ "//libc:__support_macros_config",
+ "//libc:hdr_link_macros",
+ "//libc:hdr_stdint_proxy",
+ ],
+)
+
+libc_startup_library(
+ name = "do_start",
+ srcs = ["do_start.cpp"],
+ hdrs = [
+ "do_start.h",
+ ],
+ target_compatible_with = select({
+ "@platforms//os:linux": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ }),
+ deps = [
+ ":gnu_property_section",
+ ":irelative_hdr",
+ "//libc:__support_macros_config",
+ "//libc:__support_osutil_linux_auxv",
+ "//libc:__support_osutil_syscall",
+ "//libc:__support_threads_thread",
+ "//libc:__support_threads_thread_headers",
+ "//libc:_r_debug",
+ "//libc:atexit",
+ "//libc:config_app_h",
+ "//libc:config_linux_app_h",
+ "//libc:environ",
+ "//libc:exit",
+ "//libc:hdr_elf_proxy",
+ "//libc:hdr_link_macros",
+ "//libc:hdr_pthread_macros",
+ "//libc:hdr_stdint_proxy",
+ "//libc:hdr_sys_auxv_macros",
+ "//libc:hdr_sys_mman_macros",
+ "//libc:program_invocation_name",
+ "//libc:program_invocation_short_name",
+ "//libc:types_struct_link_map",
+ "//libc:types_struct_r_debug",
+ ] + select({
+ PLATFORM_CPU_X86_64: [
+ "//libc/startup/linux/x86_64:irelative",
+ "//libc/startup/linux/x86_64:tls",
+ ],
+ "//conditions:default": [],
+ }),
+)
+
+merge_relocatable_object(
+ name = "crt1",
+ deps = [
+ ":do_start",
+ ":gnu_property_section",
+ ] + select({
+ PLATFORM_CPU_X86_64: [
+ "//libc/startup/linux/x86_64:irelative",
+ "//libc/startup/linux/x86_64:start",
+ "//libc/startup/linux/x86_64:tls",
+ ],
+ "//conditions:default": [],
+ }),
+)
+
+libc_startup_object(
+ name = "crti",
+ src = "crti.cpp",
+)
+
+libc_startup_object(
+ name = "crtn",
+ src = "crtn.cpp",
+)
diff --git a/utils/bazel/llvm-project-overlay/libc/startup/linux/x86_64/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/startup/linux/x86_64/BUILD.bazel
new file mode 100644
index 0000000000000..b9936609522b9
--- /dev/null
+++ b/utils/bazel/llvm-project-overlay/libc/startup/linux/x86_64/BUILD.bazel
@@ -0,0 +1,46 @@
+# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+load("//libc:libc_build_rules.bzl", "libc_startup_library")
+
+package(
+ default_visibility = ["//visibility:public"],
+)
+
+libc_startup_library(
+ name = "tls",
+ srcs = ["tls.cpp"],
+ deps = [
+ "//libc:__support_macros_config",
+ "//libc:__support_osutil_linux_syscall_wrappers_getrandom",
+ "//libc:__support_osutil_linux_syscall_wrappers_mmap",
+ "//libc:__support_osutil_linux_syscall_wrappers_munmap",
+ "//libc:config_app_h",
+ "//libc:hdr_sys_mman_macros",
+ "//libc:string_memory_utils",
+ ],
+)
+
+libc_startup_library(
+ name = "start",
+ srcs = ["start.cpp"],
+ deps = [
+ "//libc:__support_macros_attributes",
+ "//libc:config_app_h",
+ "//libc/startup/linux:do_start",
+ ],
+)
+
+libc_startup_library(
+ name = "irelative",
+ srcs = ["irelative.cpp"],
+ deps = [
+ "//libc:__support_macros_config",
+ "//libc:hdr_elf_macros",
+ "//libc:hdr_elf_proxy",
+ "//libc:hdr_link_macros",
+ "//libc:hdr_stdint_proxy",
+ "//libc/startup/linux:irelative_hdr",
+ ],
+)
diff --git a/utils/bazel/llvm-project-overlay/libc/startup/startup_rules.bzl b/utils/bazel/llvm-project-overlay/libc/startup/startup_rules.bzl
new file mode 100644
index 0000000000000..224d7474c4206
--- /dev/null
+++ b/utils/bazel/llvm-project-overlay/libc/startup/startup_rules.bzl
@@ -0,0 +1,173 @@
+# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+"""LLVM libc starlark rules for building startup objects."""
+
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
+load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain", "use_cc_toolchain")
+load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+load("//libc:libc_build_rules.bzl", "libc_startup_library")
+
+def _get_compilation_outputs(deps):
+ outputs = []
+ for dep in deps:
+ if OutputGroupInfo in dep and "compilation_outputs" in dep[OutputGroupInfo]:
+ outputs.extend(dep[OutputGroupInfo].compilation_outputs.to_list())
+ return outputs
+
+def _extract_object_file_impl(ctx):
+ output = ctx.actions.declare_file(ctx.label.name + ".o")
+ input_objs = _get_compilation_outputs([ctx.attr.dep])
+ if len(input_objs) != 1:
+ fail("Expected exactly one input object, got: {}".format(input_objs))
+
+ input_obj = input_objs[0]
+
+ ctx.actions.symlink(
+ output = output,
+ target_file = input_obj,
+ )
+
+ return [DefaultInfo(files = depset([output]))]
+
+_extract_object_file = rule(
+ implementation = _extract_object_file_impl,
+ attrs = {
+ "dep": attr.label(
+ mandatory = True,
+ providers = [CcInfo],
+ ),
+ },
+)
+
+def libc_startup_object(name, src, **kwargs):
+ """Compiles a C++ source file into a startup object file.
+
+ Args:
+ name: The name of the target.
+ src: The C++ source file to compile.
+ **kwargs: Other arguments to
+ """
+ library_name = name + "_lib"
+ libc_startup_library(
+ name = library_name,
+ srcs = [src],
+ **kwargs
+ )
+ _extract_object_file(
+ name = name,
+ dep = ":" + library_name,
+ )
+
+def _filter_flags(
+ flags,
+ separate_flag_names,
+ joined_flag_prefixes):
+ """Filters flags to those in joined_flag_prefixes or separate_flag_names.
+
+ Args:
+ flags: The flags to filter.
+ separate_flag_names: Names of flags whose value is specified separately
+ from the flag (for example "--target value").
+ joined_flag_prefixes: Prefixes of flags whose value is joined to the
+ flag name (for example, --target=value).
+ """
+ filtered_flags = []
+ skip_next = False
+ for i, flag in enumerate(flags):
+ if skip_next:
+ skip_next = False
+ continue
+
+ if flag in separate_flag_names:
+ if i + 1 < len(flags):
+ filtered_flags.append(flag)
+ filtered_flags.append(flags[i + 1])
+ skip_next = True
+ continue
+
+ for prefix in joined_flag_prefixes:
+ if flag.startswith(prefix):
+ filtered_flags.append(flag)
+ continue
+
+ return filtered_flags
+
+def _merge_relocatable_object_impl(ctx):
+ cc_toolchain = find_cc_toolchain(ctx)
+ output = ctx.actions.declare_file(ctx.label.name + ".o")
+
+ input_objs = _get_compilation_outputs(ctx.attr.deps)
+
+ feature_configuration = cc_common.configure_features(
+ ctx = ctx,
+ cc_toolchain = cc_toolchain,
+ requested_features = ctx.features,
+ unsupported_features = ctx.disabled_features,
+ )
+ link_variables = cc_common.create_link_variables(
+ cc_toolchain = cc_toolchain,
+ feature_configuration = feature_configuration,
+ is_linking_dynamic_library = False,
+ )
+ link_flags = cc_common.get_memory_inefficient_command_line(
+ feature_configuration = feature_configuration,
+ action_name = ACTION_NAMES.cpp_link_executable,
+ variables = link_variables,
+ )
+ linker = cc_common.get_tool_for_action(
+ feature_configuration = feature_configuration,
+ action_name = ACTION_NAMES.cpp_link_executable,
+ )
+ relocatable_link_flags = _filter_flags(
+ link_flags,
+ ["-target", "--target", "--sysroot", "-isysroot"],
+ ["-fuse-ld=", "-m", "--target=", "--sysroot="],
+ )
+
+ args = ctx.actions.args()
+ args.add_all(relocatable_link_flags)
+
+ bindir = paths.dirname(linker)
+ if bindir:
+ args.add("-B" + bindir)
+
+ args.add("-r")
+ args.add("-nostdlib")
+ args.add("-o", output)
+ args.add_all(input_objs)
+
+ ctx.actions.run(
+ outputs = [output],
+ inputs = depset(
+ input_objs,
+ transitive = [cc_toolchain.all_files],
+ ),
+ executable = linker,
+ arguments = [args],
+ mnemonic = "MergeRelocatableObject",
+ use_default_shell_env = True,
+ )
+
+ return [DefaultInfo(files = depset([output]))]
+
+merge_relocatable_object = rule(
+ implementation = _merge_relocatable_object_impl,
+ doc = """Merges multiple object files into a single relocatable object file.
+
+ This rule mimics CMake's `merge_relocatable_object`,
+ running the toolchain's linker driver `-r -nostdlib` on all direct deps.
+ """,
+ attrs = {
+ "deps": attr.label_list(
+ mandatory = True,
+ providers = [CcInfo],
+ doc = "The list of cc targets whose object files should be merged.",
+ ),
+ },
+ toolchains = use_cc_toolchain(),
+ fragments = ["cpp"],
+)
More information about the llvm-branch-commits
mailing list