rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.35k stars 12.59k forks source link

`RUSTFLAGS=-Clink-dead-code` breaks rustc build (alternatives?) #103064

Closed AE1020 closed 2 months ago

AE1020 commented 1 year ago

I'm attempting to add instrumentation functions to rustc, to be called from the LLDB debug console. Here's a simple example of trying to add a C-style function and a Rust function to a hello world program:

#[no_mangle] #[allow(dead_code)]
pub extern "C" fn c_from_debugger_test() {
    println!("I am a c call from the debugger");
}

#[no_mangle] #[allow(dead_code)]
pub fn rust_from_debugger_test(num: u8) {
    println!("I am a rust {} call from the debugger", num);
}

fn main() {
    println!("Hello, I'm just a main()");
}

I built this with rustc -g hello.rs. Then with a breakpoint in main, at the LLDB debug console I tried:

> print c_from_debugger_test()
error: could not find item

> print rust_from_debugger_test(10)
error: could not find item

I had seen suggestions online that adding #[no_mangle] should be enough to get a function into the root set. That's echoed in comments at time of writing in the rust compiler sources at compiler/rustc_monomorphize/src/collector.rs:

//! ... (In eager
//! collection mode, during incremental compilation, all non-generic functions
//! are considered as roots, as well as when the `-Clink-dead-code` option is
//! specified. Functions marked `#[no_mangle]` and functions called by inlinable
//! functions also always act as roots.)

The functions would be included in the link if I instead built with rustc -g hello.rs -Clink-dead-code:

> print c_from_debugger_test()
(()) = () {}  // terminal shows "I am a c call from the debugger"

> print rust_from_debugger_test(10)
(()) = () {}  // terminal shows "I am a rust 10 call from the debugger"

Unfortunately... there are issues (e.g. #77529) with using -Clink-dead-code when building some codebases, and that includes building the rust compiler itself.

If I try building rustc with export RUSTFLAGS="-Clink-dead-code", this error stops the build (probably there would be others):

Compiling rustc-std-workspace-core v1.99.0 (/home/ae1020/rust/library/rustc-std-workspace-core)
thread 'rustc' panicked at 'called `Option::unwrap()` on a `None` value' 
    compiler/rustc_monomorphize/src/collector.rs:1379:36

(Coincidentally (?), that's the file with the comment about #[no_mangle] that I cite above.)

What I'm actually looking for is a way to add these root functions callable from the debugger to the rustc compiler...

(A workaround is to hack in spurious calls reachable from main() somewhere to the instrumentation functions.)

Meta

$ rustc --version --verbose
rustc 1.64.0 (a55dd71d5 2022-09-19)
binary: rustc
commit-hash: a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52
commit-date: 2022-09-19
host: x86_64-unknown-linux-gnu
release: 1.64.0
LLVM version: 14.0.6
bjorn3 commented 1 year ago

Without -Clink-dead-code=yes rustc will pass --gc-section to the linker. This will cause every symbol not reachable from an exported symbol to be omitted. In case of executables only main is exported unless -Z export-executable-symbols=yes is used.

Possible if #[no_mangle] caused functions to be considered live roots, as the comment suggests

If this is not true, should the comment be updated?

The comment is about the functions that will be codegened, not about those that end up in the final executable after whatever logic the linker uses to determine if functions should be kept.

Enselic commented 2 months ago

Triage: Seems like -Z export-executable-symbols=yes is the right solution here? Closing.