diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index c971a7df3..2ad72116a 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -332,6 +332,16 @@ fn main() { naked_test(); } + // Test global_asm sym operand - calls sym_target() via assembly + #[cfg(all( + not(jit), + any(target_arch = "x86_64", target_arch = "aarch64"), + any(target_os = "linux", target_os = "macos") + ))] + unsafe { + assert_eq!(global_asm_sym_test(), 42); + } + // Both statics have a reference that points to the same anonymous allocation. static REF1: &u8 = &42; static REF2: &u8 = REF1; @@ -361,6 +371,15 @@ unsafe extern "C" { fn global_asm_test(); } +#[cfg(all( + not(jit), + any(target_arch = "x86_64", target_arch = "aarch64"), + any(target_os = "linux", target_os = "macos") +))] +unsafe extern "C" { + fn global_asm_sym_test() -> u64; +} + #[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] global_asm! { " @@ -381,6 +400,57 @@ global_asm! { " } +// Test global_asm sym operands - the function referenced via sym may be private +// to the codegen unit, so we create a wrapper function. +#[cfg(all( + not(jit), + any(target_arch = "x86_64", target_arch = "aarch64"), + any(target_os = "linux", target_os = "macos") +))] +fn sym_target() -> u64 { + 42 +} + +#[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] +global_asm! { + " + .global global_asm_sym_test + global_asm_sym_test: + jmp {sym_fn} + ", + sym_fn = sym sym_target, +} + +#[cfg(all(not(jit), target_arch = "x86_64", target_os = "macos"))] +global_asm! { + " + .global _global_asm_sym_test + _global_asm_sym_test: + jmp {sym_fn} + ", + sym_fn = sym sym_target, +} + +#[cfg(all(not(jit), target_arch = "aarch64", target_os = "linux"))] +global_asm! { + " + .global global_asm_sym_test + global_asm_sym_test: + b {sym_fn} + ", + sym_fn = sym sym_target, +} + +#[cfg(all(not(jit), target_arch = "aarch64", target_os = "macos"))] +global_asm! { + " + .global _global_asm_sym_test + _global_asm_sym_test: + b {sym_fn} + ", + sym_fn = sym sym_target, +} + #[cfg(all(not(jit), target_arch = "x86_64"))] #[unsafe(naked)] extern "C" fn naked_test() { diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 760e23f21..59d3f2874 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -518,6 +518,8 @@ fn codegen_cgu_content( let mut debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name.as_str()); let mut global_asm = String::new(); + let mut global_asm_sym_index = 0u32; + let target_config = module.target_config(); let mut type_dbg = TypeDebugContext::default(); super::predefine_mono_items(tcx, module, &mono_items); let mut codegened_functions = vec![]; @@ -527,7 +529,13 @@ fn codegen_cgu_content( let flags = tcx.codegen_instance_attrs(instance.def).flags; if flags.contains(CodegenFnAttrFlags::NAKED) { rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( - &mut GlobalAsmContext { tcx, global_asm: &mut global_asm }, + &mut GlobalAsmContext { + tcx, + global_asm: &mut global_asm, + module, + target_config, + global_asm_sym_index: &mut global_asm_sym_index, + }, instance, MonoItemData { linkage: RLinkage::External, @@ -560,7 +568,13 @@ fn codegen_cgu_content( } MonoItem::GlobalAsm(item_id) => { rustc_codegen_ssa::base::codegen_global_asm( - &mut GlobalAsmContext { tcx, global_asm: &mut global_asm }, + &mut GlobalAsmContext { + tcx, + global_asm: &mut global_asm, + module, + target_config, + global_asm_sym_index: &mut global_asm_sym_index, + }, item_id, ); } diff --git a/src/global_asm.rs b/src/global_asm.rs index 8d8cdb14d..6868b4e2f 100644 --- a/src/global_asm.rs +++ b/src/global_asm.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use std::process::{Command, Stdio}; use std::sync::Arc; +use cranelift_codegen::isa::TargetFrontendConfig; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef}; use rustc_middle::ty::TyCtxt; @@ -15,11 +16,17 @@ use rustc_middle::ty::layout::{ use rustc_session::config::{OutputFilenames, OutputType}; use rustc_target::asm::InlineAsmArch; +use crate::abi::get_function_sig; +use crate::common::create_wrapper_function; use crate::prelude::*; pub(crate) struct GlobalAsmContext<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, pub global_asm: &'a mut String, + pub module: &'a mut dyn Module, + pub target_config: TargetFrontendConfig, + /// Counter for generating unique wrapper names + pub global_asm_sym_index: &'a mut u32, } impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { @@ -30,7 +37,16 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { options: InlineAsmOptions, _line_spans: &[Span], ) { - codegen_global_asm_inner(self.tcx, self.global_asm, template, operands, options); + codegen_global_asm_inner( + self.tcx, + self.global_asm, + self.module, + self.target_config, + self.global_asm_sym_index, + template, + operands, + options, + ); } fn mangled_name(&self, instance: Instance<'tcx>) -> String { @@ -89,11 +105,15 @@ impl<'tcx> HasTypingEnv<'tcx> for GlobalAsmContext<'_, 'tcx> { fn codegen_global_asm_inner<'tcx>( tcx: TyCtxt<'tcx>, global_asm: &mut String, + module: &mut dyn Module, + target_config: TargetFrontendConfig, + global_asm_sym_index: &mut u32, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, ) { let is_x86 = matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64); + let is_macho = tcx.sess.target.is_like_darwin; if is_x86 { if !options.contains(InlineAsmOptions::ATT_SYNTAX) { @@ -119,9 +139,22 @@ fn codegen_global_asm_inner<'tcx>( } let symbol = tcx.symbol_name(instance); - // FIXME handle the case where the function was made private to the - // current codegen unit - global_asm.push_str(symbol.name); + + // Pass a wrapper rather than the function itself as the function itself + // may not be exported from the main codegen unit and may thus be + // unreachable from the object file created by an external assembler. + let wrapper_name = + format!("__global_asm_sym_wrapper{}", *global_asm_sym_index); + *global_asm_sym_index += 1; + let sig = + get_function_sig(tcx, target_config.default_call_conv, instance); + create_wrapper_function(module, sig, &wrapper_name, symbol.name); + + // For Mach-O, symbols need an underscore prefix + if is_macho { + global_asm.push('_'); + } + global_asm.push_str(&wrapper_name); } GlobalAsmOperandRef::SymStatic { def_id } => { if cfg!(not(feature = "inline_asm_sym")) { @@ -133,6 +166,11 @@ fn codegen_global_asm_inner<'tcx>( let instance = Instance::mono(tcx, def_id); let symbol = tcx.symbol_name(instance); + // For statics, we reference them directly as they should be visible. + // Add Mach-O underscore prefix if needed. + if is_macho { + global_asm.push('_'); + } global_asm.push_str(symbol.name); } } diff --git a/src/inline_asm.rs b/src/inline_asm.rs index ac0da06cb..0bc3ca128 100644 --- a/src/inline_asm.rs +++ b/src/inline_asm.rs @@ -575,7 +575,13 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { CInlineAsmOperand::Const { ref value } => { generated_asm.push_str(value); } - CInlineAsmOperand::Symbol { ref symbol } => generated_asm.push_str(symbol), + CInlineAsmOperand::Symbol { ref symbol } => { + // For Mach-O, symbols need an underscore prefix + if binary_format == BinaryFormat::Macho { + generated_asm.push('_'); + } + generated_asm.push_str(symbol); + } } } }