Tom*_*ilä 4 c assembly gcc arm
我的C代码片段获取参数的地址并将其存储在易失性存储器位置(预处理代码):
void foo(unsigned int x) {
*(volatile unsigned int*)(0x4000000 + 0xd4) = (unsigned int)(&x);
}
int main() {
foo(1);
while(1);
}
Run Code Online (Sandbox Code Playgroud)
我使用SVN版本的GCC来编译这段代码.在函数结束时,foo
我希望将值1
存储在堆栈中,并且在0x40000d4
指向该值的地址处.当我使用标志进行编译时没有进行优化时-O0
,我得到了预期的ARM7TMDI程序集输出(为方便起见而注释):
.align 2
.global foo
.type foo, %function
foo:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
sub sp, sp, #8
str r0, [sp, #4] @ 3. Store the argument on the stack
mov r3, #67108864
add r3, r3, #212
add r2, sp, #4 @ 4. Address of the stack variable
str r2, [r3, #0] @ 5. Store the address at 0x40000d4
add sp, sp, #8
bx lr
.size foo, .-foo
.align 2
.global main
.type main, %function
main:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, lr}
mov r0, #1 @ 1. Pass the argument in register 0
bl foo @ 2. Call function foo
.L4:
b .L4
.size main, .-main
.ident "GCC: (GNU) 4.4.0 20080820 (experimental)"
Run Code Online (Sandbox Code Playgroud)
它清楚地将参数存储在堆栈中,并从那里存储它0x40000d4
.当我使用优化进行编译时-O1
,我得到了一些意想不到的东西:
.align 2
.global foo
.type foo, %function
foo:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
sub sp, sp, #8
mov r2, #67108864
add r3, sp, #4 @ 3. Address of *something* on the stack
str r3, [r2, #212] @ 4. Store the address at 0x40000d4
add sp, sp, #8
bx lr
.size foo, .-foo
.align 2
.global main
.type main, %function
main:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, lr}
mov r0, #1 @ 1. Pass the argument in register 0
bl foo @ 2. Call function foo
.L4:
b .L4
.size main, .-main
.ident "GCC: (GNU) 4.4.0 20080820 (experimental)"
Run Code Online (Sandbox Code Playgroud)
这次参数永远不会存储在堆栈中,即使堆栈中的某些内容仍然存储在堆栈中0x40000d4
.
这只是预期/未定义的行为吗?我做错了什么或者我实际上发现了Compiler Bug™?
一旦你返回foo()
,x
就会消失,任何指针都无效.随后使用这样的指针会导致C标准喜欢称之为"未定义的行为",这意味着绝对允许编译器假定您不会取消引用它,或者(如果您坚持要这样做)无需生成代码像你期望的那样远程做任何事情.如果你希望指针x
在foo()
返回后保持有效,你就不能x
在foo的堆栈上分配期限 - 即使你原则上知道没有任何理由可以破坏它 - 因为在C中不允许这样做,无论多久发生一次你的期望.
最简单的解决方案可能是x
在main()
(或在任何其他函数中具有足够长寿的范围)中创建局部变量并将地址传递给foo.您还可以创建x
一个全局变量,或者使用它在堆上分配它malloc()
,或者以更奇怪的方式为它预留内存.您甚至可以尝试找出堆栈顶部的某些(希望)更便携的方式,并明确地将数据存储在堆栈的某些部分,如果您确定不需要其他任何东西而且您我确信这是你真正需要做的.但是,正如您所发现的那样,您一直使用的方法并不够可靠.
spa*_*kes -2
\n\n\n这只是预期/未定义\n 的行为吗?我是否做错了什么\n或者我实际上发现了编译器\n Bug\xe2\x84\xa2?
\n
没有错误,只是优化选项可以生成可能无法工作的奇怪代码的定义行为:)
\n\n编辑:
\n\n如果你认为你发现了 GCC 中的错误,邮件列表会很高兴你能过来,但通常他们会发现你的知识中存在一些漏洞,并无情地指责和嘲笑:(
\n\n在这种情况下,我认为可能是 -O 选项尝试使用快捷方式破坏了需要解决的代码。
\n 归档时间: |
|
查看次数: |
1360 次 |
最近记录: |