diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 3c2c6964a3d79..6e637cb883b23 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -6,7 +6,6 @@ use libc::{c_char, c_uint}; use super::ffi::{BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::Bool; -#[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme pub(crate) fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 3b0187b9d37b1..0694015b61b10 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1774,7 +1774,6 @@ unsafe extern "C" { ) -> &'ll Metadata; } -#[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { pub(crate) fn LLVMRustInstallErrorHandlers(); pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 22e262546c3a7..9f796dfb7f21c 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -66,8 +66,6 @@ codegen_ssa_extract_bundled_libs_write_file = failed to write file '{$rlib}': {$ codegen_ssa_failed_to_get_layout = failed to get layout for {$ty}: {$err} -codegen_ssa_failed_to_write = failed to write {$path}: {$error} - codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` codegen_ssa_forbidden_target_feature_attr = diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index a8d917f0fdb58..1ba9a8f7af4cf 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -25,7 +25,6 @@ use rustc_middle::bug; use rustc_middle::lint::lint_level; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; -use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{ self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames, OutputType, PrintKind, SplitDwarfKind, Strip, @@ -2054,84 +2053,6 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor } } -/// Add a synthetic object file that contains reference to all symbols that we want to expose to -/// the linker. -/// -/// Background: we implement rlibs as static library (archives). Linkers treat archives -/// differently from object files: all object files participate in linking, while archives will -/// only participate in linking if they can satisfy at least one undefined reference (version -/// scripts doesn't count). This causes `#[no_mangle]` or `#[used]` items to be ignored by the -/// linker, and since they never participate in the linking, using `KEEP` in the linker scripts -/// can't keep them either. This causes #47384. -/// -/// To keep them around, we could use `--whole-archive` and equivalents to force rlib to -/// participate in linking like object files, but this proves to be expensive (#93791). Therefore -/// we instead just introduce an undefined reference to them. This could be done by `-u` command -/// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only -/// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections` -/// from removing them, and this is especially problematic for embedded programming where every -/// byte counts. -/// -/// This method creates a synthetic object file, which contains undefined references to all symbols -/// that are necessary for the linking. They are only present in symbol table but not actually -/// used in any sections, so the linker will therefore pick relevant rlibs for linking, but -/// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections. -/// -/// There's a few internal crates in the standard library (aka libcore and -/// libstd) which actually have a circular dependence upon one another. This -/// currently arises through "weak lang items" where libcore requires things -/// like `rust_begin_unwind` but libstd ends up defining it. To get this -/// circular dependence to work correctly we declare some of these things -/// in this synthetic object. -fn add_linked_symbol_object( - cmd: &mut dyn Linker, - sess: &Session, - tmpdir: &Path, - symbols: &[(String, SymbolExportKind)], -) { - if symbols.is_empty() { - return; - } - - let Some(mut file) = super::metadata::create_object_file(sess) else { - return; - }; - - if file.format() == object::BinaryFormat::Coff { - // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections, - // so add an empty section. - file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text); - - // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the - // default mangler in `object` crate. - file.set_mangling(object::write::Mangling::None); - } - - for (sym, kind) in symbols.iter() { - file.add_symbol(object::write::Symbol { - name: sym.clone().into(), - value: 0, - size: 0, - kind: match kind { - SymbolExportKind::Text => object::SymbolKind::Text, - SymbolExportKind::Data => object::SymbolKind::Data, - SymbolExportKind::Tls => object::SymbolKind::Tls, - }, - scope: object::SymbolScope::Unknown, - weak: false, - section: object::write::SymbolSection::Undefined, - flags: object::SymbolFlags::None, - }); - } - - let path = tmpdir.join("symbols.o"); - let result = std::fs::write(&path, file.write().unwrap()); - if let Err(error) = result { - sess.dcx().emit_fatal(errors::FailedToWrite { path, error }); - } - cmd.add_object(&path); -} - /// Add object files containing code from the current crate. fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { @@ -2289,13 +2210,6 @@ fn linker_with_args( // Pre-link CRT objects. add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); - add_linked_symbol_object( - cmd, - sess, - tmpdir, - &codegen_results.crate_info.linked_symbols[&crate_type], - ); - // Sanitizer libraries. add_sanitizer_libraries(sess, flavor, crate_type, cmd); @@ -2684,7 +2598,7 @@ fn add_native_libs_from_crate( NativeLibKind::Static { bundle, whole_archive } => { if link_static { let bundle = bundle.unwrap_or(true); - let whole_archive = whole_archive == Some(true); + let whole_archive = whole_archive.unwrap_or(link_output_kind.is_dylib()); if bundle && cnum != LOCAL_CRATE { if let Some(filename) = lib.filename { // If rlib contains native libs as archives, they are unpacked to tmpdir. @@ -2706,7 +2620,7 @@ fn add_native_libs_from_crate( // link kind is unspecified. if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs { if link_static { - cmd.link_staticlib_by_name(name, verbatim, false); + cmd.link_staticlib_by_name(name, verbatim, link_output_kind.is_dylib()); } } else if link_dynamic { cmd.link_dylib_by_name(name, verbatim, true); @@ -2956,7 +2870,7 @@ fn add_static_crate( let cratepath = &src.rlib.as_ref().unwrap().0; let mut link_upstream = - |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false); + |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), true); if !are_upstream_rust_objects_already_included(sess) || ignored_for_lto(sess, &codegen_results.crate_info, cnum) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 8900405c1b8f8..b14693fa71862 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -12,7 +12,7 @@ use rustc_metadata::{ use rustc_middle::bug; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols; -use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; @@ -1808,32 +1808,6 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { vec![proc_macro_decls_name, metadata_symbol_name] } -pub(crate) fn linked_symbols( - tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { - match crate_type { - CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (), - CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => { - return Vec::new(); - } - } - - let mut symbols = Vec::new(); - - let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); - for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { - if info.level.is_below_threshold(export_threshold) || info.used { - symbols.push(( - symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), - info.kind, - )); - } - }); - - symbols -} - /// Much simplified and explicit CLI for the NVPTX linker. The linker operates /// with bitcode and uses LLVM backend to generate a PTX assembly. struct PtxLinker<'a> { diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 459f4329d6e92..194ceb53df373 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -621,60 +621,6 @@ fn calling_convention_for_symbol<'tcx>( .unwrap_or((Conv::Rust, &[])) } -/// This is the symbol name of the given instance as seen by the linker. -/// -/// On 32-bit Windows symbols are decorated according to their calling conventions. -pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>( - tcx: TyCtxt<'tcx>, - symbol: ExportedSymbol<'tcx>, - instantiating_crate: CrateNum, -) -> String { - let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate); - - // thread local will not be a function call, - // so it is safe to return before windows symbol decoration check. - if let Some(name) = maybe_emutls_symbol_name(tcx, symbol, &undecorated) { - return name; - } - - let target = &tcx.sess.target; - if !target.is_like_windows { - // Mach-O has a global "_" suffix and `object` crate will handle it. - // ELF does not have any symbol decorations. - return undecorated; - } - - let prefix = match &target.arch[..] { - "x86" => Some('_'), - "x86_64" => None, - "arm64ec" => Some('#'), - // Only x86/64 use symbol decorations. - _ => return undecorated, - }; - - let (conv, args) = calling_convention_for_symbol(tcx, symbol); - - // Decorate symbols with prefixes, suffixes and total number of bytes of arguments. - // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170 - let (prefix, suffix) = match conv { - Conv::X86Fastcall => ("@", "@"), - Conv::X86Stdcall => ("_", "@"), - Conv::X86VectorCall => ("", "@@"), - _ => { - if let Some(prefix) = prefix { - undecorated.insert(0, prefix); - } - return undecorated; - } - }; - - let args_in_bytes: u64 = args - .iter() - .map(|abi| abi.layout.size.bytes().next_multiple_of(target.pointer_width as u64 / 8)) - .sum(); - format!("{prefix}{undecorated}{suffix}{args_in_bytes}") -} - pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>( tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 40238f4b4915a..8de59a5df3d03 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,8 +5,8 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; -use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::par_map; use rustc_data_structures::unord::UnordMap; @@ -16,8 +16,7 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; -use rustc_middle::middle::exported_symbols::SymbolExportKind; -use rustc_middle::middle::{exported_symbols, lang_items}; +use rustc_middle::middle::exported_symbols; use rustc_middle::mir::BinOp; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; use rustc_middle::query::Providers; @@ -25,14 +24,13 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; -use rustc_span::{DUMMY_SP, Symbol, sym}; +use rustc_span::{DUMMY_SP, sym}; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; use {rustc_ast as ast, rustc_attr_parsing as attr}; use crate::assert_module_sources::CguReuse; -use crate::back::link::are_upstream_rust_objects_already_included; use crate::back::metadata::create_compressed_metadata_file; use crate::back::write::{ ComputedLtoType, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen, @@ -872,8 +870,6 @@ impl CrateInfo { .iter() .map(|&c| (c, crate::back::linker::exported_symbols(tcx, c))) .collect(); - let linked_symbols = - crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); let subsystem = @@ -918,7 +914,6 @@ impl CrateInfo { target_features: tcx.global_backend_features(()).clone(), crate_types, exported_symbols, - linked_symbols, local_crate_name, compiler_builtins, profiler_runtime: None, @@ -960,55 +955,6 @@ impl CrateInfo { // and we assume that they cannot define weak lang items. This is not currently enforced // by the compiler, but that's ok because all this stuff is unstable anyway. let target = &tcx.sess.target; - if !are_upstream_rust_objects_already_included(tcx.sess) { - let missing_weak_lang_items: FxIndexSet = info - .used_crates - .iter() - .flat_map(|&cnum| tcx.missing_lang_items(cnum)) - .filter(|l| l.is_weak()) - .filter_map(|&l| { - let name = l.link_name()?; - lang_items::required(tcx, l).then_some(name) - }) - .collect(); - let prefix = match (target.is_like_windows, target.arch.as_ref()) { - (true, "x86") => "_", - (true, "arm64ec") => "#", - _ => "", - }; - - // This loop only adds new items to values of the hash map, so the order in which we - // iterate over the values is not important. - #[allow(rustc::potential_query_instability)] - info.linked_symbols - .iter_mut() - .filter(|(crate_type, _)| { - !matches!(crate_type, CrateType::Rlib | CrateType::Staticlib) - }) - .for_each(|(_, linked_symbols)| { - let mut symbols = missing_weak_lang_items - .iter() - .map(|item| (format!("{prefix}{item}"), SymbolExportKind::Text)) - .collect::>(); - symbols.sort_unstable_by(|a, b| a.0.cmp(&b.0)); - linked_symbols.extend(symbols); - if tcx.allocator_kind(()).is_some() { - // At least one crate needs a global allocator. This crate may be placed - // after the crate that defines it in the linker order, in which case some - // linkers return an error. By adding the global allocator shim methods to - // the linked_symbols list, linking the generated symbols.o will ensure that - // circular dependencies involving the global allocator don't lead to linker - // errors. - linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| { - ( - format!("{prefix}{}", global_fn_name(method.name).as_str()), - SymbolExportKind::Text, - ) - })); - } - }); - } - let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => { // These are crate types for which we invoke the linker and can embed diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 3ddbe4aeeec5d..b692c8beb5874 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -586,13 +586,6 @@ pub(crate) struct LinkScriptWriteFailure { pub error: Error, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_failed_to_write)] -pub(crate) struct FailedToWrite { - pub path: PathBuf, - pub error: Error, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_unable_to_write_debugger_visualizer)] pub(crate) struct UnableToWriteDebuggerVisualizer { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9d2ac219d592c..6a49682de027c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -36,7 +36,6 @@ use rustc_middle::dep_graph::WorkProduct; use rustc_middle::lint::LintLevelSource; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; -use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; @@ -193,7 +192,6 @@ pub struct CrateInfo { pub target_features: Vec, pub crate_types: Vec, pub exported_symbols: UnordMap>, - pub linked_symbols: FxIndexMap>, pub local_crate_name: Symbol, pub compiler_builtins: Option, pub profiler_runtime: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 65736770efb8c..fe262109de1f7 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1235,6 +1235,17 @@ impl LinkOutputKind { | LinkOutputKind::WasiReactorExe => true, } } + + pub fn is_dylib(self) -> bool { + match self { + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => true, + LinkOutputKind::StaticNoPicExe + | LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::WasiReactorExe + | LinkOutputKind::StaticPicExe => false, + } + } } impl fmt::Display for LinkOutputKind { diff --git a/tests/run-make/include-all-symbols-linking/lib.rs b/tests/run-make/include-all-symbols-linking/lib.rs index 99508bcdaf314..73186ee99e3d9 100644 --- a/tests/run-make/include-all-symbols-linking/lib.rs +++ b/tests/run-make/include-all-symbols-linking/lib.rs @@ -1,5 +1,6 @@ mod foo { - #[link_section = ".rodata.STATIC"] + #[cfg_attr(target_os = "linux", link_section = ".rodata.STATIC")] + #[cfg_attr(target_vendor = "apple", link_section = "__DATA,STATIC")] #[used] static STATIC: [u32; 10] = [1; 10]; } diff --git a/tests/run-make/include-all-symbols-linking/rmake.rs b/tests/run-make/include-all-symbols-linking/rmake.rs index 77fd71ab20d21..bab510fb5be3c 100644 --- a/tests/run-make/include-all-symbols-linking/rmake.rs +++ b/tests/run-make/include-all-symbols-linking/rmake.rs @@ -7,15 +7,20 @@ // See https://github.com/rust-lang/rust/pull/95604 // See https://github.com/rust-lang/rust/issues/47384 -//@ only-linux -// Reason: differences in object file formats on OSX and Windows -// causes errors in the llvm_objdump step +//@ ignore-wasm differences in object file formats causes errors in the llvm_objdump step. +//@ ignore-windows differences in object file formats causes errors in the llvm_objdump step. -use run_make_support::{dynamic_lib_name, llvm_objdump, llvm_readobj, rustc}; +use run_make_support::{dynamic_lib_name, llvm_objdump, llvm_readobj, rustc, target}; fn main() { rustc().crate_type("lib").input("lib.rs").run(); - rustc().crate_type("cdylib").link_args("-Tlinker.ld").input("main.rs").run(); + let mut main = rustc(); + main.crate_type("cdylib"); + if target().contains("linux") { + main.link_args("-Tlinker.ld"); + } + main.input("main.rs").run(); + // Ensure `#[used]` and `KEEP`-ed section is there llvm_objdump() .arg("--full-contents") diff --git a/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs b/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs new file mode 100644 index 0000000000000..6f9d7cc59e950 --- /dev/null +++ b/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs @@ -0,0 +1,36 @@ +//! Add a constructor that runs pre-main, similar to what the `ctor` crate does. +//! +//! #[ctor] +//! fn constructor() { +//! printf(c"constructor\n"); +//! } + +//@ edition:2021 +//@ no-prefer-dynamic explicitly test with crates that are built as an archive +#![crate_type = "rlib"] + +#[cfg_attr( + any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", + target_os = "haiku" + ), + link_section = ".init_array" +)] +#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func,mod_init_funcs")] +#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] +#[used] +static CONSTRUCTOR: extern "C" fn() = constructor; + +#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")] +extern "C" fn constructor() { + extern "C" { + fn printf(format: *const std::ffi::c_char, ...) -> std::ffi::c_int; + } + unsafe { printf(c"constructor\n".as_ptr()) }; +} diff --git a/tests/ui/attributes/used_with_archive.rs b/tests/ui/attributes/used_with_archive.rs new file mode 100644 index 0000000000000..0a670a855d319 --- /dev/null +++ b/tests/ui/attributes/used_with_archive.rs @@ -0,0 +1,20 @@ +//! Ensure that `#[used]` in archives are correctly registered. +//! +//! Regression test for https://github.com/rust-lang/rust/issues/133491. + +//@ edition:2021 +//@ run-pass +//@ check-run-results +//@ aux-build: used_pre_main_constructor.rs + +//@ ignore-wasm ctor doesn't work on WASM + +// Make sure `rustc` links the archive, but intentionally do not import/use any items. +extern crate used_pre_main_constructor as _; + +fn main() { + extern "C" { + fn printf(format: *const std::ffi::c_char, ...) -> std::ffi::c_int; + } + unsafe { printf(c"main\n".as_ptr()) }; +} diff --git a/tests/ui/attributes/used_with_archive.run.stdout b/tests/ui/attributes/used_with_archive.run.stdout new file mode 100644 index 0000000000000..212372b3e5795 --- /dev/null +++ b/tests/ui/attributes/used_with_archive.run.stdout @@ -0,0 +1,2 @@ +constructor +main