当前位置: 首页 > 知识库问答 >
问题:

gcc/x86内联ASM:如何告诉gcc内联汇编部分将修改%ESP?

江煜
2023-03-14

在尝试让一些旧代码重新工作时(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计算的偏移量将在第三次调用(pushl48(%ESP))时减少8。:)

这花了我很长时间才弄明白,一开始对我来说并不是很明显。

当然,最简单的方法是使用r输入约束,以确保该值位于寄存器中。但还有其他更好的办法吗?当然,一个明显的方法是重写整个系统调用接口,以不在堆栈上推入任何东西(并使用寄存器代替,例如Linux),但这不是我今晚想要做的重构...

当天晚上更新:我确实找到了一个相关的gccML线程(https://gcc.gnu.org/ML/gcc-help/2011-06/msg00206.html),但似乎没有什么帮助。似乎在clobber列表中指定%ESP应该会使它与%EBP产生偏移量,但这并不起作用,我怀疑-o2-fomit-frame-pointer在这里有作用。我已经启用了这两个标志。

共有1个答案

顾穆冉
2023-03-14

什么有效什么无效:

>

  • 我尝试省略-fomit-frame-pointer。没有任何影响。我将%ESPESPSP包含在clobbers列表中。

    我尝试省略-fomit-frame-pointer-o3。这实际上生成了可以工作的代码,因为它依赖于%ebp而不是%esp

    pushl 16(%ebp)
    pushl 12(%ebp)
    pushl 8(%ebp)
    lcall $456, $0
    

    我尝试跳过-fomit-frame-pointer而只使用-o2。破译的代码,没有堆栈帧。

    我尝试使用-o1。破译的代码,没有堆栈帧。

    我尝试将cc添加为clobber。不行,没有任何区别。

    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;
    }
    
    #APP
    # 392 "../system/system_calls.h" 1
    pushl %esi
    pushl %eax
    pushl %ebx
    lcall $456, $0
    

  •  类似资料:
    • 我正在尝试同时处理MSVC和GCC编译器,同时更新这个代码库以在GCC上工作。但我不确定GCCs内联ASM是如何工作的。现在我并不擅长将ASM转换为C,否则我就会使用C而不是ASM。 我假设ROR13的工作方式类似于,但代码不会产生相同的输出。 什么是将这个内联ASM翻译成GCC的正确方法,或者这个代码的C翻译是什么?

    • GCC扩展内联汇编 使用GCC扩展内联汇编的例子如下: #define read_cr0() ({ \ unsigned int __dummy; \ __asm__( \ "movl %%cr0,%0\n\t" \ :"=r" (__dummy)); \ __dummy; \ }) 它代表什么含义呢?这需要从其基本格式讲起。GCC扩展内联汇编的基本格式是: asm [volat

    • GCC基本内联汇编 GCC 提供了两内内联汇编语句(inline asm statements):基本内联汇编语句(basic inline asm statement)和扩展内联汇编语句(extended inline asm statement)。GCC基本内联汇编很简单,一般是按照下面的格式: asm("statements"); 例如: asm("nop"); asm("

    • 谢谢你的帮助。

    • 问题内容: 溢出, 如何仅使用内联汇编实现putchar(char)过程?我想在x86-64汇编中做到这一点。我这样做的原因是实现我自己的标准库(或至少一部分)。这是我到目前为止的内容: 我正在编译: 感谢您的帮助! 问题答案: 使用GNU C内联asm时,请 使用约束来告诉编译器您想要的东西 ,而不是使用asm模板中的指令“手动”进行。 对于和,我们只需要为模板,用约束来设置所有寄存器输入(与所

    • 问题内容: 是否可以从内联汇编块中使用syscall编写单个字符?如果是这样,怎么办?它应该看起来像“东西”: $ 80是ascii中的“ P”,但是什么也不会返回。 任何建议,不胜感激! 问题答案: 就像是 添加 :请注意,我曾经将char的有效地址加载到寄存器中;对于我尝试$ 0和$ 1的价值,它似乎仍然可以工作… 避免使用外部字符 注意:它之所以有效是因为Intel处理器的字节序!:D