C汇编Pushl不适用于写入功能



我使用c启动来调用(_getstdhandle@4(函数以获取(输出(句柄然后使用(_writeFile@20(函数来使用我在台上写下我的字符串,然后使用我获得的句柄来写下我的字符串来自(_getStdhandle@4(。我在我的源代码中使用(PUSHL(来传递参数,但是出了问题,因为(writeFile((函数返回错误(6(,该函数是无效的句柄,但句柄是有效的...因此,传递参数有问题。。...所以我没有使用(r(,但是如果我使用(r(,则该程序可以毫无问题地工作(将参数移至寄存器,然后推动寄存器(我想推动参数而不将其移动到参数寄存器(此代码一无所有,问题来自(WriteFile(函数,如果我将(r(用于(writeFile(参数,则将完成打印,但是为什么我不能使用" g"将参数移至寄存器?

    typedef void * HANDLE;
#define GetStdHandle(result, handle)                                    
    __asm (                                                             
        "pushl  %1nt"                                                 
        "call   _GetStdHandle@4"                                        
            : "=a" (result)                                             
            : "g" (handle))
#define WriteFile(result, handle, buf, buf_size, written_bytes)         
    __asm (                                                             
        "pushl  $0nt"                                                 
        "pushl  %1nt"                                                 
        "pushl  %2nt"                                                 
        "pushl  %3nt"                                                 
        "pushl  %4nt"                                                 
        "call   _WriteFile@20"                                          
            : "=a" (result)                                             
            : "g" (written_bytes), "g" (buf_size), "g" (buf), "g" (handle))
int main()
{
    HANDLE handle;
    int write_result;
    unsigned long written_bytes;
    GetStdHandle(handle, -11);
    if(handle != INVALID_HANDLE_VALUE)
    {
        WriteFile(write_result, handle, "Hello", 5, & written_bytes);
    }
    return 0;
}

此程序的汇编代码是:

.file   "main.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "Hello"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB25:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp
    call    ___main
/APP
    pushl  $-11
    call   _GetStdHandle@4
 # 0 "" 2
/NO_APP
    movl    %eax, 12(%esp)
    cmpl    $-1, 12(%esp)
    je  L2
    leal    4(%esp), %eax
/APP
    pushl  $0
    pushl  %eax
    pushl  $5
    pushl  $LC0
    pushl  12(%esp)
    call   _WriteFile@20
 # 0 "" 2
/NO_APP
    movl    %eax, 8(%esp)
L2:
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE25:
    .ident  "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"

有什么问题?

我会质疑通过这样的包装来调用Winapi的需求,而不是直接打电话给它们。您可以用
声明stdcall调用约定的原型 __attribute__((stdcall))

如果您不需要使用内联装配,则不应该。海湾合作委员会的内联装配很难正确。弄错了它可以使代码看起来正常工作,直到有一天行不通,尤其是在启用了优化的情况下。大卫·沃尔弗德(David Wohlferd(有一篇很好的文章,说明为什么不需要在线装配。


在生成的代码的这一部分中可以看到主要问题:

pushl  $0
pushl  %eax
pushl  $5
pushl  $LC0
pushl  12(%esp)
call   _WriteFile@20

GCC已将第一个参数的内存操作数(句柄(计算为12(%esp)。问题在于,您已经使用了以前的推动更改了ESP ,现在偏移12(%esp)不再是handle所在的位置。

要解决此问题,您可以通过寄存器或直接(如果可能的话(传递内存地址。而不是使用包括m(内存约束(的g约束,而只需将ri用于寄存器和即时。这样可以防止内存操作数生成。如果您通过寄存器将指针传递,您还需要添加"memory" Clobber。

stdcall(winapi(调用惯例允许函数销毁 eax ecx edx (aka the volatile寄存器(。GetStdHandleWriteFile可能会使 ecx edx 以及返回 eax 中的值。您需要确保 ecx edx 也被列为clobbers(或具有将其标记为输出的约束(,否则编译器可以假设这些寄存器中的值在内联块块完成之前和之后,相同。如果它们与众不同,则可能导致细微的错误。

随着这些更改,您的代码可能看起来像:

#define INVALID_HANDLE_VALUE (void *)-1    
typedef void *HANDLE;
#define GetStdHandle(result, handle)                                    
    __asm (                                                             
        "pushl  %1nt"                                                 
        "call   _GetStdHandle@4"                                        
            : "=a" (result)                                             
            : "g" (handle)                                              
            : "ecx", "edx")
#define WriteFile(result, handle, buf, buf_size, written_bytes)         
    __asm __volatile (                                                  
        "pushl  $0nt"                                                 
        "pushl  %1nt"                                                 
        "pushl  %2nt"                                                 
        "pushl  %3nt"                                                 
        "pushl  %4nt"                                                 
        "call   _WriteFile@20"                                          
            : "=a" (result)                                             
            : "ri" (written_bytes), "ri" (buf_size), "ri" (buf), "ri" (handle) 
            : "memory", "ecx", "edx")
int main()
{
    HANDLE handle;
    int write_result;
    unsigned long written_bytes;
    GetStdHandle(handle, -11);
    if(handle != INVALID_HANDLE_VALUE)
    {
        WriteFile(write_result, handle, "Hello", 5, &written_bytes);
    }
    return 0;
}

注释

  • 我将WriteFile内联装配标记为__volatile,以便如果优化器认为不使用result,则优化器将无法删除整个内线组件。编译器不知道该函数的副作用是显示显示。标记功能挥发性,以防止直列组件被完全删除。

  • GetStdHandle在潜在内存操作数上没有问题,因为在初始push %1之后没有进一步使用约束。您遇到的问题仅是一个问题,当已修改了 ESP (通过推送/pop或直接更改为ESP (,并且可能会使用内存约束之后在该内联汇编中。

相关内容

  • 没有找到相关文章