about summary refs log tree commit diff
path: root/tests/run-make/compiler-builtins/rmake.rs
blob: 10093db2258dfeb83b5b4b1b049d1cfea63d3006 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! The compiler_builtins library is special. It can call functions in core, but it must not
//! require linkage against a build of core. If it ever does, building the standard library *may*
//! result in linker errors, depending on whether the linker in use applies optimizations first or
//! resolves symbols first. So the portable and safe approach is to forbid such a linkage
//! requirement entirely.
//!
//! In addition, whether compiler_builtins requires linkage against core can depend on optimization
//! settings. Turning off optimizations and enabling debug assertions tends to produce the most
//! dependence on core that is possible, so that is the configuration we test here.

// wasm and nvptx targets don't produce rlib files that object can parse.
//@ ignore-wasm
//@ ignore-nvptx64

#![deny(warnings)]

use std::collections::HashSet;

use run_make_support::object::read::Object;
use run_make_support::object::read::archive::ArchiveFile;
use run_make_support::object::{ObjectSection, ObjectSymbol, RelocationTarget};
use run_make_support::rfs::{read, read_dir};
use run_make_support::{cargo, object, path, target};

fn main() {
    let target_dir = path("target");

    println!("Testing compiler_builtins for {}", target());

    cargo()
        .args(&[
            "build",
            "--manifest-path",
            "Cargo.toml",
            "-Zbuild-std=core",
            "--target",
            &target(),
        ])
        .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes")
        .env("CARGO_TARGET_DIR", &target_dir)
        .env("RUSTC_BOOTSTRAP", "1")
        // Visual Studio 2022 requires that the LIB env var be set so it can
        // find the Windows SDK.
        .env("LIB", std::env::var("LIB").unwrap_or_default())
        .run();

    let rlibs_path = target_dir.join(target()).join("debug").join("deps");
    let compiler_builtins_rlib = read_dir(rlibs_path)
        .find_map(|e| {
            let path = e.unwrap().path();
            let file_name = path.file_name().unwrap().to_str().unwrap();
            if file_name.starts_with("libcompiler_builtins") && file_name.ends_with(".rlib") {
                Some(path)
            } else {
                None
            }
        })
        .unwrap();

    // rlib files are archives, where the archive members each a CGU, and we also have one called
    // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file.
    let data = read(compiler_builtins_rlib);

    let mut defined_symbols = HashSet::new();
    let mut undefined_relocations = HashSet::new();

    let archive = ArchiveFile::parse(&*data).unwrap();
    for member in archive.members() {
        let member = member.unwrap();
        if member.name() == b"lib.rmeta" {
            continue;
        }
        let data = member.data(&*data).unwrap();
        let object = object::File::parse(&*data).unwrap();

        // Record all defined symbols in this CGU.
        for symbol in object.symbols() {
            if !symbol.is_undefined() {
                let name = symbol.name().unwrap();
                defined_symbols.insert(name);
            }
        }

        // Find any relocations against undefined symbols. Calls within this CGU are relocations
        // against a defined symbol.
        for (_offset, relocation) in object.sections().flat_map(|section| section.relocations()) {
            let RelocationTarget::Symbol(symbol_index) = relocation.target() else {
                continue;
            };
            let symbol = object.symbol_by_index(symbol_index).unwrap();
            if symbol.is_undefined() {
                let name = symbol.name().unwrap();
                undefined_relocations.insert(name);
            }
        }
    }

    // We can have symbols in the compiler_builtins rlib that are actually from core, if they were
    // monomorphized in the compiler_builtins crate. This is totally fine, because though the call
    // is to a function in core, it's resolved internally.
    //
    // It is normal to have relocations against symbols not defined in the rlib for things like
    // unwinding, or math functions provided the target's platform libraries. Finding these is not
    // a problem, we want to specifically ban relocations against core which are not resolved
    // internally.
    undefined_relocations
        .retain(|symbol| !defined_symbols.contains(symbol) && symbol.contains("core"));

    if !undefined_relocations.is_empty() {
        panic!(
            "compiler_builtins must not link against core, but it does. \n\
            These symbols may be undefined in a debug build of compiler_builtins:\n\
            {:?}",
            undefined_relocations
        );
    }
}