簡體   English   中英

跳出內聯匯編到AVR32上的錯誤目標

[英]Jump out of inline assembly goes to the wrong target on AVR32

我們正在使用AtmelStudio 7.0.1645為Atmel AVR32 / UC3C0512C開發應用程序。 在進行一些基本測試時,我注意到一些非常奇怪的東西。

請考慮以下代碼(我知道這是不好的樣式,不常見,但這不是重點):

float GetAtan2f(float p_f_y,
                float p_f_x)
{
  unsigned int l_ui_x,
               l_ui_y,
               l_ui_Sign_x,
               l_ui_Sign_y,
               l_ui_Result;

  float l_f_Add,
        l_f_Result;

  asm volatile(
    "RJMP GETATAN2_EXIT \n"
    :
    : /* 0 */ "m" (p_f_y),
      /* 1 */ "m" (p_f_x)
    : "cc", "memory", "r0", "r1", "r2", "r3", "r5"
  );

  GETATAN2_EXIT:
  return (l_f_Result);
}

當查看該代碼的反匯編時(在編譯/鏈接之后),我發現以下內容:

Disassembly of section .text.GetAtan2f:

00078696 <GetAtan2f>:
   78696:   eb cd 40 af     pushm   r0-r3,r5,r7,lr
   7869a:   1a 97           mov r7,sp
   7869c:   20 9d           sub sp,36
   7869e:   ef 4c ff e0     st.w    r7[-32],r12
   786a2:   ef 4b ff dc     st.w    r7[-36],r11
   786a6:   e0 8f 00 00     bral    786a6 <GetAtan2f+0x10>
   786aa:   ee f8 ff fc     ld.w    r8,r7[-4]
   786ae:   10 9c           mov r12,r8
   786b0:   2f 7d           sub sp,-36
   786b2:   e3 cd 80 af     ldm sp++,r0-r3,r5,r7,pc

我們注意到rjmp已經變得bral -完全可以接受,只是同一件事的另一個助記符。

但是,當查看該行中的分支目標時,我們還注意到,這將產生一個無窮循環 ,這顯然是不應該的。 它應該分支到786aa (這是函數返回的開始),而不是786a6

如果我更改代碼以使其讀取

float GetAtan2f(float p_f_y,
                float p_f_x)
{
  unsigned int l_ui_x,
               l_ui_y,
               l_ui_Sign_x,
               l_ui_Sign_y,
               l_ui_Result;

  float l_f_Add,
        l_f_Result;

  asm volatile(
    "RJMP GETATAN2_EXIT \n"
    :
    : /* 0 */ "m" (p_f_y),
      /* 1 */ "m" (p_f_x)
    : "cc", "memory", "r0", "r1", "r2", "r3", "r5"
  );

  asm volatile(
    "GETATAN2_EXIT: \n"
    :
    :
    : "cc", "memory"
  );

  return (l_f_Result);
}

它按預期工作,即現在反匯編顯示為

Disassembly of section .text.GetAtan2f:

00078696 <GETATAN2_EXIT-0x12>:
   78696:   eb cd 40 af     pushm   r0-r3,r5,r7,lr
   7869a:   1a 97           mov r7,sp
   7869c:   20 9d           sub sp,36
   7869e:   ef 4c ff e0     st.w    r7[-32],r12
   786a2:   ef 4b ff dc     st.w    r7[-36],r11
   786a6:   c0 18           rjmp    786a8 <GETATAN2_EXIT>

000786a8 <GETATAN2_EXIT>:
   786a8:   ee f8 ff fc     ld.w    r8,r7[-4]
   786ac:   10 9c           mov r12,r8
   786ae:   2f 7d           sub sp,-36
   786b0:   e3 cd 80 af     ldm sp++,r0-r3,r5,r7,pc

我們注意到分支目標現在是正確的。

因此,內聯匯編程序顯然不了解C標簽(即,不在內聯匯編中的標簽),這本身就可以-經驗教訓。

但是此外,當遇到未知(未定義)的標簽時,它不會發出警告或引發錯誤,而是在分支/跳轉到此類標簽​​時,僅使用偏移量0會產生無限循環

我認為后者是一個災難性的錯誤。 這可能意味着(無任何警告)每當我在內聯匯編代碼中使用未定義的標簽時(例如由於輸入錯誤),我的軟件中都會出現無限循環。

有什么我可以做的嗎?

如果您不告訴編譯器執行可能不會出現在asm語句的另一端,則編譯器會認為是這種情況。

因此,您的兩個示例都不安全,很幸運第二個示例不會破壞任何內容,因為該函數太簡單了。


我不確定您的代碼是如何編譯的。 C本地標簽通常不會以相同名稱顯示為asm標簽。 如果他們使用的都是由編譯器生成的代碼,GCC使用的名稱,如.L1作為分支目標同它發明了if()for / while循環。 @Kampi使用AtmelStudios 7.0.1931報告源的鏈接器錯誤。

也許您實際上是在查看一個未鏈接的.o ,分支目標只是一個由鏈接器填充的占位符 (並且對未定義符號的引用是等待發生的鏈接器錯誤)。 e0 8f 00 00的編碼肯定適合:匯編器在編譯器提供的.s找不到分支目標標簽,因此它將其視為外部符號,並使用了具有更多位移字節的分支。 顯然,在AVR32上,相對分支位移是相對於分支指令的開始而言的,這與許多ISA相對於分支結束而言的情況不同。 (即,在對指令進行解碼/執行時,PC已遞增。)

這樣就可以說明您缺少鏈接器錯誤(因為您從未運行過鏈接器),並且看到了虛假的分支目標。 更新:這被鏈接的,但到庫中。 因此,庫本身仍具有未解析的符號。

在另一個內聯匯編語句定義的目標存在於編譯器的輸出匯編,所以匯編程序找到它,並可以使用短rjmp

(某些匯編程序通過要求extern foo聲明來幫助您捕獲此類錯誤。GAS不會;它只是假設任何未定義的符號都是extern語法來自傳統的Unix匯編程序,該匯編程序旨在匯編編譯器輸出,而古老的編譯器僅編譯一次只有一個C函數(不是整個文件的優化)不會知道該函數的定義是出現在此.c文件中還是單獨的.c文件中,因此該語法可以在不使用計算機的情況下對C進行一次編譯足夠的內存以返回並為稍后在asm輸出中未定義的符號添加extern聲明。)


GNU C asm goto使此安全

GNU C內聯匯編確實具有用於從內聯匯編語句(跳轉到C標簽)中跳出的語法。 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#GotoLabels 並查看SO: GCC內聯匯編中的標簽的示例。 (使用x86指令,但是asm模板的內容與您使用asm goto語法的方式無關。)

在沒有針對條件代碼/標志輸出的GCC6語法的目標上,使用內聯asm中的條件分支跳轉到some_label: return true;可能是一種便捷的方法some_label: return true; return false; 使用條件標志作為GNU C內聯asm輸出

但是,根據提交消息(說明Linux內核放棄AVR32支持的原因),AVR32 gcc停留在gcc4.2。 asm goto僅出現在gcc4.5中。

除非AtmelStudio編譯器(基於嗎?)是最新的gcc,否則您就無法安全地做到這一點。

暫無
暫無

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

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