为什么编译器似乎在我的函数中重新利用了 Argc 和 Argv?

问题描述 投票:0回答:1

main.cpp代码:

#include <iostream>

int main()
{
    std::cout << "Hello World!\n";
}

Ghidra 中的拆卸:

                     *************************************************************
                     *                           FUNCTION                          
                     *************************************************************
                     int  __cdecl  main (int  _Argc , char * *  _Argv , char * *  _E
                       assume GS_OFFSET = 0xff00000000
     int               EAX:4          <RETURN>
     int               ECX:4          _Argc
     char * *          RDX:8          _Argv
     char * *          R8:8           _Env
     undefined1        Stack[-0x10]:1 local_10                                XREF[1]: 

    140012292 (*)   
         undefined1        Stack[-0xd8]:1 local_d8                                XREF[1]:     14001226a (*)   
                         main                                            XREF[1]:     main:1400112cb (T) , 
                                                                                      main:1400112cb (j)   
   140012260 40  55           PUSH       RBP
   140012262 57              PUSH       RDI
   140012263 48  81  ec       SUB        RSP ,0xe8
             e8  00  00  00
   14001226a 48  8d  6c       LEA        RBP =>local_d8 ,[RSP  + 0x20 ]
             24  20
   14001226f 48  8d  0d       LEA        _Argc ,[__6AFE2A9E_TestApplication@cpp ]          = 01h
             f0  0d  01  00
   140012276 e8  63  f1       CALL       __CheckForDebuggerJustMyCode                     void __CheckForDebuggerJustMyCod
             ff  ff
   14001227b 90              NOP
   14001227c 48  8d  15       LEA        _Argv ,[s_Hello_World!_ ]                         = "Hello World!\n"
             a5  89  00  00
   140012283 48  8b  0d       MOV        _Argc ,qword ptr [->MSVCP140D.DLL::std::cout ]    = 00021d22
             0e  ef  00  00
   14001228a e8  f8  ed       CALL       std::operator<<<>                                basic_ostream<char,std::char_tra
             ff  ff
   14001228f 90              NOP
   140012290 33  c0           XOR        EAX ,EAX
   140012292 48  8d  a5       LEA        RSP =>local_10 ,[RBP  + 0xc8 ]
             c8  00  00  00
   140012299 5f              POP        RDI
   14001229a 5d              POP        RBP
   14001229b c3              RET

尝试理解我为在 Ghidra 中练习反汇编而编写的这个非常简单的程序。

序言对我来说很有意义。将之前的

RBP
压入堆栈,将
RSP
减去
0xE8
(232 字节)来分配当前堆栈帧。

地址

RSP+0x20
存储在堆栈上的
RBP+0xD8
中,但随后事情对我来说就不再有意义了......

一个地址被加载到

_Argc
...
"Hello, World!\n"
的地址被加载到
_Argv
,这是一个
char**
,所以是一个指向字符数组的指针...然后是一个四字指针(8字节),因为这个是一个 64 位应用程序,被加载到
_Argc
(参数计数),它是一个整数或 4 字节变量,然后(或多或少)调用
std::cout

为什么局部变量

_Argc
_Argv
在这里被重新调整用途来保存其他局部变量,其中一个是不同的且更大的数据类型?我完全误读了吗?我无法真正理解它,我唯一的假设是它是一些编译器优化魔法。

这对我来说是有意义的,只是吐槽,

std::cout
取决于它的调用约定,需要
RDX
_Argv
)中的字符数组来保存要输出的字符串。还要用
ECX
_Argc
)来保存要输出的字符串数量?

我可能完全错了。只是想了解一下 Visual Studio 2022 生成的程序集。任何帮助或见解将不胜感激。我在谷歌上的尝试没有结果。

反汇编程序是否也可能使用

_Argc
_Argv
代替
ECX
RDX
,因为这些寄存器用于将相应的参数传递给当前函数?感谢您的帮助。

c++ assembly disassembly
1个回答
0
投票

因为它可以。编译器没有为临时变量保留新的空间,而是发现 argc 和 argv 从未使用过,并重新调整了它们的槽的用途。事实上,编译器足够聪明,可以在上次使用后执行此操作。

几年前,linux 内核邮件列表上存在一些争论,他们希望使用一个方法标记来阻止编译器执行此操作,因为它会导致系统调用门出现问题。 (通过将所有寄存器压入系统调用门来解决问题......)

© www.soinside.com 2019 - 2024. All rights reserved.