diff --git a/changelog.d/9776-pthread-extern-signature-clash.md b/changelog.d/9776-pthread-extern-signature-clash.md new file mode 100644 index 0000000000..523fe121e2 --- /dev/null +++ b/changelog.d/9776-pthread-extern-signature-clash.md @@ -0,0 +1,18 @@ +**`perry-runtime` compiles again under `-D warnings` on linux-x86_64.** The crate +declared `pthread_getattr_np`, `pthread_attr_getstack` and `pthread_attr_destroy` +in three separate `extern "C"` blocks using two different spellings of the +`pthread_attr_t` buffer — `[u64; 8]` in `gc::roots::get_stack_bottom`, `*mut u8` +in `gc::roots::stack_maps::fp_chain` and in the Error-stack frame walk. Declaring +one symbol twice in a crate with different signatures is +`clashing_extern_declarations`, which `-D warnings` denies, and the crate stopped +building outright (#9486's frame walk supplied the third copy). + +The `*mut u8` spelling was not itself new, but its only previous home was +`fp_chain`, which is gated to `target_arch = "aarch64"`; on an x86_64 host that +module is compiled out and the two spellings never met. + +All three blocks now use `[u64; 8]`, the spelling the collector has always used. +That also clears the latent aarch64-linux collision between `fp_chain` and +`gc::roots`, and replaces a `[u8; 128]` buffer — 128 bytes but aligned to 1 — +with one that is both large enough for `pthread_attr_t` on every supported +glibc/musl target (56 bytes on both) and correctly aligned for it. diff --git a/crates/perry-runtime/src/error_stack_frames.rs b/crates/perry-runtime/src/error_stack_frames.rs index 5f7c03693e..49e8162756 100644 --- a/crates/perry-runtime/src/error_stack_frames.rs +++ b/crates/perry-runtime/src/error_stack_frames.rs @@ -195,25 +195,33 @@ mod walk { #[cfg(all(target_os = "linux", not(target_vendor = "apple")))] fn stack_top_uncached() -> usize { + // These signatures must stay identical to the ones in + // `gc::roots::get_stack_bottom` and `gc::roots::stack_maps::fp_chain`. + // Two `extern "C"` blocks in one crate that declare the same symbol + // with different argument types are a hard error under `-D warnings` + // (`clashing_extern_declarations`), and `[u64; 8]` — 64 bytes, which + // covers `pthread_attr_t` on every supported glibc/musl target — is + // the spelling the collector has always used. Unlike a `[u8; N]` + // buffer it is also correctly aligned for the attr union. unsafe extern "C" { fn pthread_self() -> usize; - fn pthread_getattr_np(thread: usize, attr: *mut u8) -> i32; + fn pthread_getattr_np(thread: usize, attr: *mut [u64; 8]) -> i32; fn pthread_attr_getstack( - attr: *const u8, - stackaddr: *mut *mut core::ffi::c_void, + attr: *const [u64; 8], + stackaddr: *mut *mut u8, stacksize: *mut usize, ) -> i32; - fn pthread_attr_destroy(attr: *mut u8) -> i32; + fn pthread_attr_destroy(attr: *mut [u64; 8]) -> i32; } - let mut attr = [0u8; 128]; - let mut addr: *mut core::ffi::c_void = core::ptr::null_mut(); + let mut attr = [0u64; 8]; + let mut addr: *mut u8 = core::ptr::null_mut(); let mut size: usize = 0; unsafe { - if pthread_getattr_np(pthread_self(), attr.as_mut_ptr()) != 0 { + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { return 0; } - let ok = pthread_attr_getstack(attr.as_ptr(), &mut addr, &mut size) == 0; - pthread_attr_destroy(attr.as_mut_ptr()); + let ok = pthread_attr_getstack(&attr, &mut addr, &mut size) == 0; + pthread_attr_destroy(&mut attr); if !ok { return 0; } diff --git a/crates/perry-runtime/src/gc/roots/stack_maps.rs b/crates/perry-runtime/src/gc/roots/stack_maps.rs index 4e52ed3915..b1162161e7 100644 --- a/crates/perry-runtime/src/gc/roots/stack_maps.rs +++ b/crates/perry-runtime/src/gc/roots/stack_maps.rs @@ -1786,27 +1786,31 @@ mod fp_chain { /// back to the platform unwinder (fail-closed like every other anomaly). #[cfg(target_os = "linux")] fn stack_top() -> usize { + // Identical to `gc::roots::get_stack_bottom` and the Error-stack + // capture in `error_stack_frames.rs`: one crate may not declare the + // same `extern "C"` symbol with two different signatures + // (`clashing_extern_declarations`, denied via `-D warnings`). unsafe extern "C" { fn pthread_self() -> usize; - fn pthread_getattr_np(thread: usize, attr: *mut u8) -> i32; + fn pthread_getattr_np(thread: usize, attr: *mut [u64; 8]) -> i32; fn pthread_attr_getstack( - attr: *const u8, - stackaddr: *mut *mut c_void, + attr: *const [u64; 8], + stackaddr: *mut *mut u8, stacksize: *mut usize, ) -> i32; - fn pthread_attr_destroy(attr: *mut u8) -> i32; + fn pthread_attr_destroy(attr: *mut [u64; 8]) -> i32; } // pthread_attr_t is at most 64 bytes on glibc/musl for the supported - // targets; over-allocate defensively. - let mut attr = [0u8; 128]; - let mut addr: *mut c_void = std::ptr::null_mut(); + // targets, and `[u64; 8]` is both that size and correctly aligned. + let mut attr = [0u64; 8]; + let mut addr: *mut u8 = std::ptr::null_mut(); let mut size: usize = 0; unsafe { - if pthread_getattr_np(pthread_self(), attr.as_mut_ptr()) != 0 { + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { return 0; } - let ok = pthread_attr_getstack(attr.as_ptr(), &mut addr, &mut size) == 0; - pthread_attr_destroy(attr.as_mut_ptr()); + let ok = pthread_attr_getstack(&attr, &mut addr, &mut size) == 0; + pthread_attr_destroy(&mut attr); if !ok { return 0; }