代码之家  ›  专栏  ›  技术社区  ›  laptou

防锈组件:我如何表示我需要SP的值?

  •  0
  • laptou  · 技术社区  · 4 年前

    我在胡闹 asm! 嵌入式手臂(拇指)目标上的宏。我有一个中断服务程序,用来获取 svc 指令被调用为:

    #[cortex_m_rt::exception]
    unsafe fn SVCall() {
        let mut svc_num: u8;
    
        asm!(
            "ldr {0}, [sp, #40]",   // read the PC that was saved before this interrupt happened
            "movs {1}, #2",         // store 2 in a reg
            "subs {0}, {1}",        // subtract 2 from that PC we recovered
            "ldrb {2}, [{0}]",      // read the byte at that position
            out (reg) _,
            out (reg) _,
            lateout (reg) svc_num
        );
    
        defmt::info!("svcall #{}", svc_num);
    }
    

    当我反汇编用 opt-level = 2 (这很重要,我得到的结果与 opt-level = 0 ),我得到以下信息:

    08000502 <SVCall>:
     8000502:       b580            push    {r7, lr}
     8000504:       466f            mov     r7, sp
     8000506:       b082            sub     sp, #8
     8000508:       980a            ldr     r0, [sp, #40]   ; 0x28
     800050a:       2102            movs    r1, #2
     800050c:       1a40            subs    r0, r0, r1
     800050e:       7802            ldrb    r2, [r0, #0]
     8000510:       f807 2c05       strb.w  r2, [r7, #-5]
     8000514:       f000 fb04       bl      8000b20 <_defmt_acquire>
     8000518:       f240 000e       movw    r0, #14
     800051c:       f2c0 0000       movt    r0, #0
     8000520:       f000 fb70       bl      8000c04 <_ZN5defmt6export9make_istr17h6ffa41eb00995773E>
     8000524:       f8ad 0004       strh.w  r0, [sp, #4]
     8000528:       a801            add     r0, sp, #4
     800052a:       f000 fba4       bl      8000c76 <_ZN5defmt6export6header17h9dd906a13f87833fE>
     800052e:       f240 0002       movw    r0, #2
     8000532:       f2c0 0000       movt    r0, #0
     8000536:       f000 fb65       bl      8000c04 <_ZN5defmt6export9make_istr17h6ffa41eb00995773E>
     800053a:       f827 0c02       strh.w  r0, [r7, #-2]
     800053e:       1eb8            subs    r0, r7, #2
     8000540:       f000 fb61       bl      8000c06 <_ZN5defmt6export4istr17hddd45161235dee63E>
     8000544:       1f78            subs    r0, r7, #5
     8000546:       f000 fbb3       bl      8000cb0 <_ZN5defmt6export8integers2i817h6232ecd7ea5eb90dE>
     800054a:       f000 faeb       bl      8000b24 <_defmt_release>
     800054e:       b002            add     sp, #8
     8000550:       bd80            pop     {r7, pc}
    

    我的计算表明,我应该只需要在我的计算中使用32的偏移量 ldr 指示,但我必须补偿 sub sp, #8 在我的代码之前插入的指令。

    我的两个问题是:

    • 有没有办法向Rust表明我不希望插入此指令(或者至少,它必须在我的指令之后插入) 啊! 说明),因为我需要读取 sp 登记
    • 我能假设吗 服务提供商 将始终复制到 r7 和使用 r7 作为堆栈帧基指针?
    0 回复  |  直到 4 年前
        1
  •  2
  •   laptou    4 年前

    你可以用 naked functions :

    #![no_std]
    #![feature(asm, naked_functions)]
    
    #[naked]
    #[export_name = "SVCall"]
    pub unsafe extern "C" fn SVCall() {
        asm!(
            "ldr r0, [sp, #40]",       // read the PC that was saved before this interrupt happened
            "ldrb r0, [r0, #-2]",      // read the byte at PC - 2
            "b other_func",            // call other_func with that byte as first argument
            options(noreturn)
        );
    }
    
    pub extern "C" fn other_func(svc_num: u8) {
        // do something
    }
    

    编辑:感谢彼得·科尔德斯指出了我之前的答案不起作用的许多方面。本质上,裸函数应该包含一个内联汇编块,并且只能在汇编中从中调用定义了ABI的函数。

        2
  •  2
  •   Martin Rosenau    4 年前

    ... 我不想插入这个指令。。。

    这对你毫无帮助:

    下一个编译器版本可能会使用 push {r6, r7, lr} 而不是 push {r7, lr} 您感兴趣的信息位于 sp+44 而不是 sp+40 .

    ... 我能假设吗 sp 将始终复制到 r7 ...

    即使是这样的情况,问题在于 按{r6,r7,lr} 这个假设无法解决这个问题。

    // read the PC that was saved before this interrupt happened
    

    要做到这一点,除了编写汇编中的整个函数和调用高级语言部分(例如Read、C、C++……),别无其他方法。来自汇编代码。

    在高级语言函数中使用内联汇编无法做到这一点。至少没有一个可以保证使用较新的编译器版本。

    推荐文章