-
Notifications
You must be signed in to change notification settings - Fork 13.3k
release builds using rustc 1.86.0 on macOS Ventura (intel) SDK exhibit incorrect behaviour #140686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Can you try running cargo bisect-rustc to narrow down what triggered the change? The command line would probably look like Edit: .. ah, you might need to replace the |
We may have to rely on your bisection to pinpoint a commit (range), because that setup is quite complicated... |
To minimize the variables:
|
@moxian Sorry, I should have been more clear; this isn't only on cross compile. it happens if the macOS 13 SDK is used with the x86_64-apple-darwin target. both native and cross compiled. I ran the bisect script and came up with this being offending commit: d88ffcd output: searched nightlies: from nightly-2025-01-03 to nightly-2025-02-15 bisected with cargo-bisect-rustc v0.6.9Host triple: x86_64-apple-darwin cargo bisect-rustc --start 1.85.0 --end 1.86.0 --prompt -- run --release confirmed that this bug occurs in the beta, as well. I ran this with the following versions of reqwest and it occurred on each: http crate version |
cc @scottmcm |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@moxian the tests in I ran tests in the
these are the same errors that I'm reporting in this issue. the code snippet you had me run does not error whether it's run with Could this be a very specific edge case that reqwest just happens to surface? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
cc: some people in the (a bisection is at this comment) |
Thanks for the ping! I can reproduce the top-level example using: DEVELOPER_DIR=/Applications/Xcode\ 14.3.1.app/Contents/Developer cargo run --release --target x86_64-apple-darwin I'll try to investigate further. |
I'm inclined to think that this is a linker bug? Can reproduce with just: cargo rustc --release --target x86_64-apple-darwin -- -Clinker="/Applications/Xcode 14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld"
./target/x86_64-apple-darwin/release/foo Though still unsure of what the linker is actually doing wrong here, and if there's something But then again, this is also present in the older Xcode 13.4.1, so it may also be a recent problem in |
I've minimized this to: // dep.rs
#![crate_type = "lib"]
pub enum Version {
Http09,
Http10,
Http11,
H2,
H3,
}
pub fn encode(version: Version, dst: &mut Vec<u8>) -> NonTrivialDrop {
let has_drop = NonTrivialDrop;
match version {
Version::Http10 => dst.extend_from_slice(b"HTTP/1.0"),
Version::Http11 => dst.extend_from_slice(b"HTTP/1.1"),
Version::H2 => dst.extend_from_slice(b"HTTP/1.1"),
_ => {}
}
has_drop
}
pub struct NonTrivialDrop;
impl Drop for NonTrivialDrop {
#[inline(never)]
#[export_name = "non_trivial_drop"]
fn drop(&mut self) {
let _ = Box::new(0);
}
} // foo.rs
extern crate dep;
use dep::{Version, encode};
fn main() {
let mut dst = Vec::with_capacity(100);
encode(Version::Http11, &mut dst);
let res = std::str::from_utf8(&dst).unwrap();
eprintln!("{res:?}");
} Compile with: rustc dep.rs --target x86_64-apple-darwin -C opt-level=1
rustc foo.rs --target x86_64-apple-darwin --extern dep=./libdep.rlib '-Clinker=/Applications/Xcode 14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld'
./foo # Outputs "", where it should've output "HTTP/1.1" Put it up as a GH repo at https://github.com/madsmtm/rustc-switch-older-xcode. Had to be in separate crates for some reason, possibly something relating to how the linker reads things from archives? Still unclear to me. |
The bisected commit is indeed a red herring, I've pushed a commit to the repo above which demonstrates the problem with at least Rust 1.76 as well. Will try to minimize it further. |
Now minimized to: // foo.rs
#[inline(never)]
#[no_mangle]
pub unsafe fn slice_len_from_ptr_end(ptr: *const u8, end: *const u8) -> usize {
core::hint::black_box(unsafe { end.offset_from(ptr) as usize })
}
#[inline]
pub fn slice_len(slice: &[u8]) -> usize {
let ptr = slice.as_ptr();
let end = unsafe { ptr.add(slice.len()) };
unsafe { slice_len_from_ptr_end(ptr, end) }
}
pub struct NonTrivialDrop;
impl Drop for NonTrivialDrop {
#[inline(never)]
#[export_name = "non_trivial_drop"]
fn drop(&mut self) {
core::hint::black_box(());
}
}
pub enum Version {
Http09,
Http10,
Http11,
H2,
H3,
}
#[no_mangle]
fn broken() -> usize {
let _has_drop = NonTrivialDrop;
match core::hint::black_box(Version::Http11) {
Version::Http10 => slice_len(b"HTTP/1.0"),
Version::Http11 => slice_len(b"HTTP/1.1"),
Version::H2 => slice_len(b"HTTP/1.1"),
_ => 0,
}
}
fn main() -> std::process::ExitCode {
if broken() == 8 { std::process::ExitCode::SUCCESS } else { std::process::ExitCode::FAILURE }
} Compile with: rustc foo.rs -C opt-level=1 --target x86_64-apple-darwin -Clink-arg=-ld_classic Seems like there's a bad interaction with the older linker (from Xcode 14.3.1 / when using |
I converted the code to C++: // Use any version of Xcode that has the old linker (-ld_classic).
// clang++ foo.cpp -fexceptions -O2 -target x86_64-apple-darwin -Wl,-ld_classic
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iostream>
// Helper to disable optimizations around a value.
static int black_box(int x) {
volatile int temp = x;
asm volatile("" ::: "memory");
return temp;
}
__attribute__((noinline))
int slice_len_from_ptr_end(const char* ptr, const char* end) {
return black_box(end - ptr);
}
static int slice_len(const char* ptr, int len) {
const char* end = ptr + len;
return slice_len_from_ptr_end(ptr, end);
}
__attribute__((noinline))
void non_trivial_destructor() {
asm volatile("" ::: "memory");
}
struct NonTrivialDestructor {
~NonTrivialDestructor() {
non_trivial_destructor();
}
};
__attribute__((noinline))
int broken(int version) {
NonTrivialDestructor _has_drop;
switch (version) {
case 1:
return slice_len("aaaaaaa", 8);
case 2:
return slice_len("bbbbbbb", 8);
case 3:
return slice_len("bbbbbbb", 8);
default:
return 42;
}
}
int main() {
// Should return 8, unexpectedly returns 0 with old linker.
return broken(2);
} I think that's as far as I'll go, I've already procrastinated on other things for wayyy too long today. I'd suggest that someone else reports this upstream to LLVM since it's not a Rust-specific issue? @rustbot label A-llvm -regression-untriaged |
Amazing. I can continue the investigation. |
Damn 🤦. Then that doesn't actually reproduce it. I've updated the code to be in C++ instead, since it seems to actually require the unwinding code. |
Wouldn't have been the first time unwinding broke in Apple's SDK tools sadly :/ #113783 was also a bad linker interaction with unwinding that impacted Rust. |
TL;DR Here is a smaller example:
|
I've submitted this upstream to Apple via. the Feedback Assistant as FB17575521, but I haven't had much luck with that in the past, so an interesting question is whether LLVM could emit different codegen to avoid this bug? |
Yes. It's possible. PR is rust-lang/llvm-project#181. I've also reached out to some folks at Apple, which might help the fix. |
I got a response from Apple on my feedback request:
|
Backread the issue but where does the |
Yup, The new linker source code hasn't been released, so this is currently the best way to cross-compile from Linux. See also tpoechtrager/osxcross#453 / tpoechtrager/osxcross#457. |
I’d like to report what appears to be a bug in the rust compiler which can be reproduced by using the reqwest crate, where it fails to include the http version string in the request and it only affects binaries that were:
The bug itself has nothing to do with the reqwest crate (looking at the code, it appears that this behaviour should be impossible) and specifically related to the built binary.
this bug does exist in release binaries cross-compiled from linux to macOS when using the macOS 13 (Ventura) SDK with osxcross.
the bug does not exist when creating a debug build or when cross-compiling from an AppleSilicon machine → intel and does not exist when performing a release build on intel → intel when the OS is not Ventura.
We tracked this down to being triggered by
opt-level = 2
this bug does not exist in rustc 1.85.0 and appears to only affect 1.86.0 (and also occurs in nightly rust).
I have a reduction:
add reqwest with
--features=blocking
and thismain.rs
:in another window, start a nc server with:
nc -l localhost 8888
then
cargo run
and you should see:If you then start a new nc server and
cargo run --release
you will see:(note the missing
HTTP/1.1
on the first line)when running this code in a debugger, I can see that the slice that’s supposed to add the version number to the request buffer has a length of 0, so it contains no data:
a note from someone on my team who continued to look into it more deeply:
“it looks like it's computing the correct string HTTP/1.1 but for some odd reason it's computing a length of 0. as best I can tell it uses one lookup table of string pointers to find the string, and it uses another lookup table of 4-byte offsets that get added to the lookup table's base address to produce a new pointer, which looks like it's supposed to be the end of the string. Unfortunately that second lookup table has 3 identical values in it, meaning it will produce the correct end pointer for HTTP/1.0 but it produces the start pointer for HTTP/1.1, and so it ends up calculating a length of 0”
This always happens no matter what version of reqwest is used, and it seems to be caused by the rustc version.
I hope this is enough information.
The text was updated successfully, but these errors were encountered: