Description
Summary
If a function is only referenced by a sym
operand in global_asm!
, the definition/body of that function will not be emitted by the compiler, and the linker will fail with unresolved symbol
error.
Reproduction
Save the following code as example.rs
: (https://rust.godbolt.org/z/4jbf3eWjb)
#![no_std]
#![no_main]
#![feature(asm_sym)]
use core::arch::global_asm;
fn foo() {
loop {}
}
static MY_STATIC: i32 = 0;
global_asm!(".global _start", "_start:", "call {}", sym foo); // ^1
// global_asm!(".global _start", "_start:", "lea rax, [rip + {}]", sym MY_STATIC); // ^2
// #[no_mangle] // ^3
// unsafe extern "sysv64" fn _start() {
// core::arch::asm!("call {}", sym foo)
// }
#[panic_handler]
fn handle_panic(_panic: &core::panic::PanicInfo<'_>) -> ! {
//foo(); // ^4
loop {}
}
(.global _start
is there to stop the linker from pruning unreachable code.)
Run rustc example.rs -Cpanic=abort -Clink-args="-nostdlib"
produces a linker error "undefined reference to example::main
":
error: linking with `cc` failed: exit status: 1
|
= note: "cc" "-m64" "example.example.2372ba8c-cgu.0.rcgu.o" "-Wl,--as-needed" "-L" "/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-2a6a2797f7a73818.rlib" "/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-0e3656b1fda5fd7b.rlib" "/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-16d69221f10b0282.rlib" "-Wl,-Bdynamic" "-Wl,--eh-frame-hdr" "-Wl,-znoexecstack" "-L" "/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "example" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro,-znow" "-nodefaultlibs" "-nostdlib"
note: /usr/bin/ld: example.example.2372ba8c-cgu.0.rcgu.o: in function `_start':
example.2372ba8c-cgu.0:(.text+0x1): undefined reference to `example::foo'
objdump -td example.example.2372ba8c-cgu.0.rcgu.o
verifies that example::foo
is indeed missing:
0000000000000000 g .text 0000000000000000 _start
0000000000000000 *UND* 0000000000000000 _ZN7example3foo17h902e29a481949057E
The emitted llvm-ir looks something like this:
module asm ".global _start"
module asm "_start:"
module asm "call _ZN7example3foo17h902e29a481949057E"
@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (void ()* @_ZN7example3foo17h902e29a481949057E to i8*)], section "llvm.metadata"
; example::foo
; Function Attrs: nounwind nonlazybind
declare void @_ZN7example3foo17h902e29a481949057E() unnamed_addr #1
Some observations
-
The problem only appears if the function in question isn't used anywhere else. If we uncomment line
^4
, the resulting IR will be correctly emitted:; omit identical part ; example::foo ; Function Attrs: nounwind nonlazybind define internal void @_ZN7example3foo17h902e29a481949057E() unnamed_addr #0 { start: br label %bb1 bb1: ; preds = %bb1, %start br label %bb1 }
-
Only functions can reproduce, static items work fine, as per line
^2
. -
Only
global_asm!
can reproduce,asm!
works fine, as per line^3
and co.
Meta
rustc 1.62.0-nightly (7c4b47696 2022-04-30)
binary: rustc
commit-hash: 7c4b47696907d64eff5621a64eb3c6e795a9ec77
commit-date: 2022-04-30
host: x86_64-unknown-linux-gnu
release: 1.62.0-nightly
LLVM version: 14.0.1
cc @Amanieu #94468
@rustbot label +A-inline-assembly +requires-nightly