I'm writing a bare-metal application for an AMR board in Rust that involves interrupt service routines. Currently, I use #naked functions with my own assembler prolog/epilog. However, I wonder whether there is a better (and hopefully more portable) way I've missed, maybe an #interrupt-like attribute in Rust nightly or any other compiler support. I think along the lines of GCC's __attribute__ ((interrupt ("IRQ"))), since Rust's backend LLVM provides such an attribute.
Interrupts are simply another type of calling convention. For the AVR port of Rust, we added two new types of calling convention, one for each kind of interrupt that AVR supports.
The authoritative list of calling conventions is the source code. Rust 1.16 lists these:
#[derive(PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Clone, Copy, Debug)]
pub enum Abi {
// NB: This ordering MUST match the AbiDatas array below.
// (This is ensured by the test indices_are_correct().)
// Single platform ABIs
Cdecl,
Stdcall,
Fastcall,
Vectorcall,
Aapcs,
Win64,
SysV64,
PtxKernel,
Msp430Interrupt,
// Multiplatform / generic ABIs
Rust,
C,
System,
RustIntrinsic,
RustCall,
PlatformIntrinsic,
Unadjusted
}
The unstable book also mentions that the different calling conventions exist.
To use these, you'd declare your function with it:
#![feature(abi_msp430_interrupt)]
extern "msp430-interrupt" fn handler() {}
It's still up to you to register the function as an exception handler with the interrupt vector table (or equivalent).
Of course, you may need to submit a PR that informs the Rust frontend of the specific LLVM calling convention to use, if yours isn't already in this list.
copy the information here, shamelessly ;)
https://github.com/nix-rust/nix
https://users.rust-lang.org/t/unix-signals-in-rust/733/3
use nix::sys::signal;
extern fn handle_sigint(_:i32) {
println!("Interrupted!");
panic!();
}
fn main() {
let sig_action = signal::SigAction::new(handle_sigint,
signal::SockFlag::empty(),
signal::SigSet::empty());
signal::sigaction(signal::SIGINT, &sig_action);
}
Related
In the example, hs reexport HashSet from std. But it compiles without error or warning. Why?
#![no_std]
pub use hs::HashSet;
pub fn new() -> HashSet<usize> {
HashSet::new()
}
pub fn insert(a: &mut HashSet<usize>, v: usize) {
a.insert(v);
}
Well #![no_std] just means that you don't include std by default. It doesn't mean you can't explicitly or implicitly (i.e. through other crates) still include std. In other words #![no_std] does not prohibit using std but it disables using it by default.
For example this works, too:
#![no_std]
extern crate std;
fn main() {
std::println!("hello!");
}
Every crate in Rust is opt-in, that is, you need to explicitly ask it as a dependency to have it as a dependency of your code (usually through Cargo.toml). All, but one: the standard library (hence the name), which is opt-out. That is to say, if you do nothing, Rust will implicitly understand you require it as a dependency, and you explicitly need to tell it that you don't (with #![no_std]) to prevent this. Besides this, std behaves like any other crate, that is, even if it's not one of your direct dependencies, you can still access to reexports of your actual dependencies.
It should be noted, however, that there is no reason to not want to standard library as a dependency except in situations where you just don't have it (for instance, if you are writing embedded code, or a kernel, or ...). In this case, having std as a (transitive) dependency will just fail at compile time, which means that not only you have to use #[no_std], but so should all the dependencies you have, and their dependencies, and so on... In your case, you have annotated your code with #[no_std], but there was no hard requirement for you to do so, so there is no problem in having a dependency that has access to std.
Finally, note that some parts of the standard library are accessible even if the whole standard library is not. For instance, core is not OS-dependent, so you can use it even on embedded, and usually obtaining alloc one way or another (that is, implementing what is required yourself for instance) also provides you quite a lot of the standard library. In particular, I think HashSet only requires core and alloc to be available, not the whole standard library.
I have project that build with cargo workspace with including lot of crates.
One of the lower level crates contains generic data-structure with a lot of serde-code involved.
In order to reduce the compile time , I tried to crate objects with monomorphized instances on the the data-struct in a crate that is lower in the compilation hierarchy and use those in the higher-level crates. My goal to compile the lower-level crate only once, and then work on the higher level crate - without generating the monomorphized instances every time.
example:
lower-level crate
-----------------
pub struct MyCache<T> {
//generic implementation of cache
}
pub struct MyCacheString {
cache: MyCache<String>
}
higher-level crate
------------------
use MyCacheString;
but the problem is that the compiler generated that monomorphized in the higer-level crate (according to "cargo llvm-lines")
Is there is a way to ask/force the compiler to generate the monorphized code while it's compile the lower level crate?
ok, according to rustc_monomorphize::collector docstring:
Lazy mode means that items will only be instantiated when actually referenced
So in order to tigger the compiler to monomorphized the code in the low-level crate I need to call the object functions from public non-generic function lower-level crate. (not sure why - but it's not working from async function).
(If someone know about macro that tell the compiler to use the 'Eager Mode' it's will be better )
How do I declare an external MFC function that has LPCTSTR tokens?
I have the following C function in a library I wish to use:
DLL_PUBLIC void output( LPCTSTR format, ... )
Where DLL_PUBLIC is resolved to
__declspec(dllimport) void output( LPCTSTR format, ...)
In Rust, I was able to get things to build with:
use winapi::um::winnt::CHAR;
type LPCTSTR = *const CHAR;
#[link(name="mylib", kind="static")]
extern {
#[link_name = "output"]
fn output( format:LPCTSTR, ...);
}
I'm not sure this is the best approach, but it seems to get me part way down. Although the module is declared in a DLL module, the symbol decorations in the native DLL are such that it does not have _imp prepended to it in the binary. So I find that "static" seems to look for the correct module, although I still have not been able to get it to link.
The dumpbin output for the module (this target function) in "mylib.dll" is:
31 ?output##YAXPEBDZZ (void __cdecl output(char const *,...))
This is assuming that what you are trying to accomplish here is to link Rust code against a custom DLL implementation. If that is the case, then things are looking good.
First things first, though, you'll need to do some sanity cleanup. LPCTSTR is not a type. It is a preprocessor symbol that either expands to LPCSTR (aka char const*) or LPCWSTR (aka wchar_t const*).
When the library gets built the compiler commits to either one of those, and that decision is made for all eternity. Clients, on the other hand, that #inlcude the header are still free to choose, and you have no control over this. Lucky you, if you're using C++ linkage, and have the linker save you. But we aren't using C++ linkage.
The first order of action is to change the C function signature using an explicit type, so that clients and implementation always agree. I will be using char const* here.
C++ library
Building the library is fairly straight forward. The following is a bare-bones C++ library implementation that simply outputs a formatted string to STDOUT.
dll.cpp:
#include <stdio.h>
#include <stdarg.h>
extern "C" __declspec(dllexport) void output(char const* format, ...)
{
va_list argptr{};
va_start(argptr, format);
vprintf(format, argptr);
va_end(argptr);
}
The following changes to the original code are required:
extern "C": This is requesting C linkage, controlling how symbols are decorated as seen by the linker. It's the only reasonable choice when planning to cross language boundaries.
__declspec(dllexport): This is telling the compiler to inform the linker to export the symbol. C and C++ clients will use a declaration with a corresponding __declspec(dllimport) directive.
char const*: See above.
This is all that's required to build the library. With MSVC the target architecture is implied by the toolchain used. Open up a Visual Studio command prompt that matches the architecture eventually used by Rust's toolchain, and run the following command:
cl.exe /LD dll.cpp
This produces, among other artifacts, dll.dll and dll.lib. The latter being the import library that needs to be discoverable by Rust. Copying it to the Rust client's crate's root directory is sufficient.
Consuming the library from Rust
Let's start from scratch here and make a new binary crate:
cargo new --bin client
Since we don't need any other dependencies, the default Cargo.toml can remain unchanged. As a sanity check you can cargo run to verify that everything is properly set up.
If that all went down well it's time to import the only public symbol exported by dll.dll. Add the following to src/main.rs:
#[link(name = "dll", kind = "dylib")]
extern "C" {
pub fn output(format: *const u8, ...);
}
And that's all there is to it. Again, a few details are important here, namely:
name = "dll": Specifies the import library. The .lib extension is implied, and must not be appended.
kind = "dylib": We're importing from a dynamic link library. This is the default and can be omitted, though I'm keeping it for posterity.
extern "C": As in the C++ code this controls name decoration and the calling convention. For variadic functions the C calling convention (__cdecl) is required.
*const u8: This is Rust's native type that corresponds to char const* in C and C++. Using type aliases (whether those provided by the winapi crate or otherwise) is not required. It wouldn't hurt either, but let's just keep this simple.
With that everything is set up and we can take this out for a spin. Replace the default generated fn main() with the following code in src/main.rs:
fn main() {
unsafe { output("Hello, world!\0".as_ptr()) };
}
and there you have it. cargo running this produces the famous output:
Hello, world!
So, all is fine, right? Well, no, not really. Actually, nothing is fine. You could have just as well written, compiled, and executed the following:
fn main() {
unsafe { output(b"Make sure this has reasons to crash: %f".as_ptr(), "💩") };
}
which produces the following output for me:
Make sure this has reasons to crash: 0.000000💩
though any other observable behavior is possible, too. After all, the behavior is undefined. There are two bugs: 1 The format specifier doesn't match the argument, and 2 the format string isn't NUL terminated.
Either one can be fixed, trivially even, though you have opted out of Rust's safety guarantees. Rust can't help you detect either issue, and when control reaches the library implementation, it cannot detect this either. It will just do what it was asked to do, subverting each and every one of Rust's safety guarantees.
Remarks
A few words of caution: Getting developers interested in Rust is great, and I will do my best to try whenever I get a chance to. Getting Rust-curious developers excited about Rust is often just a natural progression.
Though I will say that trying to get developers excited about Rust by starting out with unsafe Rust isn't going to be successful. It's eventually going to provoke a response like: "Look, ma, a steep learning curve with absolutely no benefit whatsoever, who could possibly resist?!" (I'm exaggerating, I know).
If your ultimate goal is to establish Rust as a superior alternative to C (and in part C++), don't start by evaluating how not to benefit from Rust. Specifically, trying to import a variadic function (the unsafest language construct in all of C++) and exposing it as an unsafe function to Rust is almost guaranteed to be the beginning of a lost battle.
Now, this may read bad as it is already, but this isn't over yet. In an attempt to make your C++ code accessible from Rust, things have gotten worse! With a C++ compiler and static code analysis tools (assuming the format string is known at compile time, and the tools understand the semantics), the tooling can and frequently will warn about mismatches. That option is now gone, forever, and there's not even a base level of protection.
If you absolutely want to make some sort of logging available to Rust, export a function from the library that takes a single char const*, use Rust's format! macro, and provide a variadic wrapper to C and C++ clients.
If I mark my struct as Sync will the compiler output differ? Will the compiler implement some Mutex-like magic?
struct MyStruct {
data: RefCell<u32>,
}
unsafe impl Sync for MyStruct {}
unsafe impl Send for MyStruct {}
The compiler uses a mechanism named "language items" to reference items (types, traits, etc.) that are defined in a library (usually core) but are used by the compiler, whether that be in code generated by the compiler, for validating the code or for producing specialized error messages.
Send and Sync are defined in the core library. Sync is a language item, but Send isn't. The only reference to Sync I could find in the compiler is where it checks that the type of a static variable implements Sync. (Send and Sync used to be more special to the compiler. Before auto traits were added to the language, they were implemented as "auto traits" explicitly.)
Other than that, the compiler doesn't care about what Send and Sync mean. It's the libraries (specifically, types/functions that are generic over Send/Sync types) that give the traits their meaning.
Neither trait influences what code is emitted by the compiler regarding a particular type. Making a type "thread-safe" is not something that can be done automatically. Consider a struct with many fields: even if the fields are all atomic types, a partially updated struct might not be in a valid state. The compiler doesn't know about the invariants of a particular type; only the programmer knows them. Therefore, it's the programmer's responsibility to make the type thread-safe.
std::sync::atomic::AtomicUsize implements Sync which means immutable references are free of data races when shared between multiple threads. Why does AtomicUsize not implement Send? Is there state which is linked to the thread that created the atomic or is this a language design decision relating to the way atomics are intended to be used i.e. via a Arc<_> etc.
It's a trick! AtomicUsize does implement Send:
use std::sync::atomic::AtomicUsize;
fn checker<T>(_: T) where T: Send {}
fn main() {
checker(AtomicUsize::default());
}
In fact, there's even an automated test that ensures this is the case.
Rust 1.26
These auto traits are now documented, thanks to a change made to rustdoc.
Previous versions
The gotcha lies in how Send is implemented:
This trait is automatically derived when the compiler determines it's appropriate.
This means that Rustdoc doesn't know that Send is implemented for a type because most types don't implement it explicitly.
This explains why AtomicPtr<T> shows up in the implementers list: it has a special implementation that ignores the type of T.