`core::intrinsics::abort` in no_std?


#1

I’m working on a project that uses Rust to create a WebAssembly binary which will be executed in a non-web (and even non-JS) environment. I have been using a nightly compiler but with the recent release, I started considering switching to stable. Unfortunately, I discovered some roadblocks preventing me from doing that.

My use-case is as follows:

I need to implement #[panic_handler] which performs some debug logging (which in my case is pretty much host-environment specific) and then just terminates execution via trap (ideally, with wasm’s unreachable instruction). For the moment the code is using core::intrinsics::unreachable and it requires nightly feature(core_intrinsics). Technically, it’s possible to change it to std::hint::unreachable_unchecked and get rid of the nightly feature. But this is still not a good idea since it’s an UB if the control flow reaches this function. It works for now because the optimizer is tied by immediately preceding calls to external functions I suppose, but this still looks too fragile.

I could try to abort the execution by different means like dividing by zero or adding a special function to the host environment sole purpose of which is just to trap. However, I think that those are awkward solutions.

What I think I really need is to use core::intrinsics::abort. As far as I understood it is lowered down to ud2, unreachable or similar instructions in non-std environments, and seems like this is exactly what I need.

The problem is that the stabilized version is std::process::abort which implies the existence of the standard library around which is not my case.

What do you think is the best way forward here? Does this use-case sound legit for you? If so is there any chance of stabilizing of core::intrinsics::abort for no_std environments separately?


#2

The two have the exact same semantics.

I do agree that abort is definitely the semantics that you want here, however.


#3

Can you even abort in a free-standing context? abort is normally a syscall, but you can’t syscall in a kernel.

Unless this abort does something different?


#4

The abort intrinsic is more “lightweight” - it turns into a ud2 on x86_64 for example. I wouldn’t be surprised if it still generated a call to libc abort on some architectures though.


#5

The two have the exact same semantics.

Yes, and they both suffer from the mentioned problem!

Can you even abort in a free-standing context? abort is normally a syscall, but you can’t syscall in a kernel. Unless this abort does something different?

Good questions. Indeed, abort is usually used as a name of a syscall, but here we have something different. As far as I understand, core::intrinsics::abort is lowered to llvm.trap which is defined as follows:

This intrinsic is lowered to the target dependent trap instruction. If the target does not have a trap instruction, this intrinsic will be lowered to a call of the abort() function.

It seems it is not exactly what I need. I’m not familiar with LLVM inner workings, but I wonder what LLVM would do if the target platform doesn’t have a special instruction for trap nor libc’s abort function : )

Another option would be to consider going via platform specific core::arch::wasm32::unreachable?