Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I call a raw address from Rust?

I am writing an OS in Rust and need to directly call into a virtual address that I'm calculating (of type u32). I expected this to be relatively simple:

let code = virtual_address as (extern "C" fn ());
(code)();

However, this complains that the cast is non-primitive. It suggests I use the From trait, but I don't see how this could help (although I am relatively new to Rust and so could be missing something).

error[E0605]: non-primitive cast: `u32` as `extern "C" fn()`
 --> src/main.rs:3:16
  |
3 |     let code = virtual_address as (extern "C" fn ());
  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

I have everything in libcore at my disposal, but haven't ported std and so can't rely on anything that isn't no_std

like image 729
Isaac Woods Avatar asked Sep 09 '17 19:09

Isaac Woods


People also ask

Does Rust have raw pointers?

Rust has a number of different smart pointer types in its standard library, but there are two types that are extra-special. Much of Rust's safety comes from compile-time checks, but raw pointers don't have such guarantees, and are unsafe to use.

How do you find the address of a variable in Rust?

You need to use the & operator to get the address of any variable, so you need to write &my_struct as *const _ (where _ can be a literal _ , or the type of the value behind the pointer).

What is FN in Rust?

Keyword fnA function or function pointer. Functions are the primary way code is executed within Rust. Function blocks, usually just called functions, can be defined in a variety of different places and be assigned many different attributes and modifiers.

Does Rust have pointers or references?

A pointer is a general concept for a variable that contains an address in memory. This address refers to, or “points at,” some other data. The most common kind of pointer in Rust is a reference, which you learned about in Chapter 4. References are indicated by the & symbol and borrow the value they point to.


1 Answers

Casts of the type _ as f-ptr are not allowed (see the Rustonomicon chapter on casts). So, as far as I can tell, the only way to cast to function pointer types is to use the all mighty weapon mem::transmute().

But before we can use transmute(), we have to bring our input into the right memory layout. We do this by casting to *const () (a void pointer). Afterwards we can use transmute() to get what we want:

let ptr = virtual_address as *const ();
let code: extern "C" fn() = unsafe { std::mem::transmute(ptr) };
(code)();

If you find yourself doing this frequently, various kinds of macros can remove the boilerplate. One possibility:

macro_rules! example {
    ($address:expr, $t:ty) => {
        std::mem::transmute::<*const (), $t>($address as _)
    };
}
let f = unsafe { example!(virtual_address, extern "C" fn()) };
f(); 

However, a few notes on this:

  • If you, future reader, want to use this to do simple FFI things: please take a moment to think about it again. Calculating function pointers yourself is rarely necessary.
  • Usually extern "C" functions have the type unsafe extern "C" fn(). This means that those functions are unsafe to call. You should probably add the unsafe to your function.
like image 84
Lukas Kalbertodt Avatar answered Oct 21 '22 17:10

Lukas Kalbertodt