1

I'm trying to get my head around a function's call stack with some practical examples. In all of the diagrams explaining this, it is laid out like [Local variables][Return Address][Arguments] (low memory on the left). But when I'm in gdb and breakpoint inside a function, I get them in a different order:

(gdb) info args
arg1 = 0
arg2 = 0
arg3 = 32767
(gdb) p &arg1
0x7ffff3a4697ec
(gdb) p &arg2
0x7ffff3a4697e8
(gdb) p &arg3
0x7ffff3a4697e4
(gdb) info locals
local1 = 0
local2 = 0
local3 = 0
(gdb) p &local1
0x7ffff3a4697fc
(gdb) p &local2
0x7ffff3a4697f8
(gdb) p &local3
0x7ffff3a4697f4
(gdb) info frame
Stack level 0, frame at 0x7ffff3a469810:
...
Arglist at 0x7ffff3a469800, args: arg1=0, arg2=0, arg3=32767
Locals at 0x7ffff3a469800, Previous frame's sp is 0x7ffff3a469810
Saved registers:
 rbp at 0x7ffff3a469800, rip at 0x7ffff3a469808

Why are the arguments of the function at lower memory addresses than both the local variables and the return pointer? All literature on the subject (e.g. diagrams like this https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Call_stack_layout.svg/342px-Call_stack_layout.svg.png) imply that the arguments should be at higher memory address than the return address? And the return address should be between locals & arguments, whereas I have locals & arguments in a contiguous block with the return address at the end. Many thanks - apologies if I have completely misunderstood!

Edit: Example C program to generate this:

#include <stdio.h>

void func1(int arg1, int arg2, int arg3) {
  int local1;
  int local2;
  int local3;
  local1 = 2;
  local2 = 3;
  local3 = 4;
}

int main(){
  int a;
  int b;
  int c;
  func1(a, b, c);
}

Compile this code with gcc code.c -o code on CentOS x86_64. Run with gdb and put a breakpoint in the func1. Look at address of arg variables, local variables, and return address.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Trimble52
  • 13
  • 3
  • The image you link to does not say anything about the values of any addresses. – Scott Hunter Dec 17 '21 at 17:57
  • 1
    The top of stack usually indicates low memory address; here's another picture that explicitly says this if you want https://i.stack.imgur.com/Z5cSh.jpg – Trimble52 Dec 17 '21 at 17:59
  • [Spectre](https://en.wikipedia.org/wiki/Spectre_(security_vulnerability))... ;) – Super-intelligent Shade Dec 17 '21 at 18:07
  • @Trimble52 why do you think you are getting them in the wrong order? `local1` has higher address than eg `arg1`. Or did I miss something? – Super-intelligent Shade Dec 17 '21 at 18:11
  • This image https://i.stack.imgur.com/Z5cSh.jpg (and countless like it) says that local variables should have _lower_ memory addresses than arguments. And the return address should be between the two, whereas you can see here locals and arguments are a contiguous block and the return address (rip at 0x7ffe96421358) comes at a higher address than all of them – Trimble52 Dec 17 '21 at 18:14
  • https://stackoverflow.com/questions/1677415/does-stack-grow-upward-or-downward https://stackoverflow.com/questions/664744/what-is-the-direction-of-stack-growth-in-most-modern-systems https://stackoverflow.com/questions/11177209/why-doesnt-the-c-program-stack-grow-downward `This image i.stack.imgur.com/Z5cSh.jpg (and countless like it)` It's easy for education. https://en.wikipedia.org/wiki/X86_calling_conventions , https://uclibc.org/docs/psABI-x86_64.pdf page 16 – KamilCuk Dec 17 '21 at 18:14
  • This is not a question about whether the stack goes up or down, it's the fact that instead of [Locals][Return][Args] I have [Args][Locals][Return] - that's the wrong order no matter which way the stack is going – Trimble52 Dec 17 '21 at 18:17
  • What microprocessor and programming language are you using? These are two important factors that dictate calling convention. – lurker Dec 17 '21 at 18:25
  • 1
    This is just a simple c program compiled with gcc (no arguments) on Centos x86 – Trimble52 Dec 17 '21 at 18:30
  • @Trimble52 please post reproducible example – Super-intelligent Shade Dec 17 '21 at 19:27
  • 1
    @InnocentBystander have updated the post with the C source code – Trimble52 Dec 17 '21 at 19:42
  • @Trimble52 thank you. Last question: Are you sure it's x86 and not x86_64? – Super-intelligent Shade Dec 17 '21 at 20:23
  • Sorry you're right, it's x86_64 – Trimble52 Dec 17 '21 at 20:26
  • In x86_64 three arguments aren't passed on the stack, they're passed through registers by default. – lurker Dec 17 '21 at 20:29

1 Answers1

0

Why are the arguments of the function at lower memory addresses than both the local variables and the return pointer? All literature on the subject (...) imply that the arguments should be at higher memory address than the return address?

Since you are using x86_64 ABI, the first 6 arguments will usually be passed in the registers. Here is relevant quote from the System V Application Binary Interface AMD64 Architecture Processor Supplement (page 20, emphasis is mine, thanks @KamilCuk for the link):

Passing Once arguments are classified, the registers get assigned (in left-to-right order) for passing as follows:

  1. If the class is MEMORY, pass the argument on the stack.
  2. If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used.

So, what's most likely going on is you are mixing stack frames between func1 and main. Take a look at this code:

https://godbolt.org/z/88rPebqYj

Arguments arg1, arg2 and arg3 are passed to func1 in registers rdi, rsi and rdx.

  • 1
    That makes sense - didn't realise that it was so different for x86_64! Thank you! – Trimble52 Dec 17 '21 at 20:36
  • @Trimble52 you bet. Thank you for making me flex my inter-ear gland. ;) – Super-intelligent Shade Dec 17 '21 at 20:38
  • @Trimble52 if you add `-m32` to your compiler, you will get x86 code and the answer you are expecting. See here: https://godbolt.org/z/WK4hPPEW5 – Super-intelligent Shade Dec 17 '21 at 20:46
  • 1
    It's a debug build (`-O0`) so the compiler spilled the incoming register args to the stack, and happened to put them below local vars. @Trimble52 is finding the return address at a higher address than either the locals or args, so they're not looking into main's stack frame. So it's a duplicate of [Why are parameters allocated below the frame pointer instead of above?](https://stackoverflow.com/q/68407921). But yes the key point is register args. – Peter Cordes Dec 18 '21 at 05:56