在尝试让一些旧代码再次运行时(https://github.com/chaos4ever/chaos/blob/master/libraries/system/system_calls.h#L387,FWIW)我发现gcc
的一些语义似乎在最近的10中以一种非常微妙但仍然危险的方式发生了变化 - 15年......:P
以前用于gcc
旧版本的代码,如2.95。无论如何,这是代码:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "g" (identification),
"g" (service_parameter),
"g" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
上面代码的问题是gcc
(在我的情况下是4.7)会将其编译为以下asm代码(AT&amp; T语法):
# 392 "../system/system_calls.h" 1
pushl 68(%esp) # This pointer (%esp + 0x68) is valid when the inline asm is entered.
pushl %eax
pushl 48(%esp) # ...but this one is not (%esp + 0x48), since two dwords have now been pushed onto the stack, so %esp is not what the compiler expects it to be
lcall $456, $0
# Restoration of %esp at this point is done in the called method (i.e. lret $12)
问题:变量(identification
和protocol_name
)位于调用上下文的堆栈中。所以gcc
(优化结果,不确定是否重要)将从那里获取值并将其交给内联asm部分。 但,因为我在堆栈上推送内容,gcc
计算的偏移量将在第三次调用(pushl 48(%esp)
)中偏离8。 :)
我花了很长时间才弄明白,起初对我来说并不是很明显。
最简单的方法当然是使用r
输入约束,以确保该值在寄存器中。但还有另一种更好的方法吗?一个显而易见的方法当然是重写整个系统调用接口,而不是首先在堆栈上推送东西(而是使用寄存器,比如Linux),但这不是我今晚想做的重构...
有没有办法告诉gcc
inline asm&#34;堆栈是不稳定的&#34;?你们过去一直在处理这样的事情吗?
当晚晚些时候更新:我确实找到了一个相关的gcc
ML帖子(https://gcc.gnu.org/ml/gcc-help/2011-06/msg00206.html),但它似乎没有帮助。似乎在clobber列表中指定%esp
应该使其从%ebp
做偏移,但它不起作用,我怀疑-O2 -fomit-frame-pointer
在这里有效果。我启用了这两个标志。
答案 0 :(得分:3)
什么有效,什么无效:
我尝试省略-fomit-frame-pointer
。没有任何影响。我在list of clobbers中添加了%esp
,esp
和sp
。
我尝试省略-fomit-frame-pointer
和-O3
。这实际上产生了有效的代码,因为它依赖于%ebp
而不是%esp
。
pushl 16(%ebp)
pushl 12(%ebp)
pushl 8(%ebp)
lcall $456, $0
我尝试在命令行中指定-O3
而不是-fomit-frame-pointer
。创建坏的,破坏的代码(依赖于%esp
在整个程序集块中保持不变,即没有堆栈框架。
我尝试跳过-fomit-frame-pointer
并使用-O2
。代码破碎,没有堆叠框架。
我尝试使用-O1
。代码破碎,没有堆叠框架。
我尝试添加cc
作为clobber。不能做,也没有任何区别。
我尝试将输入约束更改为ri
,并输入&amp;输出代码如下。这当然有效但是比我希望的稍微不那么优雅。再说一次,perfect is the enemy of good所以也许我现在必须忍受这个。
输入C代码:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "ri" (identification),
"ri" (service_parameter),
"ri" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
输出asm代码。可以看出,使用寄存器代替应该始终是安全的(但由于编译器必须移动东西,可能性能稍差):
#APP
# 392 "../system/system_calls.h" 1
pushl %esi
pushl %eax
pushl %ebx
lcall $456, $0