[英]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.