gpt4 book ai didi

c++ - C++函数调用的参数推送顺序不反射(reflect)参数在堆栈中的地址

转载 作者:行者123 更新时间:2023-11-27 23:27:11 25 4
gpt4 key购买 nike

我做了一个实验:

class A {
public:
A() {}
A(const A& a) {
printf("A - %p\n", this);
}
};

class B {
public:
B() {}
B(const B& b) {
printf("B - %p\n", this);
}
};

void func(A a, B b) {}

int main() {
A a;
B b;
func(a, b);
return 0;
}

输出是:

B - 0x7fff636e2c48
A - 0x7fff636e2c50

既然参数是从右向左压入的,为什么B的地址比A低?使困惑。 (堆栈从较高地址开始)。

最佳答案

简而言之:func 的参数从右到左正确地“插入”堆栈;您打印的不是这些参数的堆栈地址,而是这些参数的,这些参数也恰好是堆栈上的某些地址。

稍微详细一点...

首先,您在 x64 机器上。你应该忘记 _cdecl , _stdcall等调用约定。只有一个调用约定(简化版本):前四个函数参数(在函数调用中从左到右列出)将在寄存器上传递,其余的在堆栈上传递。现在,也就是说,调用者需要在其自己的堆栈上分配足够的“主空间”,因此被调用者可以将传递给寄存器的参数“溢出”到那里。因此,原则上,如果被调用方决定通过“主空间”使用堆栈,那么前四个参数也可以在堆栈中找到。

其次,如果在寄存器上传递的参数被被调用者“溢出”,它们仍然会从右到左“压入”堆栈:被调用者将使用从右到左的顺序将寄存器“​​溢出”到“主空间”,这意味着第一个函数参数最终将在“主空间”区域中的堆栈地址低于第二个参数。因此,在这方面,参数总是从右到左“压入”堆栈。

第三,您的输出与参数传递给 func 的方式无关。 ,但在创建临时文件的位置。这是发生了什么:main保留了足够的堆栈空间来创建临时文件 ab ; b首先在 main 堆栈的较低地址创建,这很好,因为在函数调用 A(const A&) 中调用函数的顺序(复制构造函数 B(const B&)func ,在我们的例子中)明确未定义;临时地址ab存储在寄存器中(并且有足够的“家庭空间”用于“溢出” func 已经保留); func叫做; func可以将寄存器“​​溢出”到“主空间”;如果它“溢出”它们,则地址为b将被“溢出”到比a的地址所在的堆栈地址更高的堆栈地址处的堆栈中将被“溢出”- 这是传递参数的从右到左的顺序。

这是一些代码和相应的汇编器。请注意,我使用修改后的代码(更多参数和两个函数)来展示参数是如何传递的。函数funcIint参数是为了说明要点,而不会调用复制构造函数。具有两个参数的函数版本本质上是发布的 funcC 的“切片”版本- 它被“切片”为处理 RCX 的代码和 RDX注册(到最后一个)。另外,请注意 RSP包含堆栈指针和内部 funcIfuncC小于 RSP里面main通过 8 (这解释了检索 funcIfuncC 最后两个压入堆栈的参数时的偏移量):

class A
{
public:
int mData;
A()
{ // mov qword ptr [rsp+8],rcx ; "spilling" rcx
} // mov rax,qword ptr [rsp+8] ; RAX (return value) = pointer to the object;
A( const A& )
{// mov qword ptr [rsp+10h],rdx ;"spilling" RDX and RCX
// mov qword ptr [rsp+8],rcx
}// mov rax,qword ptr [rsp+8] ; notice, RAX has value of RCX
};

class B
{
public:
int mData;
B()
{ // mov qword ptr [rsp+8],rcx ; "spilling" rcx
} // mov rax,qword ptr [rsp+8] ; RAX (return value) = pointer to the object
B( const B& )
{// mov qword ptr [rsp+10h],rdx ;"spilling" RDX and RCX
// mov qword ptr [rsp+8],rcx
}// mov rax,qword ptr [rsp+8] ; notice, RAX has value of RCX
};

void __cdecl funcI( int a, int b, int c, int d, int e, int g )
{ // mov dword ptr [rsp+20h],r9d ; "spilling" registers, notice right-to-left order
// mov dword ptr [rsp+18h],r8d
// mov dword ptr [rsp+10h],edx
// mov dword ptr [rsp+8],ecx
a = 1; // mov dword ptr [rsp+8],1 ; accessing first 4 "spilled" function parameters
b = 2; // mov dword ptr [rsp+10h],2 ; notice, first function parameter has lower stack address than second parameter etc
c = 3; // mov dword ptr [rsp+18h],3 ; so parameters pushed right-to-left by callee's "spilling"
d = 4; // mov dword ptr [rsp+20h],4 ;
e = 5; // mov dword ptr [rsp+28h],5 ; here, accessing 2 parameters pushed on stack explicitly by caller
g = 6; // mov dword ptr [rsp+30h],6 ; they were pushed right-to-left
// also notice the offset of 8 in RSP ("g" is accessed through [rsp+30h] while it was put into [rsp+28h] in main
} // ret


