#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
阿神2017-04-17 15:41:09
LZ, you’d better put a piece of your assembly code and just add -S when compiling.
I ran your code and my output is as follows:
Address of a: 0x7fff3e06b44c
Address of b: 0x7fff3e06b448
Address of c: 0x7fff3e06b444
Address of d: 0x7fff3e06b440
Address of e: 0x7fff3e06b45c
Address of f: 0x7fff3e06b458
The difference between e and a is 0x10, which is 16 in decimal.
I used your code to generate a piece of assembly, and found that the parameters of foo() were not pushed onto the stack. Instead, the parameters were given to register first, and then the register value was stored in the stack, so it is similar to your situation. Different, you can see that the address of a is higher than that of d, and the addresses of these parameters are smaller than the local variables.
.globl main
.type main, @function
main:
.LFB958:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl , %ecx
movl , %edx
movl , %esi
movl , %edi
call _Z3fooiiii
movl .LC0:
.string "Address of a: "
.LC1:
.string "Address of b: "
.LC2:
.string "Address of c: "
.LC3:
.string "Address of d: "
.LC4:
.string "Address of e: "
.LC5:
.string "Address of f: "
.text
.globl _Z3fooiiii
.type _Z3fooiiii, @function
_Z3fooiiii:
.LFB957:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq , %rsp
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
movl %edx, -28(%rbp)
movl %ecx, -32(%rbp)
movl $_ZSt3hexRSt8ios_base, %esi
movl $_ZSt4cout, %edi
call _ZNSolsEPFRSt8ios_baseS0_E
movl $.LC0, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leaq -20(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPKv
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl $.LC1, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leaq -24(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPKv
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl $.LC2, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leaq -28(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPKv
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl $.LC3, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leaq -32(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPKv
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl $.LC4, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leaq -4(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPKv
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl $.LC5, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leaq -8(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPKv
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl -20(%rbp), %edx
movl -24(%rbp), %eax
addl %eax, %edx
movl -28(%rbp), %eax
addl %eax, %edx
movl -32(%rbp), %eax
leal (%rdx,%rax), %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
rrreee迷茫2017-04-17 15:41:09
This problem is related to the platform, the calling convention adopted, and the compiler.
i386 platform, under the default _cdecl calling convention, on the runtime stack, between function parameters and function local variables are 1: return address (pc after call instruction), 2: caller’s stack frame base address ( ebp), 3: callee save registers (the number is different on different platforms), 4: information added to the stack in order to handle exceptions (different compilers may implement different things, gcc will add things)