-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
building for mipsel, link fails on missing symbol __sync_fetch_and_add_8, which doesn't exist on mips and shouldn't be getting called #112313
Comments
If your hypothesis about this being the profiler's fault is true, the question is raised: Does LLVM even support profiling this target? |
@workingjubilee Interesting idea that led to quite a deep rabbit hole. In short, LLVM implements the profiling bits using intrinsics that are implemented in C++. So, they should work on all platforms. This prompted me to take a look at the llvm-ir to possible narrow down whether the problem is coming from Rust or from LLVM. Hello world compiled with -C instrument-coverage:
Without coverage:
Here's the offending line:
That gets turned into the call to __sync_fetch_and_add_8 by LLVM. So, now, the question is where does that come from? |
A |
Well, it turns out it IS coming from LLVM after all. LLVM is hard coded to "lower" calls to the profile counter with atomic increments in many cases. Best I can figure out, the "lowering" was added behind a command line option, then later it was forced on in one of the build passes. The annoying part is that it's just blindly injecting a specific instruction that has varying target support and, from the place where it's doing that, I can't find a obvious way to get an instance of the target class that knows whether this is or isn't supported. Turning that injection off fixes the problem. I've written what I think is a decent patch that's easily extended if there's edge cases I've missed. I'm going to start the submission process with them. |
Possibly triggered by #111469, which instructs LLVM's coverage-instrumentation pass to generate atomic increments instead of non-atomic ones. |
@Zalathar Great find! Yes, that's exactly what's causing it. I'll look into that section and come up with a patch. |
I suspect that this issue affects any target that has max_atomic_width < 64. In particular, this same issue seems to also affect powerpc-unknown-linux-gnu, based on my experience in briansmith/ring#1877. I am getting undefined symbol errors for PR #113448 proposed a fix for the linking errors, to only use atomics on 64-bit targets. However, it was pointed out that is wrong on non-64-bit targets where It seems to me that we need to use libatomic or equivalent to emulate 64-bit atomics when 64-bit atomics are not implemented. However, some people metioned in PR #113448 that linking libatomic in these cases is not the right thing to do. But, that does seem to be what other (non-Rust) projects have done to resolve these issues, so I don't understand why not. |
This should be labeled O-PowerPC in addition to O-mips. Also O-thumb? if there is a label for Thumb targets. Based on llvm/llvm-project@3681be8 it seems like Clang added "prefer-atomic" which uses atomics iff the target supports them. So presumably there is mo precedent for atomic profiling data updates for 32-bit MIPS and 32-bit PowerPC, so the suggestion in the review of PR #113448 to turn off atomics for the profiler if max_atomic_width < 64 does seem reasonable. Since most targets seem to have max_atomic_width >= 64 maybe a hack to specifically disable for 32-bit targets when the arch is mips or powerpc would be acceptable, since it would avoid the need to refactor anything. If that is done, then people would likely need to run tests in single-threaded mode when collecting code coverage for 32-bit MIPS and 32-bit PowerPC. BTW, the reason it matters is that 32-bit MIPS and 32-bit PowerPC are also the only (AFAICT) Rust-supported 32-bit big-endian targets, so we have no code coverage instrumentation for any 32-bit-big-endian-specific code without supporting these targets. |
(CC'ing @bzEq for PowerPC atomic stuff) |
Besides PowerPC and MIPS, this seems to also affect riscv32. |
Would this solution be a reasonable trade-off? If not so, do you recommend trying something else? |
Fixed 64-bit step can lead to creating atomic instructions unsupported by the target architecture (see rust-lang/rust#112313).
Fixed 64-bit step can lead to creating atomic instructions unsupported by the target architecture (see rust-lang/rust#112313). When using atomics, type of the step is a pointer-sized integer. Otherwise, type of the step is of a largest legal integer type.
Fixed 64-bit step can lead to creating atomic instructions unsupported by the target architecture (see rust-lang/rust#112313). When using atomics, type of the step is a pointer-sized integer. Otherwise, type of the step is of a largest legal integer type.
Fixed 64-bit step can lead to creating atomic instructions unsupported by the target architecture (see rust-lang/rust#112313). When using atomics, type of the step is a pointer-sized integer. Otherwise, type of the step is of a largest legal integer type.
llvm/llvm-project#83239 changing the parameter in LLVM IR from i64 to i32 is incorrect, and breaks the design intention to avoid counter overflows. For Clang, |
While testing this, the only way I could reproduce this behavior is when building with my custom LLVM build. When LLVM is built alongside everything else, rust/src/bootstrap/src/core/build_steps/llvm.rs Lines 405 to 410 in 2781687
These are passed to CMake: rust/src/bootstrap/src/core/build_steps/llvm.rs Lines 777 to 779 in 2781687
This will actually make the compiler to generate So, when the compiler is built (without custom LLVM), here is the output of a random sample project build:
So, when we pass
the build is passing. I don't think this behavior is incorrect. GCC uses |
Based on @jdmitrovic-syrmia's comment above, I modified my CI configuration for to force |
I'm trying to build for mipsel-unknown-linux-gnu
x86_64 host, ubuntu 22.04
Freshly built from github source toolchain, with profiler = true in config.toml and mipsel-unknown-linux-gnu target as well as the host target. Built with x.py dist, using the stage 2 results as my build toolchain.
Building anything with RUSTFLAGS="-C instrument-coverage" set, leads to the following link error on several object files :
Single example:
Building the same code without the flag works fine. Resulting binary works fine.
Per the following GCC bug, MIPS 32 doesn't support 8 byte atomic operations:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56300
In the project/target/mipsel-unknown-linux-gnu folder, running
Shows that nearly every file includes a call to this non-existent function. I assume it's coming from the profiler somehow.
Executing the same command in my toolchain build/x86_64-unknown-linux-gnu/stage2
This is where I get stuck. The mipsel rust standard lib is under that directory, so my grep should catch any calls to __sync_fetch_and_add_8 contained in the standard lib. There don't appear to be any. The only cases I find are in libLLVM, which is built for x86_64. It's reasonable for them to be there, since x86_64 does support __sync_fetch_and_add_8.
You'll have to forgive my ignorance here, I'm still quite new to rust. I don't understand the intricacies of the build process well enough to figure out how a call to __sync_fetch_and_add_8 is getting pulled into object files targeting MIPS, when the only library instance of a call to there is in the x86_64 library.
What I am fairly certain of though is that it shouldn't be getting pulled in.
The text was updated successfully, but these errors were encountered: