Sathish Ram
Sathish Ram

Reputation: 21

Stack memory layout in c

I have written a small program for learning about the stack pointer in C. Usually, the stack pointer points to the last address of RAM. For example, I run this code in Linux, which has the stack pointer in last address (0x7fffffffffff) but the variables are stored in these addresses (for this program):

c --> 0x7ffc67ca5c94
b --> 0x7ffc67ca5c98
a --> 0x7ffc67ca5c9c

Code:

void fun(int *ptr)
{
   printf("c --> %p\n",ptr);
   ptr++;
   printf("b --> %p\n",ptr);
   ptr++;
   printf("a --> %p\n",ptr);
}

int main() 
{
   int a = 10,b = 20,c = 30;
   printf("%p     %p    %p\n",&a,&b,&c);
   fun(&c);
   return 0;    
}

Output for this program:

0x7ffc67ca5c9c     0x7ffc67ca5c98    0x7ffc67ca5c94
c --> 0x7ffc67ca5c94
b --> 0x7ffc67ca5c98
a --> 0x7ffc67ca5c9c

My questions are:

  1. Why are the variables not stored in last section of the stack frame (0x7fffffffffff) which skips some memory locations and gets stored? Any valid reason for such behavior?

  2. Why the stack pointer address has six bytes?

I am working on a 64-bit machine with a 32-bit GCC compiler.

Upvotes: 1

Views: 2397

Answers (2)

dhke
dhke

Reputation: 15388

which has the stack pointer in last address (0x7fffffffffff)

If running a 32bit process, the last address (minus the reserved area) would be 0x7fff ffff when doing the usual 2GB/2GB split. But 32bit Linux typically reserves only a 1GB kernel address area, so the stack would actually start at 0xc000 0000 (TASK_SIZE).

What you are seeing here, instead is the curious split of the x64 address space layout. Here, indeed the user address space ends at 0x0000 7fff ffff ffff, with 0xffff ff80 0000 0000 and above reserved for the kernel.

Current MMUs actually enforce this, 0xfffe ff80 0000 0000 or similar are not valid addresses, the bit 47-63 must be equal to form a Canonical Form Address

Why the stack pointer address has six bytes?

It does not look like it does from the output of your program. You are printing the size each variable takes on the stack, not the pointer size. The six-byte addresses from printf() are actually 64bit-addresses with leading zeros cut off (thanks to @Jonathan Leffler for spotting this).

Indeed, sizeof(int *) == 8, but sizeof(int) == 4, because even 64bit Linux has 32bit ints (only long is 64 bit). It's quite easy to miss that, though.

Why are the variables not stored in last section of the stack frame (0x7fffffffffff) which skips some memory locations and gets stored? Any valid reason for such behavior?

If you look here, there is quite some stuff going into that address space before the user stack starts. Since you might need whole pages for this code for protection, there might be some overhead. Add library startup code and you might get quite a few bytes of memory.

Most of the code there is probably inherited copy-on-write (or even read-only) from the parent process.

Edit: On x64, any kernel-exported code probably go in the higher memory area. Don't claim what I have not verified ;-).

As a side note: When I compile and run your code on 64bit FreeBSD 10.2 I get this

0x7fffffffe5c8     0x7fffffffe5c4    0x7fffffffe5c0
c --> 0x7fffffffe5c0
b --> 0x7fffffffe5c4
a --> 0x7fffffffe5c8

which is similar to your output, even though FreeBSD seems to position the stack differently.

Running in 32bit mode, I get this:

0xffffd788     0xffffd784    0xffffd780
c --> 0xffffd780
b --> 0xffffd784
a --> 0xffffd788

The latter is probably really fun to explain (e.g. where is my kernel address space?).

Upvotes: 2

masternone
masternone

Reputation: 489

  • variables can be stored at any address
  • that address contains more than 32 bits, so it is assigning 64 bit addresses

Upvotes: 1

Related Questions