簡體   English   中英

從裸asm函數訪問C ++非POD類數據

[英]Access C++ non-POD class data from naked asm function

是否可以使此混合的c ++ / asm函數符合標准? 函數ePendSV()必須具有以下布局:

ePendSV: //function entry point
    mrs r0,PSP
    stmdb r0!,{r4-r11,lr}
// compiler can generate any code here doing these things:
    readyTcbQueue.pTcb.runTcb->psp = r0;
    readyTcbQueue.pTcb.runTcb=readyTcbQueue.pTcb.readyTcb;
    r0 = readyTcbQueue.pTcb.readyTcb->psp;
// work with r0 in assembly
    ldmia r0!,{r4-r11,lr}
    msr PSP,r0
    bx lr

readyTcbQueue.pTcb是一個簡單的struct對象,只有兩個指向BragOsTcb對象的指針;

struct{
    BragOsTcb *runTcb;
    BragOsTcb *readyTcb;
};

BragOsTcb是非POD類,但是沒有虛擬函數和虛擬繼承,看起來像這樣:

class BragOsTcb : public TcbCdllq, public TimerTcbCdllq, public BragOsObject{
public:
    BragOsTcb();
....
private:
.....
public:
    unsigned long psp;
....
};

TcbCdllq,TimerTcbCdllq,BragOsObject也是具有相似布局且沒有虛函數的簡單類。 但是它們也是非POD。

我已經將此代碼制成了可在gcc和clang上使用的代碼,但這是一個非標准的hack,可能無法正常工作。

__attribute__((naked)) void ePendSV(){
  asm volatile("\
    mrs r0,PSP \n\
    stmdb r0!,{r4-r11,lr} \n\
\n\
    ldr r1,=%0 \n\
    ldmia r1,{r2,r3}  // r2=runTcb, r3=readyTcb \n\
    str r0,[r2,%1] // save psp \n\
    str r3,[r1,#0] // runTcb=readyTcb \n\
    ldr r0,[r3,%1] // readyTcb->psp \n\
\n\
   // restore LR(EXC_RETURN),R11-R4 from new PSP, set new PSP, return \n\
    ldmia r0!,{r4-r11,lr} \n\
    msr PSP,r0 \n\
    bx lr \n\
    "   :
        : "i"(&readyTcbQueue.pTcb),"i"(&(((BragOsTcb*)0)->psp))
        : );
    // it'is an offsetof hack which might not work
}

謝謝!

因此,您想要的是從無參數的裸函數訪問BragOsTcb的psp成員,並且進一步將其作為硬件中斷處理程序調用,這樣您就無法添加任何其他代碼來為您加載地址。 BragOsTcb不是POD,因此您擔心對於不同的編譯器,從類開頭的psp成員偏移將有所不同。

我建議以下方案:


struct BragOsTcbWrapper
{
    BragOsTcb* this_;
    unsigned long psp;
};

struct{
    BragOsTcbWrapper *runTcb;
    BragOsTcbWrapper *readyTcb;
};

class BragOsTcb : public TcbCdllq, public TimerTcbCdllq, public BragOsObject{
public:
    BragOsTcb()
    : pspHolder({this,0})
    {

    }
....
private:
.....
public:
    BragOsTcbWrapper pspHolder;
....
};

現在,您將開始:


__attribute__((naked)) void ePendSV(){
  asm volatile("\
    mrs r0,PSP \n\
    stmdb r0!,{r4-r11,lr} \n\
\n\
    ldr r1,=%0 \n\
    ldmia r1,{r2,r3}  // r2=runTcb, r3=readyTcb \n\
    str r0,[r2,%1] // save psp \n\
    str r3,[r1,#0] // runTcb=readyTcb \n\
    ldr r0,[r3,%1] // readyTcb->psp \n\
\n\
   // restore LR(EXC_RETURN),R11-R4 from new PSP, set new PSP, return \n\
    ldmia r0!,{r4-r11,lr} \n\
    msr PSP,r0 \n\
    bx lr \n\
    "   :
        : "i"(&readyTcbQueue.pTcb),"i"(&(((BragOsTcbWrapper*)0)->psp))
        : );
}

我想這對你有用

由於gcc / clang不支持裸功能中的擴展asm

nude該屬性允許編譯器構造必要的函數聲明,同時允許函數主體為匯編代碼。 指定的函數將沒有編譯器生成的序言/結尾序列。 裸函數中只能安全地包含基本的asm語句(請參見基本Asm)。 雖然使用擴展asm或基本asm和C代碼的混合似乎可以工作,但不能依靠它們可靠地工作並且不受支持。 https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html#ARM-Function-Attributes

我們可以使用普通的C ++函數並從裸跳/調用它

typedef unsigned long U32;

__attribute__((naked)) void ePendSV(){
    asm volatile("\
        mrs r0, PSP \n\
        stmdb r0!, {r4-r11,lr} \n\
        b realSwitchContext"); // this solution costs single jump instruction named **b**. It's valid for ARM-assembly in this case.
                              // Stack pointer has been saved in r0 register. we dont care about stack for now.
}

extern "C" void realSwitchContext(U32 psp){
    readyTcbQueue.pTcb.runTcb->psp = psp;
    BragOsTcb *tcb = readyTcbQueue.pTcb.readyTcb;
    readyTcbQueue.pTcb.runTcb = tcb;
    psp = tcb->psp;
    asm volatile("\
        ldmia %0!, {r4-r11,lr} \n\
        msr PSP, %0 \n\
        bx lr" : : "r"(psp)); // we just changed stack pointer then can return here and don't care about C++ stacked data
}

由於兩個堆棧指針-MSP和PSP,上述實現中存在一些錯誤。 如果C ++使用堆棧,則最后一個bx lr指令將破壞MSP。 下面的解決方案是正確的,但要花費2個“昂貴”的說明-bl(調用)和bx lr(返回)

__attribute__((naked)) void ePendSV(){
    asm volatile("\
        mrs r0, PSP \n\
        stmdb r0!, {r4-r11,lr} \n\
        bl realSwitchContext \n\
        ldmia r0!, {r4-r11,lr} \n\
        msr PSP, r0 \n\
        bx lr");
}

extern "C" U32 realSwitchContext(U32 psp){
    readyTcbQueue.pTcb.runTcb->psp = psp;
    BragOsTcb *tcb = readyTcbQueue.pTcb.readyTcb;
    readyTcbQueue.pTcb.runTcb = tcb;
    return tcb->psp;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM