Skip to content
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

*ring* fails to build for target_os = "none" targets #1793

Open
japaric opened this issue Nov 3, 2023 · 6 comments
Open

*ring* fails to build for target_os = "none" targets #1793

japaric opened this issue Nov 3, 2023 · 6 comments

Comments

@japaric
Copy link
Contributor

japaric commented Nov 3, 2023

I'm tried compiling a no-std executable that depends on ring to the x86_64-unknown-none target and got a linker error listing several "undefined references" like ring_core_0_17_5_ChaCha20_ctr32 and ring_core_0_17_5_bn_mul_mont.

I have tracked down the problem to these being symbols in the (pregenerated?) asm group. The build script does not produce the assembly files because none is not in the LINUX_ABI list. Adding "none" to this list fixes the linker error and produces a working binary. I have also tested that my program works (+) with the aarch64-unknown-none target. I'll submit a PR with that change.

(+) I'll eventually push the source code of my program to this draft rustls PR

japaric added a commit to japaric/ring that referenced this issue Nov 3, 2023
the built-in rustc targets `{x86,x86_64,arm,aarch64}-none` all use the
ELF format

fixes briansmith#1793
japaric added a commit to japaric/ring that referenced this issue Nov 3, 2023
the built-in rustc targets `{x86,x86_64,arm,aarch64}-none` all use the
ELF format

fixes briansmith#1793
@briansmith
Copy link
Owner

This is intentional. See https://doc.rust-lang.org/rustc/platform-support/x86_64-unknown-none.html#requirements. We cannot use vector or floating point registers for this target without additional target features being enabled, but much of our x86-64 assembly code generally assumes at least SSE2 can be used without checking CPUID or target features.

Further, it assumes AVX and other things can be used if CPUID reports they are available. Are we even allowed to execute CPUID on all these targets?

I do not know in all of the assembly code is free of use of the red zone. x86_64-xlate.pl seems to imply all the PerlAsm code avoids the red zone so maybe we just need to audit the .S files for x86-64.

So, as a start, at a minimum, every time we have target_arch = "x86_64" we'd need to replace it with at least all(target_arch = "x86_64", target_feature = "sse2").

Also, every assembly function's Rust declaration would need to be declared extern "win64" on Windows x86-64 and extern "sysv64" on non-Windows x86-64 targets. And the same for the C declarations of those few assembly functions that are called from C.

For AAarch64, I guess it would be similar.

@briansmith
Copy link
Owner

Also, every assembly function's Rust declaration would need to be declared extern "win64" on Windows x86-64 and extern "sysv64" on non-Windows x86-64 targets. And the same for the C declarations of those few assembly functions that are called from C.

The "perlasm_format" stuff in build.rs already handles this specific part. "linux64" as the PerlAsm format means "sysv64", basically.

@japaric
Copy link
Contributor Author

japaric commented Nov 7, 2023

Thanks for the pointers. I think that sounds reasonable. I'll have a closer look next week-ish.

@briansmith
Copy link
Owner

Thanks for the pointers. I think that sounds reasonable. I'll have a closer look next week-ish.

Thanks. I recommend commenting here with a proposed solution before writing a PR.

For aarch64 and ARM we make use of static target feature detection. We combine the features detected statically with any dynamic target feature detection that we do. In order to properly support this target, I think that we need to do the analogous thing for x86/x86-64 features. Notably, it seems like we should have an OS allowlist for the dynamic CPU feature detection for x86-64/x86 like we do for AArch64 as it apparently is not safe on such targets to depend on what CPUID says when deciding which CPU features to use. As a side effect, we would then also be able to support i586 targets since we'd drop our implicit SSE2 dependency. Then the spin dependency in turn would be conditional on the OS using the same list of OSs.

However, that would leave these targets with, by default, the slowest/worst implementations of all algorithms.

I wonder if there are better targets where you can demonstrate your no_std support in a way where we'd be free to keep doing our dynamic detection and make use of SSE2 by default as we currently are?

@briansmith
Copy link
Owner

So, I think phase 1 is just to implement static feature detection for x86_64 like we do for aarch64. The combination of static and dynamic feature detection in Aarch64 serves as a model. See

macro_rules! features {
. I hope we could find some way to generalize that without just copy/pasting it.

Dynamic feature detection has a lot more complexity.

Let's take x86_64-unknown-none as an example. This is the target that is being used for Linux kernel code. In order to safely use vector registers for this target when running within the Linux kernel, we need to wrap all SSE usage in kernel_fpu_begin and kernel_fpu_end. Maybe there are other things we need to do too? But, how do we know that we're actually targeting the Linux kernel? Is target_env or target_family set to something that clearly identifies the target as Linux?

Now, let's say the target is x86_64-unknown-none and the target isn't the Linux kernel, but rather some other environment. How are we supposed to determine if the environment requires us to call functions analogous to kernel_fpu_begin and kernel_fpu_end and how do we discover the names of those functions and what library they are defined in? Or how could we determine that the environment allows us to use vector registers and other advanced CPU features without doing this extra work? How do we determine if it is safe to execute CPUID/xgetbv, rely on the reported results, and use cached copies of the results across all CPU cores? See rust-lang/rust#60123 (comment). Note that in a bare-metal target with mixed big/little cores, each core might support a different set of CPU features. Userspace applications rely on operating systems to abstract this difference in core capabilities away from us, and both ring and Rust Crypto rely on this.

Similar questions for the other target architectures (aarch64-, etc.).

@briansmith briansmith changed the title *ring* lacks assembly symbols when build for target_os = "none" targets *ring* fails to build for target_os = "none" targets Nov 28, 2023
@nspin
Copy link

nspin commented Jan 27, 2024

#1937 would resolve the immediate issue of the build failures by fixing a bug that was preventing such targets from using the fallbacks, but does not address the availability of PerlAsm implementations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants