【发布时间】:2019-10-28 00:14:59
【问题描述】:
我们正在使用 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 来产生无限循环。
我认为后者是一个灾难性的错误。这可能意味着(没有任何警告)每当我在内联汇编代码中使用未定义的标签时(例如由于拼写错误),我都会在我的软件中得到一个无限循环。
我能做些什么吗?
【问题讨论】:
-
(a) 查看编译器生成的程序集(与 GCC 的
-S开关一样)而不是反汇编可能会有所帮助。 (b) 我不确定是否支持跳出内联汇编。一方面,考虑您列出的被内联汇编修改的寄存器。编译器可能会在内联程序集周围保存和恢复这些寄存器,但跳转会跳过恢复。 -
编译器可能会保存和恢复内联程序集周围的那些寄存器,但跳转会跳过恢复。非常感谢,我还没有考虑过(目前只做基本测试,稍后再做更复杂的测试)。但这不是编译器发出警告的另一个原因吗?奇怪的是,
-S似乎被 AVR32 工具链默默地忽略了,至少当一个库是目标时。几个月前,我已经花了一天时间尝试让 avr32-gcc 生成一个列表文件(.lss),但没有成功,所以我现在使用 avr32-objdump。 -
你还做了什么吗?我无法通过内联汇编程序跳转到已编译的外部标签来获取我的示例。编译器会抛出对该标签
asm volatile("rjmp Label \n" :::"cc", "memory"); Label: return 0;的未定义引用的错误。这只适用于两个内联汇编程序部分。 -
@EricPostpischil:支持跳出内联汇编,但您必须告诉编译器
asm goto的可能性。 (并且您可以使用 C 本地标签)。 gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#GotoLabels -
但是根据提交消息说明 Linux 放弃 AVR32 支持的原因 (github.com/torvalds/linux/commit/…) AVR32 gcc 卡在 gcc4.2,而
asm goto只出现在 gcc4.5。除非 AtmelStudio 编译器更新,否则您根本无法安全地执行此操作。
标签: c gcc inline-assembly goto avr32