From 1c1a287900ab85cd303b10931acf3fb0abbd9f71 Mon Sep 17 00:00:00 2001 From: Dundy Pasupuleti Date: Wed, 7 Oct 2026 22:59:28 -0400 Subject: [PATCH] feat(oxide-artifacts): add strong anchor-only host objects, bump to 0.2.2 Add two writers next to build_host_anchor_object_for_target: * build_host_strong_anchor_object_for_target defines the artifact link anchor as a strong global symbol in the anchor section, with no .oxart payload. * build_host_strong_anchor_object_for_target_with_legacy_anchor does the same with a weak legacy alias, matching the symbol bindings of build_host_object_for_target_with_legacy_anchor for full embeds. The existing weak anchor-only writer is unchanged; both share one helper that takes the binding strength. The backend needs these for issue 1365: a selected library whose cuda_module only has generic kernels it does not monomorphize still gets the anchor keep-alive reference from the macro, so it must define the anchor without embedding a bundle (PR 1372). The API is additive, so this is a patch bump. Per check-oxide-artifacts-parity.sh the release then gets published and the SIMT and backend requirements move to 0.2.2 with a relock. Tests: two new object tests check the strong binding, the weak legacy alias, and that discovery still finds no artifact bundle. Signed-off-by: Dundy Pasupuleti --- cuda-oxide/Cargo.lock | 16 +-- cuda-oxide/crates/oxide-artifacts/Cargo.toml | 2 +- cuda-oxide/crates/oxide-artifacts/src/lib.rs | 116 ++++++++++++++++++- 3 files changed, 124 insertions(+), 10 deletions(-) diff --git a/cuda-oxide/Cargo.lock b/cuda-oxide/Cargo.lock index 5bb9ee62fa..43ef5bed20 100644 --- a/cuda-oxide/Cargo.lock +++ b/cuda-oxide/Cargo.lock @@ -212,7 +212,7 @@ dependencies = [ "cuda-artifact-finalizer", "libnvvm-sys", "nvjitlink-sys", - "oxide-artifacts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "oxide-artifacts 0.2.1", "ptx-parse", "ptx-schedule", "reserved-oxide-symbols", @@ -405,7 +405,7 @@ dependencies = [ "cuda-bindings", "cuda-core-derive", "half", - "oxide-artifacts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "oxide-artifacts 0.2.1", ] [[package]] @@ -437,7 +437,7 @@ dependencies = [ "half", "libc", "libloading", - "oxide-artifacts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "oxide-artifacts 0.2.1", "ptx-parse", "sha2", "thiserror 2.0.18", @@ -994,7 +994,7 @@ dependencies = [ "llvm-export", "mir-lower", "once_cell", - "oxide-artifacts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "oxide-artifacts 0.2.1", "pliron", "pliron-derive", "reserved-oxide-symbols", @@ -1089,15 +1089,15 @@ checksum = "5a6298074a1742b584aaca148a6c61ecb27f0b55fdf237c235939c84924377a1" [[package]] name = "oxide-artifacts" version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d9a941c0c43d2dceaf25f5c9ca4c9f9cceb28dfa5e4e3cfd14e476f003e7c9" dependencies = [ "object", ] [[package]] name = "oxide-artifacts" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d9a941c0c43d2dceaf25f5c9ca4c9f9cceb28dfa5e4e3cfd14e476f003e7c9" +version = "0.2.2" dependencies = [ "object", ] @@ -1202,7 +1202,7 @@ version = "0.1.0" dependencies = [ "clap", "dialect-ptx", - "oxide-artifacts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "oxide-artifacts 0.2.1", "ptx-parse", "serde", "serde_json", diff --git a/cuda-oxide/crates/oxide-artifacts/Cargo.toml b/cuda-oxide/crates/oxide-artifacts/Cargo.toml index 42e95b8424..e7dd00b07d 100644 --- a/cuda-oxide/crates/oxide-artifacts/Cargo.toml +++ b/cuda-oxide/crates/oxide-artifacts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxide-artifacts" -version = "0.2.1" +version = "0.2.2" edition.workspace = true description = "Architecture-neutral embedded device artifact metadata for cuda-oxide" readme = "README.md" diff --git a/cuda-oxide/crates/oxide-artifacts/src/lib.rs b/cuda-oxide/crates/oxide-artifacts/src/lib.rs index 99ef56fea2..e317617a31 100644 --- a/cuda-oxide/crates/oxide-artifacts/src/lib.rs +++ b/cuda-oxide/crates/oxide-artifacts/src/lib.rs @@ -755,6 +755,58 @@ pub fn build_host_object_for_target_with_legacy_anchor( pub fn build_host_anchor_object_for_target( target: &str, anchor_symbol: &str, +) -> Result, ArtifactError> { + build_host_anchor_object_for_target_with_strength(target, anchor_symbol, true) +} + +/// Build a host object that only defines a strong artifact link-anchor symbol. +/// +/// Unlike [`build_host_anchor_object_for_target`], the anchor is a strong +/// global definition, the same binding a full `.oxart` embed gives it. The +/// CUDA backend uses this for a selected crate whose `#[cuda_module]` only has +/// generic kernels that this crate does not monomorphize: the macro still +/// references the anchor to keep the rlib member linked (#72, #1365), but +/// there is no device bundle to embed. Artifact discovery sees no `.oxart` +/// section in the result. +#[cfg(feature = "object-write")] +pub fn build_host_strong_anchor_object_for_target( + target: &str, + anchor_symbol: &str, +) -> Result, ArtifactError> { + build_host_anchor_object_for_target_with_strength(target, anchor_symbol, false) +} + +/// Like [`build_host_strong_anchor_object_for_target`], plus a weak legacy +/// alias. +/// +/// The target-specific anchor is strong and the legacy package anchor is weak, +/// matching [`build_host_object_for_target_with_legacy_anchor`] for full +/// embeds, so older macro expansions that still reference the legacy name +/// link against the same placeholder. +#[cfg(feature = "object-write")] +pub fn build_host_strong_anchor_object_for_target_with_legacy_anchor( + target: &str, + anchor_symbol: &str, + legacy_anchor_symbol: &str, +) -> Result, ArtifactError> { + if anchor_symbol.is_empty() || legacy_anchor_symbol.is_empty() { + return Err(ArtifactError::Malformed( + "embedded artifact anchor symbol is empty".to_string(), + )); + } + build_host_object_with_section( + ARTIFACT_ANCHOR_SECTION_NAME, + &[0], + target, + &[(anchor_symbol, false), (legacy_anchor_symbol, true)], + ) +} + +#[cfg(feature = "object-write")] +fn build_host_anchor_object_for_target_with_strength( + target: &str, + anchor_symbol: &str, + weak: bool, ) -> Result, ArtifactError> { if anchor_symbol.is_empty() { return Err(ArtifactError::Malformed( @@ -766,7 +818,7 @@ pub fn build_host_anchor_object_for_target( ARTIFACT_ANCHOR_SECTION_NAME, &[0], target, - &[(anchor_symbol, true)], + &[(anchor_symbol, weak)], ) } @@ -1306,6 +1358,68 @@ mod tests { ); } + /// A strong anchor-only object binds the anchor like a full embed does + /// (#72, #1365) and still carries no artifact bundle. + #[cfg(all(feature = "object-read", feature = "object-write"))] + #[test] + fn strong_anchor_only_object_is_global_and_not_weak() { + use object::{Object, ObjectSymbol}; + + let bytes = build_host_strong_anchor_object_for_target( + "x86_64-unknown-linux-gnu", + "selected_empty_merge_anchor", + ) + .unwrap(); + let file = object::File::parse(bytes.as_slice()).unwrap(); + let anchor = file + .symbols() + .find(|symbol| symbol.name() == Ok("selected_empty_merge_anchor")) + .expect("strong anchor symbol missing"); + + assert!(anchor.is_definition()); + assert!(anchor.is_global()); + assert!(!anchor.is_weak()); + assert!( + read_artifact_bundles_from_object_bytes(&bytes) + .unwrap() + .is_empty() + ); + } + + #[cfg(all(feature = "object-read", feature = "object-write"))] + #[test] + fn strong_target_anchor_also_defines_weak_legacy_alias() { + use object::{Object, ObjectSymbol}; + + let bytes = build_host_strong_anchor_object_for_target_with_legacy_anchor( + "x86_64-unknown-linux-gnu", + "target_stub_anchor", + "legacy_stub_anchor", + ) + .unwrap(); + let file = object::File::parse(bytes.as_slice()).unwrap(); + let target = file + .symbols() + .find(|symbol| symbol.name() == Ok("target_stub_anchor")) + .expect("strong target anchor missing"); + let legacy = file + .symbols() + .find(|symbol| symbol.name() == Ok("legacy_stub_anchor")) + .expect("weak legacy alias missing"); + + assert!(target.is_definition()); + assert!(target.is_global()); + assert!(!target.is_weak()); + assert!(legacy.is_definition()); + assert!(legacy.is_global()); + assert!(legacy.is_weak()); + assert!( + read_artifact_bundles_from_object_bytes(&bytes) + .unwrap() + .is_empty() + ); + } + #[cfg(feature = "object-write")] #[test] fn anchor_only_object_rejects_empty_symbol() {