2015-05-20 21 views
4

我想branch使用ARM assembly而不是標籤,而不需要修改LR寄存器。所以我用B而不是BLBX。 我想要在GCC inline asm中完成。使用GCC內聯ARM asm分支到地址

Here是文檔,這裏是我已經試過:

#define JMP(addr) \ 
    __asm__("b %0" \ 
      : /*output*/ \ 
      : /*input*/ \ 
      "r" (addr) \ 
      ); 

它是一個C宏,可以用一個address被調用。當我運行它,我得到以下錯誤:

error: undefined reference to 'r3'

的錯誤是因爲"r"使用的。我仔細研究了一下,發現它可能是gcc 4.9。*版本上的一個bug。

順便說一下,我正在使用Android/Linux Gcc 4.9 cross compiler,在OSX。 另外,我不知道我應該在Rm上加載一些東西。

乾杯!

編輯: 我改變了宏這個,我仍然獲得undefined reference to r3 and r4

#define JMP(addr) \ 
    __asm__("LDR r5,=%0\n\t" \ 
      "LDR r4,[r5]\n\t"\ 
      "ADD r4,#1\n\t" \ 
      "B r4" \ 
      : /*output*/ \ 
      : /*input*/ \ 
      "r" (addr) \ 
      : /*clobbered*/ \ 
      "r4" ,"r5" \ 
      ); 

說明: 負載變量R5的地址,然後加載地址R4的值。然後給LSB加上1(ARM規範要求的emm)?最後轉到那個地址。

+0

「LDR僞指令裝載的寄存器與任一:一個32位常數值或地址」。所以在你的情況下編譯器認爲rX是一個符號。 – auselen

回答

3

您不能分支到一個註冊,您只能分支到一個標籤。如果要跳轉到寄存器中的地址,則需要將其移入PC寄存器(r15)。

#define JMP(addr) \ 
    __asm__("mov pc,%0" \ 
      : /*output*/ \ 
      : /*input*/ \ 
      "r" (addr) \ 
      ); 
+0

我幾分鐘前寫了類似的東西,但我還沒有測試過它!讓我們希望它有效! 順便說一句,因爲它是'gcc asm' pc和%0應該在你的例子中交換! – Paschalis

+3

我不認爲他們應該交換。使用ARM GCC使用標準的ARM操作數排序。您可能會考慮使用非標準訂單的x86目標。 –

+0

你是對的!謝謝 :) – Paschalis

2

既然你是在C語言進行編程,你可以只使用一個普通的C方法沒有任何組件都沒有:只有轉換的變量,保存指針,以解決您要跳轉到的,一個函數指針並調用它的時候了:

((void (*)(void)) addr)(); 

只是一個解釋,這片叢林括號: 與此代碼,你是鑄造addr的指針的函數不帶任何參數(由明星(*)表示)(第二void表示沒有參數),也不返回任何內容(第一個void)。最後最後兩個括號是該函數的實際調用。 谷歌的「C函數指針」有關該方法的更多信息。

但是,如果這不適合你,你仍然想用組裝的方法,你正在尋找的指令實際上是BX(不知道爲什麼你最初排除了這一點,但我可以猜測名稱「分支和交換」誤導你相信寄存器參數與程序計數器交換(從而改變),這是不是的情況,但它也在開始時使我困惑)。

對於只是說明一個簡單的回顧:

  • B會採取一個標籤作爲參數。實際上,跳轉會被編碼爲與當前位置的偏移量,這會告訴處理器跳轉許多指令向前或向後跳轉(通常編譯器,彙編器或鏈接器將負責爲您計算偏移量)。在執行期間,控制流將被簡單地轉移到該位置而不改變任何寄存器(這也意味着該鏈接寄存器LR將保持不變)
  • BX R0將採取絕對的(所以偏移量)地址從寄存器,在這種情況下,R0,並繼續在該地址執行。這也是沒有改變任何其他註冊。
  • BLBLX R0是前兩條指令的相應對應部分。他們會做同樣的事情控制流明智的,但最重要的是保存當前程序計數器在鏈接寄存器LR。如果被調用的函數應該稍後返回,則這是必需的。

所以在本質上,你需要做的是:

asm("BX %0" : : "r"(addr)); 

告訴編譯器確保變量addr是在寄存器(r),你是有希望爲只讀和不要改變。最重要的是,返回時你不會改變(破壞)任何其他寄存器。

請參閱此處 https://gcc.gnu.org/onlinedocs/gcc/Constraints.html 瞭解有關內聯裝配約束的更多信息。

爲了幫助你理解爲什麼也有其他的解決辦法左右浮動,這裏有些東西對ARM架構:

  • 程序計數器PC是作爲一個普通寄存器R15訪問許多指令。這只是該確切註冊號的別名。
  • 這意味着幾乎所有的算術和寄存器更改指令都可以將其作爲參數。但是,對於其中的很多人來說,這是被高度棄用的。
  • 如果你正在尋找在編譯ARM代碼程序的拆卸,任何功能將與三種情況之一結尾:
    • BX LR它做了你想做什麼:把鏈接寄存器的內容( LRR14的別名)並跳轉到該位置,實際返回給調用者
    • POP {R4-R11, PC}恢復調用者保存的寄存器並跳轉回調用者。這幾乎肯定會在函數的開頭有PUSH {R4-R11, LR}的對應部分:您正在將鏈接寄存器(返回地址)的內容壓入堆棧,但將其存回程序計數器,最終有效返回給調用者
    • B分支到不同的功能,如果此功能以尾呼叫結束並將其留給該功能返回到原始呼叫者。

希望幫助, 馬丁