1# Copyright 2021 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5# This file provides the Rust standard library for GN targets. 6# 7# For Rust targets, it either copies a prebuilt stdlib or builds a stdlib, and 8# then points rustc to it with `--sysroot`. 9# 10# When linking it ensures the libraries (and their C library dependencies) are 11# part of the linker line. If Rust drives the linking, this is redundant but if 12# Clang drives the linking it is required. 13# 14# Part of the standard library provided here is "remap_alloc" which maps 15# allocator functions to PartitionAlloc when `use_partition_alloc_as_malloc` is 16# true, so that Rust and C++ use the same allocator backend. 17 18import("//build/buildflag_header.gni") 19import("//build/config/compiler/compiler.gni") 20import("//build/config/coverage/coverage.gni") 21import("//build/config/rust.gni") 22import("//build/config/sanitizers/sanitizers.gni") 23 24rust_allocator_uses_partition_alloc = false 25if (build_with_chromium) { 26 import("//base/allocator/partition_allocator/partition_alloc.gni") 27 rust_allocator_uses_partition_alloc = use_partition_alloc_as_malloc 28} 29 30buildflag_header("buildflags") { 31 header = "buildflags.h" 32 flags = [ 33 "RUST_ALLOCATOR_USES_PARTITION_ALLOC=$rust_allocator_uses_partition_alloc", 34 ] 35 visibility = [ ":*" ] 36} 37 38if (toolchain_has_rust) { 39 # If clang performs the link step, we need to provide the allocator symbols 40 # that are normally injected by rustc during linking. 41 # 42 # We also "happen to" use this to redirect allocations to PartitionAlloc, 43 # though that would be better done through a #[global_allocator] crate (see 44 # above). 45 source_set("remap_alloc") { 46 public_deps = [] 47 if (rust_allocator_uses_partition_alloc) { 48 public_deps += [ "//base/allocator/partition_allocator:partition_alloc" ] 49 } 50 deps = [ ":buildflags" ] 51 sources = [ 52 # `alias.*`, `compiler_specific.h`, and `immediate_crash.*` have been 53 # copied from `//base`. 54 # TODO(crbug.com/40279749): Avoid duplication / reuse code. 55 "alias.cc", 56 "alias.h", 57 "compiler_specific.h", 58 "immediate_crash.h", 59 "remap_alloc.cc", 60 ] 61 } 62 63 # List of Rust stdlib rlibs which are present in the official Rust toolchain 64 # we are using from the Android team. This is usually a version or two behind 65 # nightly. Generally this matches the toolchain we build ourselves, but if 66 # they differ, append or remove libraries based on the 67 # `use_chromium_rust_toolchain` GN variable. 68 # 69 # If the build fails due to missing symbols, it would be because of a missing 70 # library that needs to be added here in a newer stdlib. 71 stdlib_files = [ 72 "std", # List first because it makes depfiles more debuggable (see below) 73 "alloc", 74 "cfg_if", 75 "compiler_builtins", 76 "core", 77 "getopts", 78 "hashbrown", 79 "panic_abort", 80 "panic_unwind", 81 "rustc_demangle", 82 "std_detect", 83 "test", 84 "unicode_width", 85 "unwind", 86 ] 87 88 if (!is_win) { 89 # These are no longer present in the Windows toolchain. 90 stdlib_files += [ 91 "addr2line", 92 "adler", 93 "gimli", 94 "libc", 95 "memchr", 96 "miniz_oxide", 97 "object", 98 ] 99 } 100 101 if (toolchain_for_rust_host_build_tools) { 102 # When building proc macros, include the proc_macro crate in what should be 103 # copied with find_stdlib. Otherwise it is not copied since it will be 104 # unused. 105 stdlib_files += [ "proc_macro" ] 106 } 107 108 # Different Rust toolchains may add or remove files relative to the above 109 # list. That can be specified in gn args for anyone using (for instance) 110 # nightly or some other experimental toolchain, prior to it becoming official. 111 stdlib_files -= removed_rust_stdlib_libs 112 stdlib_files += added_rust_stdlib_libs 113 114 # rlib files which are distributed alongside Rust's prebuilt stdlib, but we 115 # don't need to pass to the C++ linker because they're used for specialized 116 # purposes. 117 skip_stdlib_files = [ 118 "profiler_builtins", 119 "rustc_std_workspace_alloc", 120 "rustc_std_workspace_core", 121 "rustc_std_workspace_std", 122 ] 123 124 config("stdlib_dependent_libs") { 125 # TODO(crbug.com/40264561): These should really be `libs`, however that 126 # breaks. Normally, we specify lib files with the `.lib` suffix but 127 # then when rustc links an EXE, it invokes lld-link with `.lib.lib` 128 # instead. 129 # 130 # Omitting the `.lib` suffix breaks linking as well, when clang drives 131 # the linking step of a C++ EXE that depends on Rust. 132 if (is_win) { 133 # The libc crate tries to link in the Windows CRT, but we specify the CRT 134 # library ourselves in //build/config/win:dynamic_crt and 135 # //build/config/win:static_crt because Rustc does not allow us to specify 136 # using the debug CRT: https://github.com/rust-lang/rust/issues/39016 137 # 138 # As such, we have disabled all #[link] directives from the libc crate, 139 # and we need to add any non-CRT libs here. 140 ldflags = [ "legacy_stdio_definitions.lib" ] 141 } 142 } 143 config("stdlib_public_dependent_libs") { 144 # TODO(crbug.com/40264561): These should really be `libs`, however that 145 # breaks. Normally, we specify lib files with the `.lib` suffix but 146 # then when rustc links an EXE, it invokes lld-link with `.lib.lib` 147 # instead. 148 # 149 # Omitting the `.lib` suffix breaks linking as well, when clang drives 150 # the linking step of a C++ EXE that depends on Rust. 151 if (is_win) { 152 # These libs provide functions that are used by the stdlib. Rust crates 153 # will try to link them in with #[link] directives. However these don't 154 # get propagated to the linker if Rust isn't driving the linking (a C++ 155 # target that depends on a Rust rlib). So these need to be specified 156 # explicitly. 157 ldflags = [ 158 "advapi32.lib", 159 "bcrypt.lib", 160 "kernel32.lib", 161 "ntdll.lib", 162 "synchronization.lib", 163 "userenv.lib", 164 "ws2_32.lib", 165 ] 166 } 167 168 # From rust/library/std/src/sys/unix/mod.rs. 169 # 170 # libs = [ "System" ] (meaning /usr/lib/libSystem.B.dylib) is intentionally 171 # omitted here on Apple platforms, because the linker driver is responsible 172 # for adding it, much like libc on most other POSIX platforms. It can be 173 # ordering-sensitive, and including it here can alter its order relative to 174 # other libraries. https://crbug.com/367764848 is an example of a bug caused 175 # by specifying libSystem too early. 176 # 177 # TODO(danakj): We should generate this list somehow when building or 178 # rolling the Rust toolchain? 179 if (is_android) { 180 libs = [ "dl" ] 181 } else if (target_os == "freebsd") { 182 libs = [ 183 "execinfo", 184 "pthread", 185 ] 186 } else if (target_os == "netbsd") { 187 libs = [ 188 "rt", 189 "pthread", 190 ] 191 } else if (is_ios) { 192 libs = [ "objc" ] 193 frameworks = [ 194 "Security.framework", 195 "Foundation.framework", 196 ] 197 } else if (is_fuchsia) { 198 libs = [ 199 "zircon", 200 "fdio", 201 ] 202 } 203 } 204 205 # Construct sysroots for rustc invocations to better control what libraries 206 # are linked. We have two: one with copied prebuilt libraries, and one with 207 # our locally-built std. Both reside in root_out_dir: we must only have one of 208 # each per GN toolchain anyway. 209 210 sysroot_lib_subdir = "lib/rustlib/$rust_abi_target/lib" 211 212 if (!rust_prebuilt_stdlib) { 213 local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot" 214 215 # All std targets starting with core build with our sysroot. It starts empty 216 # and is incrementally built. The directory must exist at the start. 217 generated_file("empty_sysroot_for_std_build") { 218 outputs = [ "$local_rustc_sysroot/$sysroot_lib_subdir/.empty" ] 219 contents = "" 220 visibility = [ ":*" ] 221 } 222 223 # Target to be depended on by std build targets. Creates the initially empty 224 # sysroot. 225 group("std_build_deps") { 226 deps = [ ":empty_sysroot_for_std_build" ] 227 public_configs = [ ":local_stdlib_sysroot" ] 228 visibility = [ "rules:*" ] 229 } 230 231 profiler_builtins_crates = [ 232 "core", 233 "compiler_builtins", 234 "profiler_builtins", 235 ] 236 237 # When using instrumentation, profiler_builtins and its deps must be built 238 # before other std crates. Other crates depend on this target so they are 239 # built in the right order. 240 group("profiler_builtins_group") { 241 deps = [] 242 foreach(libname, profiler_builtins_crates) { 243 deps += [ "rules:$libname" ] 244 } 245 visibility = [ "rules:*" ] 246 } 247 248 config("local_stdlib_sysroot") { 249 sysroot = rebase_path(local_rustc_sysroot, root_build_dir) 250 rustflags = [ "--sysroot=$sysroot" ] 251 visibility = [ ":*" ] 252 } 253 254 # Builds and links against the Rust stdlib. Both Rust and C++ targets should 255 # depend on this, as it provides the path to the library and includes the 256 # allocator hooks. 257 group("std") { 258 assert(toolchain_has_rust, 259 "Some C++ target is depending on Rust code even though " + 260 "toolchain_has_rust=false. Usually this would mean" + 261 "a NaCl target is depending on Rust, as there's no Rust " + 262 "toolchain targetting NaCl.") 263 all_dependent_configs = [ 264 ":stdlib_public_dependent_libs", 265 ":local_stdlib_sysroot", 266 ":stdlib_dependent_libs", 267 ] 268 deps = [] 269 foreach(libname, stdlib_files + skip_stdlib_files) { 270 deps += [ "rules:$libname" ] 271 } 272 273 public_deps = [ ":remap_alloc" ] 274 } 275 } else { 276 action("find_stdlib") { 277 # Collect prebuilt Rust libraries from toolchain package and copy to a 278 # known location. 279 # 280 # The Rust toolchain contains prebuilt rlibs for the standard library and 281 # its dependencies. However, they have unstable names: an unpredictable 282 # metadata hash is appended to the known crate name. 283 # 284 # We must depend on these rlibs explicitly when rustc is not in charge of 285 # linking. However, it is difficult to construct GN rules to do so when 286 # the names can't be known statically. 287 # 288 # This action copies the prebuilt rlibs to a known location, removing the 289 # metadata part of the name. In the process it verifies we have all the 290 # libraries we expect and none that we don't. A depfile is generated so 291 # this step is re-run when any libraries change. The action script 292 # additionally verifies rustc matches the expected version, which is 293 # unrelated but this is a convenient place to do so. 294 # 295 # The action refers to `stdlib_files`, `skip_stdlib_files`, and the 296 # associated //build/config/rust.gni vars `removed_rust_stdlib_libs` and 297 # `added_rust_stdlib_libs` for which rlib files to expect. 298 # `extra_sysroot_libs` is also used to copy non-std libs, if any. 299 script = "find_std_rlibs.py" 300 depfile = "$target_out_dir/stdlib.d" 301 out_libdir = rebase_path(target_out_dir, root_build_dir) 302 out_depfile = rebase_path(depfile, root_build_dir) 303 304 # For the rustc sysroot we must include even the rlibs we don't pass to 305 # the C++ linker. 306 all_stdlibs_to_copy = stdlib_files + skip_stdlib_files 307 args = [ 308 "--rust-bin-dir", 309 rebase_path("${rust_sysroot}/bin", root_build_dir), 310 "--output", 311 out_libdir, 312 "--depfile", 313 out_depfile, 314 315 # Due to limitations in Ninja's handling of .d files, we have to pick 316 # *the first* of our outputs. To make diagnostics more obviously 317 # related to the Rust standard library, we ensure libstd.rlib is first. 318 "--depfile-target", 319 stdlib_files[0], 320 321 # Create a dependency on the rustc version so this action is re-run when 322 # it changes. This argument is not actually read by the script. 323 "--rustc-revision", 324 rustc_revision, 325 ] 326 327 if (extra_sysroot_libs != []) { 328 args += [ 329 "--extra-libs", 330 string_join(",", extra_sysroot_libs), 331 ] 332 } 333 334 args += [ 335 "--target", 336 rust_abi_target, 337 ] 338 339 outputs = [] 340 foreach(lib, all_stdlibs_to_copy) { 341 outputs += [ "$target_out_dir/lib$lib.rlib" ] 342 } 343 foreach(lib, extra_sysroot_libs) { 344 outputs += [ "$target_out_dir/$lib" ] 345 } 346 347 visibility = [ ":*" ] 348 } 349 350 prebuilt_rustc_sysroot = "$root_out_dir/prebuilt_rustc_sysroot" 351 copy("prebuilt_rustc_copy_to_sysroot") { 352 assert(enable_rust, 353 "Some C++ target is including Rust code even though " + 354 "enable_rust=false") 355 deps = [ ":find_stdlib" ] 356 sources = get_target_outputs(":find_stdlib") 357 outputs = 358 [ "$prebuilt_rustc_sysroot/$sysroot_lib_subdir/{{source_file_part}}" ] 359 360 visibility = [ ":*" ] 361 } 362 363 config("prebuilt_stdlib_sysroot") { 364 # Match the output directory of :prebuilt_rustc_copy_to_sysroot 365 sysroot = rebase_path(prebuilt_rustc_sysroot, root_build_dir) 366 rustflags = [ "--sysroot=$sysroot" ] 367 visibility = [ ":*" ] 368 } 369 370 config("prebuilt_stdlib_libs") { 371 ldflags = [] 372 lib_dir = rebase_path("$prebuilt_rustc_sysroot/$sysroot_lib_subdir", 373 root_build_dir) 374 375 # We're unable to make these files regular gn dependencies because 376 # they're prebuilt. Instead, we'll pass them in the ldflags. This doesn't 377 # work for all types of build because ldflags propagate differently from 378 # actual dependencies and therefore can end up in different targets from 379 # the remap_alloc.cc above. For example, in a component build, we might 380 # apply the remap_alloc.cc file and these ldlags to shared object A, 381 # while shared object B (that depends upon A) might get only the ldflags 382 # but not remap_alloc.cc, and thus the build will fail. There is 383 # currently no known solution to this for the prebuilt stdlib - this 384 # problem does not apply with configurations where we build the stdlib 385 # ourselves, which is what we'll use in production. 386 foreach(lib, stdlib_files) { 387 this_file = "$lib_dir/lib$lib.rlib" 388 ldflags += [ this_file ] 389 } 390 visibility = [ ":*" ] 391 } 392 393 group("std") { 394 all_dependent_configs = [ 395 ":prebuilt_stdlib_libs", 396 ":stdlib_public_dependent_libs", 397 ] 398 deps = [ ":prebuilt_rustc_copy_to_sysroot" ] 399 400 # The host builds tools toolchain supports Rust only and does not use 401 # the allocator remapping to point it to PartitionAlloc. 402 if (!toolchain_for_rust_host_build_tools) { 403 deps += [ ":remap_alloc" ] 404 } 405 } 406 } 407} 408