2015-11-04 107 views
0

我想通過asm內聯一個分支來調用c中的外部函數。我正在編譯爲arm m0指令集,但它返回的是錯誤的表達式。ASM內聯調用C外部函數

的代碼是:

__asm volatile (
       " cmp  r3,#0     \n"      
       " b %[my_function]    \n" //Call function 
       " bx r14       \n" 
       : // no output 
       : [my_function] "i" (my_function) // input 
       : "r0" // clobber 
      ); 

的回報是:

/tmp/ccICkDIE.s: Assembler messages: 
/tmp/ccICkDIE.s:152: Error: bad expression -- `b #my_function' 

我們需要做什麼?

+1

爲什麼你將'my_function'標記爲直接參數?那麼'may_function'將如何返回,因爲你只能分支?下面這行永遠不會被執行。 – Olaf

+0

創建一個調用'my_function'的非常短的C程序。然後用'-S'選項編譯並查看編譯器生成的程序集。 – user3386109

+0

此代碼適用於Cortex-M4指令集,但不適用於Cortex-M0。 Olaf –

回答

0

你想要BL指令。這是「分支和鏈接」。它跳轉並將返回地址存儲在r14中。

但是,你仍然有一個問題...當你做BL,它破壞需要r14。你還有更多的工作要做,即使在以下情況下:

stmfd sp!,{v1-v6,lr}    // preserve caller registers 
bl  %[my_function]    // call function 
ldmfd sp!,{v1-v6,pc} @std   // restore caller registers and return 

你需要更多的調查。您可能想要反彙編已編譯的函數,並查看哪些包裝器會繞過內聯asm並進行相應調整。它可能爲你做stmfd/ldmfd。嘗試將r14標記爲clobber。

你可能會更好,只有BL。沒有恢復的BX可能會產生無限循環或不可預知的結果。我會離開它

+0

應該沒有需要一個_call site_來保存被保存的寄存器,即使在完全未定義的瘋狂的情況下,比如將內控轉移到內聯asm之外([asm goto](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm) .html#GotoLabels)例外)。在這種情況下,所有被調用者不需要保存的所有寄存器都需要保存,但是因爲無論如何你都必須在clobber列表中聲明所有這些寄存器,因此它有最低的工作機會,這是有效的已經照顧好了。 – Notlikethat

+0

@Notlikethat我回答了有關BL的主要內容。我從獨立的代碼中提取代碼。我告訴OP反彙編和檢查包裝,它可能會爲他做保存/恢復。最後,我說bl本身可能就足夠了。矯枉過正,也許,OP會弄清楚。但是,對於任何更復雜的情況:(例如)包含func需要3個參數:調用fA(a1),fB(a2),fC(a3)。您需要先在v1-v3中保存a1-a3,然後在呼叫前執行a1 = v2,a1 = v3。那麼stmfd/ldmfd就是v1-v3。也許比試圖用限制來做到這一點更容易[如果可能] –

+0

這甚至沒有解決實際問題;你剛纔在被問到的仍然是破折號的代碼行上添加了一個不恰當的樣板函數序言和尾聲,並建議OP應該在他們的實現基本破碎的自毀代碼時使用稍微不同的指令來產生它們的語法錯誤理念。此外,即使是「有用的建議」也是非常有害的,因爲不能保證r14在那個時候包含返回地址,即使這樣做,將它彈回到PC中,在內聯asm_中也是完全錯誤的。 – Notlikethat

0

在撰寫以下內容後,我想起了ethernut tutorial。他有幾乎相同的答案,

asm volatile(
    "mov lr, %1\n\t" 
    "bx %0\n\t" 
    : : "r" (main), "r" (JMPADDR)); 

該OP將很好地閱讀本教程;即使它是用於傳統的ARM而不是'm0'。


您可以使用'r'約束放置地址寄存器並跳轉到它。

An example可以在在線編譯器godbolt上找到。

extern int my_function(void); 

void f(void) 
{ 
__asm volatile (
       " cmp  r3,#0     \n"      
       " b %[my_function]    \n" //Call function 
       " bx r14       \n" 
       : // no output 
       : [my_function] "r" (my_function) // input 
       : "r0" // clobber 
      ); 

} 

隨着輸出,

f(): 
    ldr r3, .L2 
     cmp  r3,#0     
    b r3     
    bx r14       

    bx lr 
.L2: 
    .word my_function() 

我們可以看到輸出的幾個問題。 r14是lrb r3將直接轉移控制權並返回給f的調用者。 cmp r3, #0似乎完全不需要(由於有限的問題)。

上面的示例回答了這個問題,它可以用於尾部調用宏或其他用途,但它顯然需要一些工作。一個函數指針等,

int (*g)(void) = my_function; 

也將作爲參數「創建my_function」到GCC擴展彙編。

Another method只是使用'C'宏字符串連接。下面是一個起始樣品,

#define xstr(s) str(s) 
#define str(s) #s 
#define TAIL_CALL(func) __asm volatile(" b " str(func) "\n") 

對於大多數代碼尺寸(跳躍距離),分公司將能夠解決(4MB?)。如果你使用函數指針方法,那麼沒有問題。

+0

**注意!! **您不能將此機制用於複雜功能(使用堆棧框架)。 f()函數非常簡單。如果你想返回到'f()',那麼你需要使用'bl'並且應該在'clobber'列表中添加'lr'。然而,'b'可能對高級程序員有用;帶'longjmp()'的條件'TAIL_CALL'可以實現一些原始的異常處理。 –