Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions changelog.d/9776-pthread-extern-signature-clash.md
Original file line number Diff line number Diff line change
@@ -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.
26 changes: 17 additions & 9 deletions crates/perry-runtime/src/error_stack_frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
24 changes: 14 additions & 10 deletions crates/perry-runtime/src/gc/roots/stack_maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Loading