Change:
This code will fail to link because is merged into . Since global asms in LLVM can't reference symbols natively, we currently will add the mangled name to the string and pass it to LLVM. To prevent the file being optimized away, we add it to . A few issues exist: does not handle these functions specially, and therefore above is considered as internal function and have attribute attached. LLVM believes that it is allowed to merge internal functions into an exported function despite present in . I think LLVM should be allowed to this because we emit the internal linkage, and from my understanding renaming a local symbol should probably be allowed. argues otherwise and says that shouldn't touch the symbol. The symbols are , but it wouldn't show up as used in the exported symbol list, so it can still be stripped while doing LTO. We should do the following: [ ] Ensure that these functions are not emitted with internal linkage [ ] Ensrue that the functions used will be marked as used in and thus reach the linker/LTO. [ ] Ask LLVM to clarify the behaviour of combining + . cc label: +A-inline-assembly +F-asm This will block
I think should be enough to ensure that the symbol reaches the linker even through LTO. So really the only thing to do is change the symbol visibility. This isn't ideal though since we don't need this symbol to be exported outside the current compilation unit, it's just that LLVM is unable to see the use of the symbol.
It could be argued that this is actually an LLVM bug since linkage is supposed to preserve the symbol name in the object file:
I had to have to prevent from being thrown away.
Anyway, the biggest issue is the linkage, which is wrong anyway if the global asm and the referenced symbol are not in the same CGU. Given that currently both and the linkage computation comes from the same source of truth , a potential fix should be able to address all issues simultaneously.
This is fixed by . The proper behavior for LLVM is still unclear, but at least fixes the immediate issue since those symbols now have external linkage. This is a separate issue, but I think the proper way to fix this is to ensure that the itself reaches the linker. The problem here is that if there are no symbols to pull in the object file containing a from an archive then that will not be linked into the final binary. I think we can fix this by inserting a dummy symbol in the and then including that symbol in the synthetic object file from .
It's of a different cause, but will have the same effect. I prefer to have it tracked in the same issue (but different work items). The problem is two fold. For the normal non-LTO linking, what you described is sufficient. The major issue is LTO. Because LTO works on IR but not binary, it doesn't know if a symbol is being used in . We need to pass a list of symbols to LLVM to ensure they are kept. Currently this includes all exported and symbols, but it also needs to include all symbols referenced by .
Do you have an example demonstrating the LTO related issue? Optimizations during LTO cannot remove symbols from yet, since as you describe the references to those symbols from the inline assembly are still opaque to it. For example, the internalize pass in LLVM comments:
That comment suggests that even with internal visibility, LLVM isn't supposed to delete a symbol that is used by . So perhaps the issue really is in the MergeFunctions pass which should be fixed to preserve a symbol alias if that symbol is used by .
Here's my attempt at a fix in LLVM, but I'm not too happy about the code. It could use some review by someone more familiar with LLVM.

Discussion:
<ins> // build-pass // compile-flags: -O // min-llvm-version: 14.0.5 // needs-asm-support // only-x86_64 // only-linux // regression test for #96797 #![feature(asm_sym)] use std::arch::global_asm; #[no_mangle] fn my_func() {} global_asm!("call_foobar: jmp {}", sym foobar); fn foobar() {} fn main() { extern "Rust" { fn call_foobar(); } unsafe { call_foobar() }; } </ins>