I'm writing an interrupt handler in Rust, and I want to use the ADR instruction to get the address of an array containing functions:
#[naked]
#[export_name = "SVCall"]
unsafe extern "C" fn svcall_isr() -> ! {
asm!(
"ldr r0, [sp, #24]", // read the PC that was saved before this interrupt happened
"movs r1, #2", // store 2 in a reg
"subs r0, r1", // subtract 2 from that PC we recovered
"ldrb r0, [r0]", // read the byte at that position
"adr r1, {0}", // put address of svcall_table into r1
"lsls r0, #2", // multiply svc number by 4 to get byte offset into svcall_table
"ldr r2, [r1, r0]", // load the pointer at offset in svcall_table
"mov pc, r2", // branch into specified svcall routine
sym SVCALL_TABLE,
options(noreturn)
);
}
pub static SVCALL_TABLE: [unsafe extern "C" fn() -> !; 3] = [svcall0_isr, svcall1_isr, svcall2_isr];
So that this interrupt handler can call svcall0_isr, svcall1_isr, or svcall2_isr (which are all unsafe extern "C" fns defined elsewhere in the file). However, this ADR instruction is not compiling correctly; this is the output of an objdump:
08000306 <SVCall>:
8000306: 9806 ldr r0, [sp, #24]
8000308: 2102 movs r1, #2
800030a: 1a40 subs r0, r0, r1
800030c: 7800 ldrb r0, [r0, #0]
800030e: f2af 010c subw r1, pc, #12
8000312: 0080 lsls r0, r0, #2
8000314: 580a ldr r2, [r1, r0]
8000316: 4697 mov pc, r2
8000318: defe udf #254 ; 0xfe
The ADR instruction is being transformed into a SUBW, and the resulting address is not correct. I also can't seem to find SVCALL_TABLE using objdump, even if I add a #[no_mangle]. Why is this happening, and how do I get the correct address?
Edit: I notice in pure-assembly examples like the one in this question, the table is defined using lines that say .word. How do I do this in Rust?