![](/img/trans.png)
[英]Atmelstudio UC3C AVR32 - framework objects in wrong place in memory?
[英]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
聲明。)
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.