From df828069ea98c621f4ebf92bfc573ebc0727817a Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 23 Aug 2026 13:58:31 -0400 Subject: [PATCH 01/10] Fix and support more linkages --- src/base.rs | 59 ++++++++++++----- src/mono_item.rs | 2 +- tests/c/import_linkage.c | 16 +++++ tests/c/weak_function_linkage.c | 49 ++++++++++++++ tests/run/import_linkage.rs | 77 ++++++++++++++++++++++ tests/run/weak_function_linkage.rs | 100 +++++++++++++++++++++++++++++ 6 files changed, 285 insertions(+), 18 deletions(-) create mode 100644 tests/c/import_linkage.c create mode 100644 tests/c/weak_function_linkage.c create mode 100644 tests/run/import_linkage.rs create mode 100644 tests/run/weak_function_linkage.rs diff --git a/src/base.rs b/src/base.rs index 9c06c7090c8..a7ee26b4002 100644 --- a/src/base.rs +++ b/src/base.rs @@ -39,32 +39,57 @@ pub fn symbol_visibility_to_gcc(visibility: SymbolVisibility) -> gccjit::Visibil } } +/// The kind of a global declared with an explicit `#[linkage]`. +/// +/// This is only reached for imports (`extern { #[linkage = "..."] static X: *const T; }`), where +/// every flavour but `internal` is an undefined reference. `extern_weak` additionally gets +/// `VarAttribute::Weak` from the caller, so that an unresolved symbol reads as null. pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { match linkage { - Linkage::External => GlobalKind::Imported, - Linkage::AvailableExternally => GlobalKind::Imported, - Linkage::LinkOnceAny => unimplemented!(), - Linkage::LinkOnceODR => unimplemented!(), - Linkage::WeakAny => unimplemented!(), - Linkage::WeakODR => unimplemented!(), Linkage::Internal => GlobalKind::Internal, - Linkage::ExternalWeak => GlobalKind::Imported, // FIXME(antoyo): should be weak linkage. - Linkage::Common => unimplemented!(), + Linkage::External + | Linkage::AvailableExternally + | Linkage::LinkOnceAny + | Linkage::LinkOnceODR + | Linkage::WeakAny + | Linkage::WeakODR + | Linkage::ExternalWeak + | Linkage::Common => GlobalKind::Imported, } } +/// The type of a function *definition* with an explicit `#[linkage]`. +/// +/// The flavours that another object file is allowed to override also need +/// `linkage_needs_weak_attribute` from the caller: `FunctionType` alone cannot express weakness. pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { match linkage { Linkage::External => FunctionType::Exported, - // FIXME(antoyo): set the attribute externally_visible. - Linkage::AvailableExternally => FunctionType::Extern, - Linkage::LinkOnceAny => unimplemented!(), - Linkage::LinkOnceODR => unimplemented!(), - Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce. - Linkage::WeakODR => unimplemented!(), - Linkage::Internal => FunctionType::Internal, - Linkage::ExternalWeak => unimplemented!(), - Linkage::Common => unimplemented!(), + // libgccjit cannot emit a definition that the linker discards in favour of the one in + // another object file, so emit a private copy of it instead. + Linkage::AvailableExternally | Linkage::Internal => FunctionType::Internal, + // libgccjit exposes no comdat, so `weak` stands in for every overridable flavour. + Linkage::LinkOnceAny + | Linkage::LinkOnceODR + | Linkage::WeakAny + | Linkage::WeakODR + | Linkage::ExternalWeak + | Linkage::Common => FunctionType::Exported, + } +} + +/// Whether a definition with this linkage must carry the `weak` attribute, so that a strong +/// definition in another object file wins over it instead of clashing with it. +#[cfg(feature = "master")] +pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { + match linkage { + Linkage::LinkOnceAny + | Linkage::LinkOnceODR + | Linkage::WeakAny + | Linkage::WeakODR + | Linkage::ExternalWeak + | Linkage::Common => true, + Linkage::External | Linkage::AvailableExternally | Linkage::Internal => false, } } diff --git a/src/mono_item.rs b/src/mono_item.rs index 521c86e6274..371f3fd4996 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -172,7 +172,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { attributes::from_fn_attrs(self, fn_decl, instance, Some(fn_abi)); #[cfg(feature = "master")] - if linkage == Linkage::WeakAny { + if base::linkage_needs_weak_attribute(linkage) { fn_decl.add_attribute(FnAttribute::Weak); } diff --git a/tests/c/import_linkage.c b/tests/c/import_linkage.c new file mode 100644 index 00000000000..d725b86c6c1 --- /dev/null +++ b/tests/c/import_linkage.c @@ -0,0 +1,16 @@ +/* The symbols that `tests/run/import_linkage.rs` imports with an explicit `#[linkage]`. + * + * Such an import is a pointer whose value is the address of the symbol, so what the Rust side + * reads back is `&value_*`, not the pointer stored in it. The distinct values make a mix-up + * visible. */ + +#include + +int32_t external_value = 1; +int32_t available_externally_value = 2; +int32_t linkonce_value = 3; +int32_t linkonce_odr_value = 4; +int32_t weak_value = 5; +int32_t weak_odr_value = 6; +int32_t common_value = 7; +int32_t extern_weak_value = 8; diff --git a/tests/c/weak_function_linkage.c b/tests/c/weak_function_linkage.c new file mode 100644 index 00000000000..72ea483fd86 --- /dev/null +++ b/tests/c/weak_function_linkage.c @@ -0,0 +1,49 @@ +/* Strong definitions of the functions that `tests/run/weak_function_linkage.rs` also defines, but + * weakly. The linker has to keep these and drop the Rust ones. + * + * A backend that emits the Rust definitions as ordinary global symbols does not merely pick the + * wrong one: the link fails outright with a duplicate definition. */ + +#include + +int32_t weak_function(void) +{ + return 1; +} + +int32_t weak_odr_function(void) +{ + return 2; +} + +int32_t linkonce_function(void) +{ + return 3; +} + +int32_t linkonce_odr_function(void) +{ + return 4; +} + +int32_t common_function(void) +{ + return 5; +} + +/* Called from Rust, so that the calls also go through a caller that GCC compiled: a cg_gcc caller + * could inline the weak body it can see instead of calling the symbol. */ +int32_t c_call_all(void) +{ + if (weak_function() != 1) + return 11; + if (weak_odr_function() != 2) + return 12; + if (linkonce_function() != 3) + return 13; + if (linkonce_odr_function() != 4) + return 14; + if (common_function() != 5) + return 15; + return 0; +} diff --git a/tests/run/import_linkage.rs b/tests/run/import_linkage.rs new file mode 100644 index 00000000000..0b044529b9b --- /dev/null +++ b/tests/run/import_linkage.rs @@ -0,0 +1,77 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks the `#[linkage]` flavours an `extern` static can be imported with, against the symbols +// `tests/c/import_linkage.c` defines. `linkonce`, `linkonce_odr`, `weak`, `weak_odr` and `common` +// used to reach an `unimplemented!()` in `global_linkage_to_gcc`. +// +// The value of such an import is the address of the symbol rather than its contents, which is why +// the types are pointers: an `extern_weak` import of a symbol nobody defines reads as null instead +// of failing the link. + +#![feature(linkage, no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +extern "C" { + #[linkage = "external"] + static external_value: *const i32; + #[linkage = "available_externally"] + static available_externally_value: *const i32; + #[linkage = "linkonce"] + static linkonce_value: *const i32; + #[linkage = "linkonce_odr"] + static linkonce_odr_value: *const i32; + #[linkage = "weak"] + static weak_value: *const i32; + #[linkage = "weak_odr"] + static weak_odr_value: *const i32; + #[linkage = "common"] + static common_value: *const i32; + #[linkage = "extern_weak"] + static extern_weak_value: *const i32; + + // Nothing defines this one, so it stays null instead of breaking the link. + #[linkage = "extern_weak"] + static undefined_value: *const i32; +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + unsafe { + if *external_value != 1 { + return 1; + } + if *available_externally_value != 2 { + return 2; + } + if *linkonce_value != 3 { + return 3; + } + if *linkonce_odr_value != 4 { + return 4; + } + if *weak_value != 5 { + return 5; + } + if *weak_odr_value != 6 { + return 6; + } + if *common_value != 7 { + return 7; + } + if *extern_weak_value != 8 { + return 8; + } + if undefined_value as usize != 0 { + return 9; + } + } + 0 +} diff --git a/tests/run/weak_function_linkage.rs b/tests/run/weak_function_linkage.rs new file mode 100644 index 00000000000..68052136369 --- /dev/null +++ b/tests/run/weak_function_linkage.rs @@ -0,0 +1,100 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks that the `#[linkage]` flavours another object file is allowed to override are emitted as +// weak symbols, by linking against `tests/c/weak_function_linkage.c`, which defines the same +// symbols strongly. +// +// `weak` used to be emitted as an ordinary global symbol, which the C definitions clash with, and +// `weak_odr`, `linkonce`, `linkonce_odr` and `common` reached an `unimplemented!()` in +// `linkage_to_gcc`. `available_externally` reached libgccjit, which rejects a body on an imported +// function. + +#![feature(linkage, no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[linkage = "weak"] +#[no_mangle] +extern "C" fn weak_function() -> i32 { + 0 +} + +#[linkage = "weak_odr"] +#[no_mangle] +extern "C" fn weak_odr_function() -> i32 { + 0 +} + +#[linkage = "linkonce"] +#[no_mangle] +extern "C" fn linkonce_function() -> i32 { + 0 +} + +#[linkage = "linkonce_odr"] +#[no_mangle] +extern "C" fn linkonce_odr_function() -> i32 { + 0 +} + +#[linkage = "common"] +#[no_mangle] +extern "C" fn common_function() -> i32 { + 0 +} + +// Not overridden by the C side: the definition here is the one that runs. +#[linkage = "weak"] +#[no_mangle] +extern "C" fn only_weak_function() -> i32 { + 6 +} + +// Emitted as a private copy of a definition that lives elsewhere, so it must still be callable. +#[linkage = "available_externally"] +#[no_mangle] +extern "C" fn available_externally_function() -> i32 { + 7 +} + +extern "C" { + fn c_call_all() -> i32; +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + let result = unsafe { c_call_all() }; + if result != 0 { + return result; + } + + if weak_function() != 1 { + return 1; + } + if weak_odr_function() != 2 { + return 2; + } + if linkonce_function() != 3 { + return 3; + } + if linkonce_odr_function() != 4 { + return 4; + } + if common_function() != 5 { + return 5; + } + if only_weak_function() != 6 { + return 6; + } + if available_externally_function() != 7 { + return 7; + } + 0 +} From 642aaa8c9de2adb5099d90ce268b483e7bccbc55 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 23 Aug 2026 14:42:00 -0400 Subject: [PATCH 02/10] Implement linkage in predefine_static and fix internal linkage on extern statics --- src/base.rs | 20 +++++----- src/consts.rs | 17 +++++--- src/declare.rs | 6 ++- src/mono_item.rs | 18 +++++++-- tests/c/import_linkage.c | 1 + tests/c/static_linkage.c | 33 ++++++++++++++++ tests/run/import_linkage.rs | 9 ++++- tests/run/static_linkage.rs | 77 +++++++++++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 22 deletions(-) create mode 100644 tests/c/static_linkage.c create mode 100644 tests/run/static_linkage.rs diff --git a/src/base.rs b/src/base.rs index a7ee26b4002..46f864bed98 100644 --- a/src/base.rs +++ b/src/base.rs @@ -39,22 +39,24 @@ pub fn symbol_visibility_to_gcc(visibility: SymbolVisibility) -> gccjit::Visibil } } -/// The kind of a global declared with an explicit `#[linkage]`. +/// The kind of a global *definition* with an explicit `#[linkage]`. /// -/// This is only reached for imports (`extern { #[linkage = "..."] static X: *const T; }`), where -/// every flavour but `internal` is an undefined reference. `extern_weak` additionally gets -/// `VarAttribute::Weak` from the caller, so that an unresolved symbol reads as null. +/// The flavours that another object file is allowed to override also need +/// `linkage_needs_weak_attribute` from the caller: `GlobalKind` alone cannot express weakness. pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { match linkage { - Linkage::Internal => GlobalKind::Internal, - Linkage::External - | Linkage::AvailableExternally - | Linkage::LinkOnceAny + Linkage::External => GlobalKind::Exported, + // libgccjit cannot emit a definition that the linker discards in favour of the one in + // another object file, so emit a private copy of it instead. + Linkage::AvailableExternally | Linkage::Internal => GlobalKind::Internal, + // libgccjit exposes neither comdat nor common storage, so `weak` stands in for every + // overridable flavour. + Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR | Linkage::ExternalWeak - | Linkage::Common => GlobalKind::Imported, + | Linkage::Common => GlobalKind::Exported, } } diff --git a/src/consts.rs b/src/consts.rs index b1e06f88a23..061c09abcf1 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -21,7 +21,6 @@ use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; -use crate::base; use crate::common::bytes_type_in_context; use crate::context::CodegenCx; use crate::type_::struct_attributes; @@ -469,10 +468,10 @@ fn check_and_apply_linkage<'gcc, 'tcx>( ) -> LValue<'gcc> { let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); if let Some(linkage) = attrs.import_linkage { - // Declare a symbol `foo` with the desired linkage. - let global1 = - cx.declare_global_with_linkage(sym, cx.type_i8(), base::global_linkage_to_gcc(linkage)); + // Whatever the flavour, an import is an undefined reference to a symbol defined elsewhere. + let global1 = cx.declare_global_with_linkage(sym, cx.type_i8(), GlobalKind::Imported); + // Only `extern_weak` lets the symbol stay unresolved, in which case it reads as null. if linkage == Linkage::ExternalWeak { #[cfg(feature = "master")] global1.add_attribute(VarAttribute::Weak); @@ -486,8 +485,14 @@ fn check_and_apply_linkage<'gcc, 'tcx>( // zero. let real_name = format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE)); - let global2 = cx.define_global(&real_name, gcc_type, is_tls, attrs.link_section); - // FIXME(antoyo): set linkage. + let global2 = cx.define_global( + &real_name, + gcc_type, + GlobalKind::Exported, + is_tls, + attrs.link_section, + ); + // FIXME(antoyo): set linkage: cg_llvm makes this helper global internal. let value = cx.const_ptrcast(global1.get_address(None), gcc_type); global2.global_set_initializer_rvalue(value); global2 diff --git a/src/declare.rs b/src/declare.rs index 9bf57fbf75b..32bb7c3aa34 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -14,6 +14,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { &self, name: &str, ty: Type<'gcc>, + global_kind: GlobalKind, is_tls: bool, link_section: Option, ) -> LValue<'gcc> { @@ -31,7 +32,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } global } else { - self.declare_global(name, ty, GlobalKind::Exported, is_tls, link_section) + self.declare_global(name, ty, global_kind, is_tls, link_section) } } @@ -141,10 +142,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { &self, name: &str, ty: Type<'gcc>, + global_kind: GlobalKind, is_tls: bool, link_section: Option, ) -> LValue<'gcc> { - self.get_or_insert_global(name, ty, is_tls, link_section) + self.get_or_insert_global(name, ty, global_kind, is_tls, link_section) } pub fn get_declared_value(&self, name: &str) -> Option> { diff --git a/src/mono_item.rs b/src/mono_item.rs index 371f3fd4996..47889e1847e 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -21,7 +21,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn predefine_static( &mut self, def_id: DefId, - _linkage: Linkage, + linkage: Linkage, visibility: Visibility, global_name: &str, ) { @@ -47,10 +47,20 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { }; let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); - let global = self.define_global(global_name, gcc_type, is_tls, attrs.link_section); + let global_kind = base::global_linkage_to_gcc(linkage); + let global = + self.define_global(global_name, gcc_type, global_kind, is_tls, attrs.link_section); #[cfg(feature = "master")] - global.add_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); - // FIXME(antoyo): set linkage. + { + // GCC warns that it ignores `visibility` on an internal global, and cg_gcc turns + // libgccjit warnings into errors. + if !matches!(global_kind, GlobalKind::Internal) { + global.add_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); + } + if base::linkage_needs_weak_attribute(linkage) { + global.add_attribute(VarAttribute::Weak); + } + } #[cfg(feature = "master")] self.add_static_aliases(gcc_type, global_name, attrs, &attrs.foreign_item_symbol_aliases); diff --git a/tests/c/import_linkage.c b/tests/c/import_linkage.c index d725b86c6c1..f2beb9603d0 100644 --- a/tests/c/import_linkage.c +++ b/tests/c/import_linkage.c @@ -14,3 +14,4 @@ int32_t weak_value = 5; int32_t weak_odr_value = 6; int32_t common_value = 7; int32_t extern_weak_value = 8; +int32_t internal_value = 9; diff --git a/tests/c/static_linkage.c b/tests/c/static_linkage.c new file mode 100644 index 00000000000..1a9b4ca5bd7 --- /dev/null +++ b/tests/c/static_linkage.c @@ -0,0 +1,33 @@ +/* Strong definitions of the statics that `tests/run/static_linkage.rs` also defines, but weakly. + * The linker has to keep these and drop the Rust ones; a backend that emits the Rust definitions + * as ordinary global symbols fails the link with a duplicate definition instead. + * + * `internal_static` is the opposite case: the Rust side keeps its own, and the two definitions + * coexist because the Rust one is local. */ + +#include + +int32_t weak_static = 1; +int32_t weak_odr_static = 2; +int32_t linkonce_static = 3; +int32_t linkonce_odr_static = 4; +int32_t common_static = 5; +int32_t internal_static = 200; + +/* Called from Rust, so that the reads also happen in a translation unit GCC compiled. */ +int32_t c_read_all(void) +{ + if (weak_static != 1) + return 11; + if (weak_odr_static != 2) + return 12; + if (linkonce_static != 3) + return 13; + if (linkonce_odr_static != 4) + return 14; + if (common_static != 5) + return 15; + if (internal_static != 200) + return 16; + return 0; +} diff --git a/tests/run/import_linkage.rs b/tests/run/import_linkage.rs index 0b044529b9b..c721309020e 100644 --- a/tests/run/import_linkage.rs +++ b/tests/run/import_linkage.rs @@ -36,6 +36,10 @@ extern "C" { static common_value: *const i32; #[linkage = "extern_weak"] static extern_weak_value: *const i32; + // An import is an undefined reference whatever the flavour says; this used to declare a + // private zeroed object of its own instead of reaching the definition in the C file. + #[linkage = "internal"] + static internal_value: *const i32; // Nothing defines this one, so it stays null instead of breaking the link. #[linkage = "extern_weak"] @@ -69,9 +73,12 @@ extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { if *extern_weak_value != 8 { return 8; } - if undefined_value as usize != 0 { + if *internal_value != 9 { return 9; } + if undefined_value as usize != 0 { + return 10; + } } 0 } diff --git a/tests/run/static_linkage.rs b/tests/run/static_linkage.rs new file mode 100644 index 00000000000..adc43ab9fe3 --- /dev/null +++ b/tests/run/static_linkage.rs @@ -0,0 +1,77 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks that `#[linkage]` on a static that this crate defines reaches the symbol, against +// `tests/c/static_linkage.c`, which defines the overridable ones strongly. +// +// `predefine_static` used to ignore its `linkage` argument outright, so every static came out as +// an ordinary global symbol: the overridable ones clashed with the C definitions at link time, and +// `internal` exported a symbol it should have kept private. + +#![feature(linkage, no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[linkage = "weak"] +#[no_mangle] +pub static weak_static: i32 = 0; + +#[linkage = "weak_odr"] +#[no_mangle] +pub static weak_odr_static: i32 = 0; + +#[linkage = "linkonce"] +#[no_mangle] +pub static linkonce_static: i32 = 0; + +#[linkage = "linkonce_odr"] +#[no_mangle] +pub static linkonce_odr_static: i32 = 0; + +#[linkage = "common"] +#[no_mangle] +pub static common_static: i32 = 0; + +// Private to this crate, so the C definition of the same name is a different object. +#[linkage = "internal"] +#[no_mangle] +pub static internal_static: i32 = 100; + +// Not overridden by the C side: the definition here is the one that survives. +#[linkage = "weak"] +#[no_mangle] +pub static only_weak_static: i32 = 6; + +// Emitted as a private copy of a definition that lives elsewhere, so it must still be readable. +#[linkage = "available_externally"] +#[no_mangle] +pub static available_externally_static: i32 = 7; + +extern "C" { + fn c_read_all() -> i32; +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + let result = unsafe { c_read_all() }; + if result != 0 { + return result; + } + + if internal_static != 100 { + return 1; + } + if only_weak_static != 6 { + return 2; + } + if available_externally_static != 7 { + return 3; + } + 0 +} From 9ad916e901d1b9e6e2c6d21dafcd8cd535e254ff Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 23 Aug 2026 16:56:27 -0400 Subject: [PATCH 03/10] Use internal linkage for check_and_apply_linkage --- src/consts.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index 061c09abcf1..956b79b0cac 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -488,11 +488,10 @@ fn check_and_apply_linkage<'gcc, 'tcx>( let global2 = cx.define_global( &real_name, gcc_type, - GlobalKind::Exported, + GlobalKind::Internal, is_tls, attrs.link_section, ); - // FIXME(antoyo): set linkage: cg_llvm makes this helper global internal. let value = cx.const_ptrcast(global1.get_address(None), gcc_type); global2.global_set_initializer_rvalue(value); global2 From b0feb7ff8193d017e1b05007a1de343c05e586c1 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 24 Aug 2026 18:10:25 -0400 Subject: [PATCH 04/10] Fix ICE that happened on a weak function marked inline --- src/attributes.rs | 13 +++++++++++++ src/mono_item.rs | 11 ++++++++++- tests/run/weak_function_linkage.rs | 13 +++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/attributes.rs b/src/attributes.rs index 95d12480efa..e4d44d790d3 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -15,6 +15,8 @@ use rustc_target::callconv::FnAbi; #[cfg(feature = "master")] use rustc_target::spec::Arch; +#[cfg(feature = "master")] +use crate::base; use crate::context::CodegenCx; use crate::gcc_util::to_gcc_features; @@ -116,6 +118,17 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } else { codegen_fn_attrs.inline }; + // GCC warns that `inline` and `weak` conflict, and cg_gcc turns libgccjit warnings into + // errors. The linkage is what has to survive: rustc lints `#[inline]` as ignored on a + // function with an explicit `#[linkage]` anyway. `inline(never)` does not conflict. + let inline = match inline { + InlineAttr::Always | InlineAttr::Hint | InlineAttr::Force { .. } + if codegen_fn_attrs.linkage.is_some_and(base::linkage_needs_weak_attribute) => + { + InlineAttr::None + } + inline => inline, + }; if let Some(attr) = inline_attr(cx, inline, instance) { if let FnAttribute::AlwaysInline = attr { func.add_attribute(FnAttribute::Inline); diff --git a/src/mono_item.rs b/src/mono_item.rs index 47889e1847e..cb133d9c233 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -55,7 +55,16 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // GCC warns that it ignores `visibility` on an internal global, and cg_gcc turns // libgccjit warnings into errors. if !matches!(global_kind, GlobalKind::Internal) { - global.add_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); + // If we're compiling the compiler-builtins crate, e.g., the equivalent of + // compiler-rt, then we want to implicitly compile everything with hidden + // visibility as we're going to link this object all over the place but + // don't want the symbols to get exported. + let visibility = if self.tcx.is_compiler_builtins(LOCAL_CRATE) { + gccjit::Visibility::Hidden + } else { + base::visibility_to_gcc(visibility) + }; + global.add_attribute(VarAttribute::Visibility(visibility)); } if base::linkage_needs_weak_attribute(linkage) { global.add_attribute(VarAttribute::Weak); diff --git a/tests/run/weak_function_linkage.rs b/tests/run/weak_function_linkage.rs index 68052136369..82e1c3d2681 100644 --- a/tests/run/weak_function_linkage.rs +++ b/tests/run/weak_function_linkage.rs @@ -64,6 +64,16 @@ extern "C" fn available_externally_function() -> i32 { 7 } +// GCC warns that `inline` and `weak` conflict, and cg_gcc turns libgccjit warnings into errors, so +// this used to fail to compile at all. The inline hint is what gives way: rustc lints it as ignored +// on a function with an explicit `#[linkage]` anyway, hence the `allow`. +#[linkage = "weak"] +#[inline] +#[allow(unused_attributes)] +extern "C" fn weak_inline_function() -> i32 { + 8 +} + extern "C" { fn c_call_all() -> i32; } @@ -96,5 +106,8 @@ extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { if available_externally_function() != 7 { return 7; } + if weak_inline_function() != 8 { + return 8; + } 0 } From 9f3034d809679a738621fc87088e7279d8a9cc33 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 25 Aug 2026 14:16:52 -0400 Subject: [PATCH 05/10] Cleanup --- tests/run/import_linkage.rs | 6 ++---- tests/run/static_linkage.rs | 6 +++--- tests/run/weak_function_linkage.rs | 5 ----- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/tests/run/import_linkage.rs b/tests/run/import_linkage.rs index c721309020e..bf83801d355 100644 --- a/tests/run/import_linkage.rs +++ b/tests/run/import_linkage.rs @@ -4,8 +4,7 @@ // status: 0 // Checks the `#[linkage]` flavours an `extern` static can be imported with, against the symbols -// `tests/c/import_linkage.c` defines. `linkonce`, `linkonce_odr`, `weak`, `weak_odr` and `common` -// used to reach an `unimplemented!()` in `global_linkage_to_gcc`. +// `tests/c/import_linkage.c` defines. // // The value of such an import is the address of the symbol rather than its contents, which is why // the types are pointers: an `extern_weak` import of a symbol nobody defines reads as null instead @@ -36,8 +35,7 @@ extern "C" { static common_value: *const i32; #[linkage = "extern_weak"] static extern_weak_value: *const i32; - // An import is an undefined reference whatever the flavour says; this used to declare a - // private zeroed object of its own instead of reaching the definition in the C file. + // An import is an undefined reference whatever the flavour says. #[linkage = "internal"] static internal_value: *const i32; diff --git a/tests/run/static_linkage.rs b/tests/run/static_linkage.rs index adc43ab9fe3..1a9b672de36 100644 --- a/tests/run/static_linkage.rs +++ b/tests/run/static_linkage.rs @@ -6,9 +6,9 @@ // Checks that `#[linkage]` on a static that this crate defines reaches the symbol, against // `tests/c/static_linkage.c`, which defines the overridable ones strongly. // -// `predefine_static` used to ignore its `linkage` argument outright, so every static came out as -// an ordinary global symbol: the overridable ones clashed with the C definitions at link time, and -// `internal` exported a symbol it should have kept private. +// If `predefine_static` were to ignore its `linkage` argument outright, every static would come out as +// an ordinary global symbol: the overridable ones would clash with the C definitions at link time, and +// `internal` would export a symbol it should have kept private. #![feature(linkage, no_core)] #![no_std] diff --git a/tests/run/weak_function_linkage.rs b/tests/run/weak_function_linkage.rs index 82e1c3d2681..353b71a1e62 100644 --- a/tests/run/weak_function_linkage.rs +++ b/tests/run/weak_function_linkage.rs @@ -6,11 +6,6 @@ // Checks that the `#[linkage]` flavours another object file is allowed to override are emitted as // weak symbols, by linking against `tests/c/weak_function_linkage.c`, which defines the same // symbols strongly. -// -// `weak` used to be emitted as an ordinary global symbol, which the C definitions clash with, and -// `weak_odr`, `linkonce`, `linkonce_odr` and `common` reached an `unimplemented!()` in -// `linkage_to_gcc`. `available_externally` reached libgccjit, which rejects a body on an imported -// function. #![feature(linkage, no_core)] #![no_std] From 7a6930e3117c7ca4ba1e68589aecbb7d58e6f8df Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 25 Aug 2026 16:34:47 -0400 Subject: [PATCH 06/10] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1bbd3a99580..13bd0d0ffde 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ llvm build_system/target config.toml build -rustlantis \ No newline at end of file +rustlantis +stuff/ From ec5988dae1e54dea6315df8b2231ae9e5fe56829 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 25 Aug 2026 20:58:48 -0400 Subject: [PATCH 07/10] Improve tests --- tests/c/static_linkage.c | 4 ++++ tests/c/weak_function_linkage.c | 7 +++++++ tests/run/import_linkage.rs | 4 +++- tests/run/static_linkage.rs | 6 ++++-- tests/run/weak_function_linkage.rs | 11 ++++++++--- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/tests/c/static_linkage.c b/tests/c/static_linkage.c index 1a9b4ca5bd7..787e61f9cf1 100644 --- a/tests/c/static_linkage.c +++ b/tests/c/static_linkage.c @@ -14,6 +14,10 @@ int32_t linkonce_odr_static = 4; int32_t common_static = 5; int32_t internal_static = 200; +/* `available_externally` promises the real definition lives elsewhere: a backend may read this one + * or emit an equivalent copy of the Rust initializer, so the two have to hold the same value. */ +int32_t available_externally_static = 7; + /* Called from Rust, so that the reads also happen in a translation unit GCC compiled. */ int32_t c_read_all(void) { diff --git a/tests/c/weak_function_linkage.c b/tests/c/weak_function_linkage.c index 72ea483fd86..98325003549 100644 --- a/tests/c/weak_function_linkage.c +++ b/tests/c/weak_function_linkage.c @@ -31,6 +31,13 @@ int32_t common_function(void) return 5; } +/* `available_externally` promises the real definition lives elsewhere: a backend may call this one + * or emit an equivalent copy of the Rust body, so the two have to return the same value. */ +int32_t available_externally_function(void) +{ + return 7; +} + /* Called from Rust, so that the calls also go through a caller that GCC compiled: a cg_gcc caller * could inline the weak body it can see instead of calling the symbol. */ int32_t c_call_all(void) diff --git a/tests/run/import_linkage.rs b/tests/run/import_linkage.rs index bf83801d355..bf5cb9e5327 100644 --- a/tests/run/import_linkage.rs +++ b/tests/run/import_linkage.rs @@ -35,7 +35,9 @@ extern "C" { static common_value: *const i32; #[linkage = "extern_weak"] static extern_weak_value: *const i32; - // An import is an undefined reference whatever the flavour says. + // An import is an undefined reference whatever the flavour says. Upstream bug: rustc lowers + // this one to an internal declaration, which LLVM's verifier rejects ("Global is external, but + // doesn't have external or weak linkage!") and which crashes cg_llvm at -O3. #[linkage = "internal"] static internal_value: *const i32; diff --git a/tests/run/static_linkage.rs b/tests/run/static_linkage.rs index 1a9b672de36..7b911c064d7 100644 --- a/tests/run/static_linkage.rs +++ b/tests/run/static_linkage.rs @@ -34,9 +34,10 @@ pub static linkonce_static: i32 = 0; #[no_mangle] pub static linkonce_odr_static: i32 = 0; +// `common` is only valid on a mutable global: LLVM rejects a constant one. #[linkage = "common"] #[no_mangle] -pub static common_static: i32 = 0; +pub static mut common_static: i32 = 0; // Private to this crate, so the C definition of the same name is a different object. #[linkage = "internal"] @@ -48,7 +49,8 @@ pub static internal_static: i32 = 100; #[no_mangle] pub static only_weak_static: i32 = 6; -// Emitted as a private copy of a definition that lives elsewhere, so it must still be readable. +// The real definition is the one in the C file; a backend may read it or emit an equivalent copy of +// this initializer, so both spell the same value. #[linkage = "available_externally"] #[no_mangle] pub static available_externally_static: i32 = 7; diff --git a/tests/run/weak_function_linkage.rs b/tests/run/weak_function_linkage.rs index 353b71a1e62..349d3a3a485 100644 --- a/tests/run/weak_function_linkage.rs +++ b/tests/run/weak_function_linkage.rs @@ -21,10 +21,12 @@ extern "C" fn weak_function() -> i32 { 0 } +// `_odr` promises every definition of the symbol is equivalent, which lets a backend call this body +// instead of the one in the C file. They spell the same value for that reason. #[linkage = "weak_odr"] #[no_mangle] extern "C" fn weak_odr_function() -> i32 { - 0 + 2 } #[linkage = "linkonce"] @@ -36,9 +38,11 @@ extern "C" fn linkonce_function() -> i32 { #[linkage = "linkonce_odr"] #[no_mangle] extern "C" fn linkonce_odr_function() -> i32 { - 0 + 4 } +// Upstream bug: LLVM rejects `common` on a function ("Functions may not have common linkage"), and +// with its verifier off inlines this body over the strong C one at -O3, so cg_llvm fails here. #[linkage = "common"] #[no_mangle] extern "C" fn common_function() -> i32 { @@ -52,7 +56,8 @@ extern "C" fn only_weak_function() -> i32 { 6 } -// Emitted as a private copy of a definition that lives elsewhere, so it must still be callable. +// The real definition is the one in the C file; a backend may call it or emit an equivalent copy of +// this body, so both spell the same value. #[linkage = "available_externally"] #[no_mangle] extern "C" fn available_externally_function() -> i32 { From 64092c925ecb3787967f939caae0bc50e98dcce1 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 26 Aug 2026 11:57:13 -0400 Subject: [PATCH 08/10] Add support for the common attribute --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- src/base.rs | 21 ++++++++++++++++++--- src/consts.rs | 21 +++++++++++++++++++-- src/mono_item.rs | 4 ++-- tests/c/weak_function_linkage.c | 7 ------- tests/run/weak_function_linkage.rs | 12 ++---------- 7 files changed, 46 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44aeab75c29..6c3dc4b82f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "6.0.0" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb358d2563af5e32af92620915e6b05839ae60645343473735619441f45eb04" +checksum = "6d85b5754389edaad832ba320709a25086b3081a8c6c0fab2322965e5fb512b3" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2389fb01673e9cc63684d996a58079edccc5de89008274f3be59f1b16ac1f017" +checksum = "e081669728b490723537f9def7eb674b7c9acd8de0b92ad4f4abf5f5cc75ea4b" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 1aff8ed115e..abfa47a05bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ default = ["master"] [dependencies] object = { version = "0.37.0", default-features = false, features = ["std", "read"] } tempfile = "3.20" -gccjit = { version = "6.0.0", features = ["dlopen"] } +gccjit = { version = "6.1.0", features = ["dlopen"] } #gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] } # Local copy. diff --git a/src/base.rs b/src/base.rs index 46f864bed98..3346ff85074 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,6 +1,8 @@ use std::sync::Arc; use std::time::Instant; +#[cfg(feature = "master")] +use gccjit::VarAttribute; use gccjit::{CType, FunctionType, GlobalKind}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; @@ -42,15 +44,14 @@ pub fn symbol_visibility_to_gcc(visibility: SymbolVisibility) -> gccjit::Visibil /// The kind of a global *definition* with an explicit `#[linkage]`. /// /// The flavours that another object file is allowed to override also need -/// `linkage_needs_weak_attribute` from the caller: `GlobalKind` alone cannot express weakness. +/// `global_linkage_attribute` from the caller: `GlobalKind` alone cannot express weakness. pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { match linkage { Linkage::External => GlobalKind::Exported, // libgccjit cannot emit a definition that the linker discards in favour of the one in // another object file, so emit a private copy of it instead. Linkage::AvailableExternally | Linkage::Internal => GlobalKind::Internal, - // libgccjit exposes neither comdat nor common storage, so `weak` stands in for every - // overridable flavour. + // libgccjit exposes no comdat, so `weak` stands in for the linkonce flavours. Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny @@ -60,6 +61,16 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { } } +/// The attribute a global *definition* needs on top of its [`GlobalKind`] to get this linkage. +#[cfg(feature = "master")] +pub fn global_linkage_attribute<'gcc>(linkage: Linkage) -> Option> { + match linkage { + Linkage::Common => Some(VarAttribute::Common), + _ if linkage_needs_weak_attribute(linkage) => Some(VarAttribute::Weak), + _ => None, + } +} + /// The type of a function *definition* with an explicit `#[linkage]`. /// /// The flavours that another object file is allowed to override also need @@ -82,6 +93,10 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { /// Whether a definition with this linkage must carry the `weak` attribute, so that a strong /// definition in another object file wins over it instead of clashing with it. +/// +/// `common` is in here for functions only: GCC honours that attribute on a variable, but drops it +/// on a function, so a common function falls back to weak. Globals go through +/// `global_linkage_attribute` instead. #[cfg(feature = "master")] pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { match linkage { diff --git a/src/consts.rs b/src/consts.rs index 956b79b0cac..06e945a6a45 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -13,7 +13,8 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_log::tracing::trace; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - self, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, read_target_uint, + self, Allocation, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, + read_target_uint, }; use rustc_middle::mono::MonoItem; use rustc_middle::ty::layout::LayoutOf; @@ -112,7 +113,12 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); - global.global_set_initializer_rvalue(value); + // A common symbol is storage the linker allocates and zero-fills, so giving the definition + // an initializer — even an all-zero one — takes it back out of `.comm`. A non-zero one is + // kept: the symbol is then an ordinary definition, which is what GCC does with it too. + if attrs.linkage != Some(Linkage::Common) || !is_zero_initializer(alloc) { + global.global_set_initializer_rvalue(value); + } // As an optimization, all shared statics which do not have interior // mutability are placed into read-only memory. @@ -452,6 +458,17 @@ pub(crate) fn const_alloc_to_gcc_uncached<'gcc>( cx.const_struct(&llvals, true) } +/// Whether this allocation is all zeroes, and so needs no initializer to be spelled out. +fn is_zero_initializer(alloc: &Allocation) -> bool { + alloc.provenance().ptrs().is_empty() + // This `inspect` is okay: it is within the bounds of the allocation, there is no provenance + // to misread, and it does not affect interpreter execution. + && alloc + .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.size().bytes_usize()) + .iter() + .all(|&byte| byte == 0) +} + fn codegen_static_initializer<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId, diff --git a/src/mono_item.rs b/src/mono_item.rs index cb133d9c233..f0b8c8a9dcc 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -66,8 +66,8 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { }; global.add_attribute(VarAttribute::Visibility(visibility)); } - if base::linkage_needs_weak_attribute(linkage) { - global.add_attribute(VarAttribute::Weak); + if let Some(attribute) = base::global_linkage_attribute(linkage) { + global.add_attribute(attribute); } } diff --git a/tests/c/weak_function_linkage.c b/tests/c/weak_function_linkage.c index 98325003549..1d64f5365d1 100644 --- a/tests/c/weak_function_linkage.c +++ b/tests/c/weak_function_linkage.c @@ -26,11 +26,6 @@ int32_t linkonce_odr_function(void) return 4; } -int32_t common_function(void) -{ - return 5; -} - /* `available_externally` promises the real definition lives elsewhere: a backend may call this one * or emit an equivalent copy of the Rust body, so the two have to return the same value. */ int32_t available_externally_function(void) @@ -50,7 +45,5 @@ int32_t c_call_all(void) return 13; if (linkonce_odr_function() != 4) return 14; - if (common_function() != 5) - return 15; return 0; } diff --git a/tests/run/weak_function_linkage.rs b/tests/run/weak_function_linkage.rs index 349d3a3a485..c6c978c6179 100644 --- a/tests/run/weak_function_linkage.rs +++ b/tests/run/weak_function_linkage.rs @@ -41,13 +41,8 @@ extern "C" fn linkonce_odr_function() -> i32 { 4 } -// Upstream bug: LLVM rejects `common` on a function ("Functions may not have common linkage"), and -// with its verifier off inlines this body over the strong C one at -O3, so cg_llvm fails here. -#[linkage = "common"] -#[no_mangle] -extern "C" fn common_function() -> i32 { - 0 -} +// `#[linkage = "common"]` is absent on purpose: a common symbol is `SHN_COMMON`, which the object +// format only allows for objects, so no backend can give a function that linkage. // Not overridden by the C side: the definition here is the one that runs. #[linkage = "weak"] @@ -97,9 +92,6 @@ extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { if linkonce_odr_function() != 4 { return 4; } - if common_function() != 5 { - return 5; - } if only_weak_function() != 6 { return 6; } From 996806ba1e078a8524b99cac3e824f6acf6f2e4b Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 26 Aug 2026 13:47:18 -0400 Subject: [PATCH 09/10] Update comments --- src/attributes.rs | 5 ++--- src/mono_item.rs | 4 ++-- tests/c/weak_function_linkage.c | 5 +++++ tests/run/weak_function_linkage.rs | 9 +++++---- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/attributes.rs b/src/attributes.rs index e4d44d790d3..9ff6c19f6f1 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -118,9 +118,8 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } else { codegen_fn_attrs.inline }; - // GCC warns that `inline` and `weak` conflict, and cg_gcc turns libgccjit warnings into - // errors. The linkage is what has to survive: rustc lints `#[inline]` as ignored on a - // function with an explicit `#[linkage]` anyway. `inline(never)` does not conflict. + // GCC drops `weak` from a function that is also `inline`, leaving the symbol strong, and + // the linkage is what has to survive. `inline(never)` does not conflict. let inline = match inline { InlineAttr::Always | InlineAttr::Hint | InlineAttr::Force { .. } if codegen_fn_attrs.linkage.is_some_and(base::linkage_needs_weak_attribute) => diff --git a/src/mono_item.rs b/src/mono_item.rs index f0b8c8a9dcc..57411c85477 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -52,8 +52,8 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.define_global(global_name, gcc_type, global_kind, is_tls, attrs.link_section); #[cfg(feature = "master")] { - // GCC warns that it ignores `visibility` on an internal global, and cg_gcc turns - // libgccjit warnings into errors. + // Visibility is meaningless on an internal global: GCC ignores the attribute and + // warns about it. if !matches!(global_kind, GlobalKind::Internal) { // If we're compiling the compiler-builtins crate, e.g., the equivalent of // compiler-rt, then we want to implicitly compile everything with hidden diff --git a/tests/c/weak_function_linkage.c b/tests/c/weak_function_linkage.c index 1d64f5365d1..25dcdedbcd9 100644 --- a/tests/c/weak_function_linkage.c +++ b/tests/c/weak_function_linkage.c @@ -26,6 +26,11 @@ int32_t linkonce_odr_function(void) return 4; } +int32_t weak_inline_function(void) +{ + return 8; +} + /* `available_externally` promises the real definition lives elsewhere: a backend may call this one * or emit an equivalent copy of the Rust body, so the two have to return the same value. */ int32_t available_externally_function(void) diff --git a/tests/run/weak_function_linkage.rs b/tests/run/weak_function_linkage.rs index c6c978c6179..677f0135340 100644 --- a/tests/run/weak_function_linkage.rs +++ b/tests/run/weak_function_linkage.rs @@ -59,14 +59,15 @@ extern "C" fn available_externally_function() -> i32 { 7 } -// GCC warns that `inline` and `weak` conflict, and cg_gcc turns libgccjit warnings into errors, so -// this used to fail to compile at all. The inline hint is what gives way: rustc lints it as ignored -// on a function with an explicit `#[linkage]` anyway, hence the `allow`. +// GCC drops `weak` from a function that is also `inline`: a backend that keeps the hint emits this +// as an ordinary global symbol and clashes with the C definition. rustc lints the hint as ignored +// on a function with an explicit `#[linkage]`, hence the `allow`. #[linkage = "weak"] #[inline] +#[no_mangle] #[allow(unused_attributes)] extern "C" fn weak_inline_function() -> i32 { - 8 + 0 } extern "C" { From 3de358db9e02fc7409f380dc14bee3bb431b26b8 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 26 Aug 2026 13:47:25 -0400 Subject: [PATCH 10/10] Update libgccjit version --- libgccjit.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgccjit.version b/libgccjit.version index 47539d889df..62417a80f82 100644 --- a/libgccjit.version +++ b/libgccjit.version @@ -1 +1 @@ -201ca90ac810d1c6509c252cc9c87d3ace0661d7 +badf78d09d16e66f4ca07971c51aa6a227558d4f