#include <iostream>
int foo(int a, int b, int c, int d) {
int e;
int f;
std::cout << std::hex;
std::cout << "Address of a: " << &a << std::endl;
std::cout << "Address of b: " << &b << std::endl;
std::cout << "Address of c: " << &c << std::endl;
std::cout << "Address of d: " << &d << std::endl;
std::cout << "Address of e: " << &e << std::endl;
std::cout << "Address of f: " << &f << std::endl;
return a + b + c + d;
}
int main() {
foo(4, 2, 3, 4);
return 0;
}
输出:
gcc 4.9.2 -32bit release:
Address of a: 0x6efea0
Address of b: 0x6efea4
Address of c: 0x6efea8
Address of d: 0x6efeac
Address of e: 0x6efe8c
而就我目前的知识了解,栈是从高地址到底地址存储数据,而读取参数的顺序为从右到左,以第一次函数调用为例,入栈顺序应该是:d-c-b-a之后是按顺序将局部变量入栈,即e-f,但是比较e和a的地址可以发现二者在32位下相差14个字节,在64位下相差36个字节,感到比较奇怪,按之前看到的文章:http://blog.csdn.net/tdgx2004...,在局部变量与参数之间应该是函数地址与保护栈底(32位下8个字节),但是实际情况是14个字节,非常好奇还有6个字节装的是什么?
求教大神们。
下面是foo函数的汇编:
foo(int,int,int,int)
0x00401500 <+0>: push %ebp
0x00401501 <+1>: mov %esp,%ebp
0x00401503 <+3>: sub $0x28,%esp
0x00401506 <+6>: movl $0x479590,(%esp)
0x0040150d <+13>: mov $0x488140,%ecx
0x00401512 <+18>: call 0x451580 <_ZNSolsEPFRSt8ios_baseS0_E>
0x00401517 <+23>: sub $0x4,%esp
0x0040151a <+26>: movl $0x489000,0x4(%esp)
0x00401522 <+34>: movl $0x488140,(%esp)
0x00401529 <+41>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x0040152e <+46>: lea 0x8(%ebp),%ecx
0x00401531 <+49>: mov %ecx,(%esp)
0x00401534 <+52>: mov %eax,%ecx
0x00401536 <+54>: call 0x4515c0 <_ZNSolsEPKv>
0x0040153b <+59>: sub $0x4,%esp
0x0040153e <+62>: movl $0x4795c0,(%esp)
0x00401545 <+69>: mov %eax,%ecx
0x00401547 <+71>: call 0x451570 <_ZNSolsEPFRSoS_E>
0x0040154c <+76>: sub $0x4,%esp
0x0040154f <+79>: movl $0x48900f,0x4(%esp)
0x00401557 <+87>: movl $0x488140,(%esp)
0x0040155e <+94>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x00401563 <+99>: mov %eax,%edx
0x00401565 <+101>: lea 0xc(%ebp),%eax
0x00401568 <+104>: mov %eax,(%esp)
0x0040156b <+107>: mov %edx,%ecx
0x0040156d <+109>: call 0x4515c0 <_ZNSolsEPKv>
0x00401572 <+114>: sub $0x4,%esp
0x00401575 <+117>: movl $0x4795c0,(%esp)
0x0040157c <+124>: mov %eax,%ecx
0x0040157e <+126>: call 0x451570 <_ZNSolsEPFRSoS_E>
0x00401583 <+131>: sub $0x4,%esp
0x00401586 <+134>: movl $0x48901e,0x4(%esp)
0x0040158e <+142>: movl $0x488140,(%esp)
0x00401595 <+149>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x0040159a <+154>: mov %eax,%edx
0x0040159c <+156>: lea 0x10(%ebp),%eax
0x0040159f <+159>: mov %eax,(%esp)
0x004015a2 <+162>: mov %edx,%ecx
0x004015a4 <+164>: call 0x4515c0 <_ZNSolsEPKv>
0x004015a9 <+169>: sub $0x4,%esp
0x004015ac <+172>: movl $0x4795c0,(%esp)
0x004015b3 <+179>: mov %eax,%ecx
0x004015b5 <+181>: call 0x451570 <_ZNSolsEPFRSoS_E>
0x004015ba <+186>: sub $0x4,%esp
0x004015bd <+189>: movl $0x48902d,0x4(%esp)
0x004015c5 <+197>: movl $0x488140,(%esp)
0x004015cc <+204>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x004015d1 <+209>: mov %eax,%edx
0x004015d3 <+211>: lea 0x14(%ebp),%eax
0x004015d6 <+214>: mov %eax,(%esp)
0x004015d9 <+217>: mov %edx,%ecx
0x004015db <+219>: call 0x4515c0 <_ZNSolsEPKv>
0x004015e0 <+224>: sub $0x4,%esp
0x004015e3 <+227>: movl $0x4795c0,(%esp)
0x004015ea <+234>: mov %eax,%ecx
0x004015ec <+236>: call 0x451570 <_ZNSolsEPFRSoS_E>
0x004015f1 <+241>: sub $0x4,%esp
0x004015f4 <+244>: movl $0x48903c,0x4(%esp)
0x004015fc <+252>: movl $0x488140,(%esp)
0x00401603 <+259>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x00401608 <+264>: mov %eax,%edx
0x0040160a <+266>: lea -0xc(%ebp),%eax
0x0040160d <+269>: mov %eax,(%esp)
0x00401610 <+272>: mov %edx,%ecx
0x00401612 <+274>: call 0x4515c0 <_ZNSolsEPKv>
0x00401617 <+279>: sub $0x4,%esp
0x0040161a <+282>: movl $0x4795c0,(%esp)
0x00401621 <+289>: mov %eax,%ecx
0x00401623 <+291>: call 0x451570 <_ZNSolsEPFRSoS_E>
0x00401628 <+296>: sub $0x4,%esp
0x0040162b <+299>: movl $0x48904b,0x4(%esp)
0x00401633 <+307>: movl $0x488140,(%esp)
0x0040163a <+314>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x0040163f <+319>: mov %eax,%edx
0x00401641 <+321>: lea -0x10(%ebp),%eax
0x00401644 <+324>: mov %eax,(%esp)
0x00401647 <+327>: mov %edx,%ecx
0x00401649 <+329>: call 0x4515c0 <_ZNSolsEPKv>
0x0040164e <+334>: sub $0x4,%esp
0x00401651 <+337>: movl $0x4795c0,(%esp)
0x00401658 <+344>: mov %eax,%ecx
0x0040165a <+346>: call 0x451570 <_ZNSolsEPFRSoS_E>
0x0040165f <+351>: sub $0x4,%esp
0x00401662 <+354>: mov 0x8(%ebp),%edx
0x00401665 <+357>: mov 0xc(%ebp),%eax
0x00401668 <+360>: add %eax,%edx
0x0040166a <+362>: mov 0x10(%ebp),%eax
0x0040166d <+365>: add %eax,%edx
0x0040166f <+367>: mov 0x14(%ebp),%eax
0x00401672 <+370>: add %edx,%eax
=> 0x00401674 <+372>: leave
0x00401675 <+373>: ret
LZ, 어셈블리 코드 일부를 넣고 컴파일할 때 -S만 추가하는 것이 좋습니다.
으아아아귀하의 코드를 실행했는데 결과는 다음과 같습니다.
e와 a의 차이는 0x10으로 십진수로는 16입니다.
귀하의 코드를 사용하여 어셈블리 조각을 생성했는데 foo()의 매개변수가 스택에 푸시되지 않은 대신 매개변수가 먼저 레지스터에 제공된 다음 레지스터 값이 스택에 저장되었음을 발견했습니다. , 따라서 귀하의 상황과 유사합니다. 다르게는 a의 주소가 d의 주소보다 높고 이러한 매개변수의 주소가 지역 변수보다 작은 것을 알 수 있습니다.
으아아아 으아아아이 문제는 플랫폼, 호출 규칙, 컴파일러와 관련이 있습니다.
i386 플랫폼은 기본 _cdecl 호출 규칙에 따라 런타임 스택에서 함수 매개변수와 함수 로컬 변수 사이에 1: 반환 주소(호출 명령 후 PC), 2: 호출자의 스택 프레임 기본 주소(ebp), 3: 호출 수신자 저장 레지스터(플랫폼에 따라 숫자가 다름), 4: 예외를 처리하기 위해 스택에 정보가 추가됨(컴파일러마다 다른 것을 구현할 수 있으며 gcc에서는 항목을 추가함)