Make Sure Function Address is 4Byte Aligned

I'm writing to the RISC-V register MTVEC (Machine Trap Vector). This tells the processor where to go when a trap happens whether interrupt or exception. My program wasn't entering my trap function, so I printed out my trap function address and compared it to what was read out of the MTVEC register after writing.

trap_function address: 0x2001102A
MTVEC address:         0x20011028

The lower 2 bits of the MTVEC register determines the trap mode, therefore the function address must be at an address that is 4-Byte aligned (last two bits being zero), which 0x2001102A is not.

How can I change, or make sure the trap function is located at a 4-Byte aligned address?

I was able to get my function 4-byte aligned by moving it inside my program, but I don't like not knowing if adding more code will cause it to not be aligned again.

Just a thought; what if you use inline assembly to insert 3 nop instructions and just increment the function pointer before setting it to the register? Really hacky, but it could work

Another option: you might be able to use a static byte array + some #[repr(align)] semantics to create a pseudo function which jumps into the correct one.

I'm offering these hacks in case there is no correct way to do this.

1 Like

How did you print out function addresses ?
Is the function on Rust side, or on extern side in C ?

I'm asking this because function address and function item are different in Rust.

Also, if it is possible, could you make a small self-contained example on https://godbolt.org or playground ? That could clear things up alot.

1 Like

Another not great solution: specify a link_section attribute and either hope new sections are at least 4-byte aligned or write a linker script that enforces the alignment.

1 Like

This is my original code. Putting the trap_handler function in front of the user_mode function changed it in a shortened version of this I made for debugging, but this program still has the function as not 4-Byte Aligned. I believe it will change every time I add more code. So I definitely need to solve this

Here is some code to play with in the playground https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ae3a99c0b38c2770dc5e7f858e3acc5b

#![no_std]
#![no_main]

extern crate panic_halt;

use riscv_rt::entry;
use hifive1::hal::prelude::*;
use hifive1::hal::DeviceResources;
use hifive1::{sprintln, pin};
//use core::mem;
use riscv::register::{mepc,mstatus,mcause,mtvec,misa};

// Function to use as entry for user mode
fn user_mode(){
    //sprintln!("User Mode Entered!");  // Verify that user mode has been entered
    let _attempt = misa::read();
}
// This function handles machine traps from user mode due to interrupts or exceptions
fn trap_handler(){
    sprintln!("Machine Trap Occurred!");
    let trap_code= mcause::read().code();
    let ext = misa::read();
    sprintln!("Trap Code: {:0X}", trap_code);
    loop{};
}

#[entry]
fn main() -> ! {
    let dr = DeviceResources::take().unwrap();
    let p = dr.peripherals;
    let pins = dr.pins;

    // Configure clocks
    let clocks = hifive1::clock::configure(p.PRCI, p.AONCLK, 320.mhz().into());

    // Configure UART for stdoutcar
    hifive1::stdout::configure(p.UART0, pin!(pins, uart0_tx), pin!(pins, uart0_rx), 115_200.bps(), clocks);

    // Top of stack using all 16k of ram
    let stack_ptr: usize = 0x80004000;

    // Setup machine trap vector
    let trap_address = trap_handler as *const();
    unsafe{mtvec::write(trap_address as usize,mtvec::TrapMode::Direct)};

    // prepare to enter user mode
    let user_entry = user_mode as *const();
    mepc::write(user_entry as usize);  // Entry point for user mode
    unsafe{mstatus::set_mpp(mstatus::MPP::User)}; // Set MPP bit to enter user mode (00)

    //Set return address to 0, set stack pointer, mret to enter user mode 
    unsafe{
        asm!("mov ra, zero",
              "mov sp, {}" ,
              "mret",
               in(reg) stack_ptr);
    }


    loop{};
}

That is not a bad suggestion, but I've never messed with linkers before (other than changing a simple memory definition file). I'm not sure if I could do it.

@OptimisticPeach's suggestion on the align attribute might work. I've played with aligning a structure with #[repr(align(16))] then putting an array in the structure to align the array. I might be able to define the function inside an aligned structure? I haven't used structures a whole lot before, and I'm not sure on how to define a function inside a structure. Isn't that a method?

You'd need to manually fill the aligned array with the machine code necessary to jump to your actual implementation, and then cast the data pointer into a function pointer. That probably means a hardcoded byte sequence that you copy fn() as usize into.

Edit: Looks like RISC-V has only relative jumps, which will make this more complicated: your code generator will need to take into account both the function address and the location of the array it's storing into. (cf RISC-V Spec, Vol. 1, Sec. 2.5 )

I don't follow what you are referring to here.

RISC-V uses relative jumps, so the number stored in the jump instruction is the difference between the instruction's address and the destination address (ie. the value is added to the program counter instead of copied). If you have a fixed address you want to jump to, then, you need to convert it to relative by subtracting the location of the jump instruction itself; this requires knowing where in memory the instruction will be located.

I'm not sure about that. I'm using a core that uses the privileged specification. Straight from the The RISC-V Reader by Andrew Waterman and David Patterson.

Right; because Rust doesn't guarantee the alignment you need, the idea is to write a trampoline that's properly aligned and jumps to your real implementation. That trampoline is an unconditional jump, and the best option I see is a LUI, JALR sequence-- I assume nonprivileged instructions are also usable on the privileged core.

I've not worked with RISC-V, so this is based only on a few minutes with the spec; there may be other options.

1 Like

Oh I see, you are not saying that the pc cannot be set to the MEPC contents, you mean in order to get my function 4byte aligned I might have to align something else then assign that to the MEPC register then the alignment item would need to possibly do a jump sequence (relative) to get to the actual function address.

1 Like

Isn't the rust compiler built on llvm? Could this work?

3 Likes

Yahoo! I got it :slight_smile: And very simple too. in my .cargo/config file I added this line to my rust flags.

rustflags = [
...
"-C", "llvm-args=-align-all-functions=2"]

setting this to flag to 2 means 2^2 = 4 byte alignment

Everything worked on the first try surprisingly. I appreciate everyone's replies!

5 Likes

Instruction set manual section 1.5 says:

We use the term IALIGN (measured in bits) to refer to the instruction-address alignment constraintthe implementation enforces. IALIGN is 32 bits in the base ISA, but some ISA extensions, includingthe compressed ISA extension, relax IALIGN to 16 bits. IALIGN may not take on any value otherthan 16 or 32.

If you are not using the C extension, every function has to be aligned to 4 bytes anyway.

I'm running on a HiFive1 RevB with IMACU extensions