void __cdecl funcC( A a1, A a2, A a3, A a4, A a5, B b1 )
{// mov qword ptr [rsp+20h],r9 ; "spilling"
// mov qword ptr [rsp+18h],r8
// mov qword ptr [rsp+10h],rdx
// mov qword ptr [rsp+8],rcx
a1.mData = 1;
// mov rax,qword ptr [rsp+8] ; same right-to-left order: a1 itself has lower stack address 'rsp+8' than a2 'rsp+18h'
// mov dword ptr [rax],1 ; HOWEVER, stack address value 'rsp+88h' stored in a1 is HIGHER than stack address value 'rsp+78h' stored in a2!!!!
a2.mData = 2;
// mov dword ptr [rax],2
// mov rax,qword ptr [rsp+18h]
a3.mData = 3;
// mov rax,qword ptr [rsp+18h]
// mov dword ptr [rax],3
a4.mData = 4;
// mov rax,qword ptr [rsp+20h]
// mov dword ptr [rax],4
a5.mData = 5;
// mov rax,qword ptr [rsp+28h]
// mov dword ptr [rax],5
b1.mData = 6;
// mov rax,qword ptr [rsp+30h]
// mov dword ptr [rax],6
} // ret

int main( void )
{
// sub rsp,0C8h ; reserving stack for `main`
A a;
// lea rcx,[rsp+30h] ; putting into RCX stack address of local 'a'
// call A::A()
B b;
// lea rcx,[rsp+34h] ; putting into RCX stack address of local 'b'
// call B::B()

funcI( 1, 2, 3, 4, 5, 6 );
// mov dword ptr [rsp+28h],6 ; passing parameters to `funcI`
// mov dword ptr [rsp+20h],5 ; last 2 on stack, first 4 on registers
// mov r9d,4 ; notice the right-to-left order:
// mov r8d,3 ; "6" is put on stack at higher address than "5" etc.
// mov edx,2 ; notice also that "6" is put into [rsp+28h]: during call to `funcI` RSP will be less by 8
// mov ecx,1 ; and inside `funcI` parameter will be accessed accordingly through [rsp+30h]
// call funcI
funcC( a, a, a, a, a, b );
// lea rax,[rsp+38h] ; some preparations: putting stack addresses of temporaries into stack variables
// mov qword ptr [rsp+40h],rax ; there are few indirections in debug mode, we can ignore them, noticing the addresses
// lea rax,[rsp+48h]
// mov qword ptr [rsp+50h],rax
// lea rax,[rsp+58h]
// mov qword ptr [rsp+60h],rax
// lea rax,[rsp+68h]
// mov qword ptr [rsp+70h],rax
// lea rax,[rsp+78h]
// mov qword ptr [rsp+80h],rax
// lea rax,[rsp+88h]
// mov qword ptr [rsp+90h],rax

// lea rdx,[rsp+34h] ; putting into RDX stack address of local 'b'
// mov rcx,qword ptr [rsp+40h] ; putting into RCX stack address of temporary - this is right-most temporary of type `B` in `funcC`:
// call B::B(const B&) ; [rsp+40h] = (rsp+38h) which is stack address of temporary `b`
// mov qword ptr [rsp+98h],rax ; putting on stack value of RAX: [rsp+98h] now contains (rsp+38h)


// lea rdx,[rsp+30h]
// mov rcx,qword ptr [rsp+50h]
// call A::A(const B&)
// mov qword ptr [rsp+0A0h],rax

// lea rdx,[rsp+30h]
// mov rcx,qword ptr [rsp+60h]
// call A::A(const A&)
// mov qword ptr [rsp+0A8h],rax

// lea rdx,[rsp+30h]
// mov rcx,qword ptr [rsp+70h]
// call A::A(const A&)
// mov qword ptr [rsp+0B0h],rax

// lea rdx,[rsp+30h]
// mov rcx,qword ptr [rsp+80h]
// call A::A(const A&)
// mov qword ptr [rsp+0B8h],rax

// lea rdx,[rsp+30h]
// mov rcx,qword ptr [rsp+90h]
// call A::A(const A&) ; notice, RAX is not copied onto stack, it's preserved (see below)

// mov rcx,qword ptr [rsp+98h] ; passing parameters (ignoring indirections): putting stack address of right-most temporary `b` onto stack
// mov qword ptr [rsp+28h],rcx ; notice stack address 'rsp+28h' where stack address of 'b' 'rsp+38h' is put
// mov rcx,qword ptr [rsp+0A0h] ; same for right-most temporary of type 'A' (indirection again)
// mov qword ptr [rsp+20h],rcx ; stack address 'rsp+20h' where stack address 'rsp+48h' of right-most `a` is put
// mov rcx,qword ptr [rsp+0A8h] ; NOW: this is RIGHT-TO-LEFT order - take a closer look:
// mov r9,rcx ; rsp+28h > rsp+20h but value [rsp+28h] = rsp+38h < rsp+48h = [rsp+20h]
// mov rcx,qword ptr [rsp+0B0h] ; parameters are pushed right-to-left, parameters' values are not ordered this way (and, really, can be anything)
// mov r8,rcx ; other 4 parameters are put on registers
// mov rcx,qword ptr [rsp+0B8h]
// mov rdx,rcx
// mov rcx,rax ; notice, preserved RAX is put into RCX - it has stack address of first temporary of type 'A'

// call funcC
return ( 0 ); // xor eax,eax ; return value is 0
}
// add rsp,0C8h ; restoring stack
// ret

关于c++ - C++函数调用的参数推送顺序不反射(reflect)参数在堆栈中的地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8496384/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com