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