繁体   English   中英

GCC / x86内联asm:你怎么告诉gcc内联汇编部分会修改%esp?

[英]GCC/x86 inline asm: How do you tell gcc that inline assembly section will modify %esp?

在试图让一些旧代码再次运行时( 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&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)

问题:变量( identificationprotocol_name )位于调用上下文的堆栈中。 因此gcc (优化结果,不确定是否重要)将从那里获取值并将其交给内联asm部分。 但是由于我在堆栈上推送东西, gcc计算的偏移量将在第三次调用中偏离8( pushl 48(%esp) )。 :)

这花了我很长时间才弄清楚,起初并不是很明显。

最简单的方法当然是使用r输入约束,以确保该值在寄存器中。 但还有另一种更好的方法吗? 一个显而易见的方法当然是重写整个系统调用接口,而不是首先在堆栈上推送东西(而是使用寄存器,比如Linux),但这不是我今晚想做的重构......

有没有办法告诉gcc inline asm“堆栈是不稳定的”? 你们过去一直在处理这样的事情?


同一天晚上更新 :我确实找到了相关的gcc ML线程( https://gcc.gnu.org/ml/gcc-help/2011-06/msg00206.html ),但它似乎没有帮助。 似乎在clobber列表中指定%esp 应该使它从%ebp做偏移,但它不起作用,我怀疑-O2 -fomit-frame-pointer在这里有效。 我启用了这两个标志。

什么有用,有什么不可用:

  1. 我试着省略-fomit-frame-pointer 没有任何影响。 我将%espespspclobbers列表中

  2. 我试着省略-fomit-frame-pointer-O3 这实际上产生了有效的代码,因为它依赖于%ebp而不是%esp

     pushl 16(%ebp) pushl 12(%ebp) pushl 8(%ebp) lcall $456, $0 
  3. 我尝试在命令行中指定-O3和not -fomit-frame-pointer 创建坏的,破坏的代码(依赖于%esp在整个程序集块中保持不变,即没有堆栈帧)。

  4. 我尝试跳过-fomit-frame-pointer并使用-O2 代码破碎,没有堆栈框架。

  5. 我尝试使用-O1 代码破碎,没有堆栈框架。

  6. 我尝试添加cc作为clobber。 没有办法,没有任何区别。

  7. 我尝试将输入约束更改为ri ,给出下面的输入和输出代码。 这当然有效,但比我希望的稍微不那么优雅。 然后, 完美是好的敌人所以也许我现在必须忍受这个。

输入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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM