Deep C — activation frames/ stack frames

Ok, this ‘Deep C programming’ — Slide talk suggests that(slide 136) activation frames and execution stack are two very different concepts in the executable created by compiling and linking a C program.

I was curious and looked up activation frames in C, which led me to here. Ofcourse, before i began this journey, I only had heard of stack memory used by a program, and had an understanding that it is one part of the initial memory allocated to the process by the OS, that is used as a stack fashion.(i.e LIFO).

The university of calgary link mixes the terms activation,stack, frame, and execution rather freely. Something’s up. Though first let me try out the example in that Univ. of Calgary link and see what happens.

Here’s the code:, compilation and running it.

#include 

void print_facts(int num1, int num2);
int max_of_two(int j, int k);
double avg_of_two(int c, int d);

int main(void)
{
  int i;
  int j;
  /* point  1 */
  i = -8;
  j = 7;
  /* point  2 */
  print_facts(i, j);
  /* point 10 */
  return 0;
}

void print_facts(int num1, int num2)
{
  int larger;
  double the_avg;
  /* point  3 */
  larger = max_of_two(num1, num2);
  /* point  6 */
  the_avg = avg_of_two(num1, num2);
  /* point  9 */
  printf("For the two integers %d and %d,n", num1, num2);
  printf("the larger is %d and the average is %g.n",
         larger, the_avg);
}

int max_of_two(int j, int k)
{
  /* point  4 */
  if (j < k)
    j = k;
  /* point  5 */
  return j;
}

double avg_of_two(int c, int d)
{
  double sum;
  /* point  7 */
  sum = c + d;
  /* point  8 */
  return (c + d) / 2.0;
}

Compile(make), and run executable:

anand@anand-usb-boot:C [master] $ make activation_record_demo
gcc --std=c99 -g -pedantic -Wall -lm    activation_record_demo.c   -o activation_record_demo
activation_record_demo.c: In function ‘avg_of_two’:
activation_record_demo.c:50:12: warning: variable ‘sum’ set but not used [-Wunused-but-set-variable]
anand@anand-usb-boot:C [master] $ ./activation_record_demo 
For the tow integers -8 and 7, 
the larger is 7 and the average is -0.5.

Now comes the fun part: We need to be able to examine the stack, during the execution of the program.
GDB to the rescue.

Here’s the gdb session i went through:

anand@anand-usb-boot:C [master] $ gdb64 ./activation_record_demo
GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /home/anand/workspace/github_stuff_public/Miscellaneous/C/activation_record_demo...done.
(gdb) b 11
Breakpoint 1 at 0x400534: file activation_record_demo.c, line 11.
(gdb) run
Starting program: /home/anand/workspace/github_stuff_public/Miscellaneous/C/activation_record_demo 

Breakpoint 1, main () at activation_record_demo.c:12
12	    i = -8;
(gdb) frame
#0  main () at activation_record_demo.c:12
12	    i = -8;
(gdb) n
13	    j = 7;
(gdb) n
16	    print_facts(i,j);
(gdb) frame
#0  main () at activation_record_demo.c:16
16	    print_facts(i,j);
(gdb) s
print_facts (num1=-8, num2=7) at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) frame
#0  print_facts (num1=-8, num2=7) at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) s
max_of_two (j=-8, k=7) at activation_record_demo.c:42
42	    if (j < k)
(gdb) frame
#0  max_of_two (j=-8, k=7) at activation_record_demo.c:42
42	    if (j < k)
(gdb) frame 0
#0  max_of_two (j=-8, k=7) at activation_record_demo.c:42
42	    if (j < k)
(gdb) frame 
#0  max_of_two (j=-8, k=7) at activation_record_demo.c:42
42	    if (j < k)
(gdb) down
Bottom (innermost) frame selected; you cannot go down.
(gdb) s
43	        j = k;
(gdb) s
45	    return j;
(gdb) s
46	}
(gdb) s
print_facts (num1=-8, num2=7) at activation_record_demo.c:31
31	    the_avg = avg_of_two(num1,num2);
(gdb) s
avg_of_two (c=-8, d=7) at activation_record_demo.c:52
52	    sum = c + d;
(gdb) s
54	    return (c+d) / 2.0;
(gdb) frame
#0  avg_of_two (c=-8, d=7) at activation_record_demo.c:54
54	    return (c+d) / 2.0;
(gdb) s
55	}
(gdb) s
print_facts (num1=-8, num2=7) at activation_record_demo.c:34
34	    printf("For the tow integers %d and %d, n",num1,num2);
(gdb) n
For the tow integers -8 and 7, 
35	    printf("the larger is %d and the average is %g. n",larger,the_avg);
(gdb) n
the larger is 7 and the average is -0.5. 
37	}
(gdb) c
Continuing.
[Inferior 1 (process 24420) exited normally]
(gdb) run
Starting program: /home/anand/workspace/github_stuff_public/Miscellaneous/C/activation_record_demo 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000

Breakpoint 1, main () at activation_record_demo.c:12
12	    i = -8;
(gdb) s
13	    j = 7;
(gdb) s
16	    print_facts(i,j);
(gdb) frame
#0  main () at activation_record_demo.c:16
16	    print_facts(i,j);
(gdb) s
print_facts (num1=-8, num2=7) at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) frame
#0  print_facts (num1=-8, num2=7) at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) info frame
Stack level 0, frame at 0x7fffffffddb0:
 rip = 0x400566 in print_facts (activation_record_demo.c:28); 
    saved rip 0x400551
 called by frame at 0x7fffffffddd0
 source language c.
 Arglist at 0x7fffffffdda0, args: num1=-8, num2=7
 Locals at 0x7fffffffdda0, Previous frame's sp is 0x7fffffffddb0
 Saved registers:
  rbp at 0x7fffffffdda0, rip at 0x7fffffffdda8
(gdb) s
max_of_two (j=-8, k=7) at activation_record_demo.c:42
42	    if (j < k)
(gdb) s
43	        j = k;
(gdb) s
45	    return j;
(gdb) info frame
Stack level 0, frame at 0x7fffffffdd80:
 rip = 0x4005e6 in max_of_two (activation_record_demo.c:45); 
    saved rip 0x400575
 called by frame at 0x7fffffffddb0
 source language c.
 Arglist at 0x7fffffffdd70, args: j=7, k=7
 Locals at 0x7fffffffdd70, Previous frame's sp is 0x7fffffffdd80
 Saved registers:
  rbp at 0x7fffffffdd70, rip at 0x7fffffffdd78
(gdb) frame
#0  max_of_two (j=7, k=7) at activation_record_demo.c:45
45	    return j;
(gdb) up
#1  0x0000000000400575 in print_facts (num1=-8, num2=7)
    at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) frame
#1  0x0000000000400575 in print_facts (num1=-8, num2=7)
    at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) info frame
Stack level 1, frame at 0x7fffffffddb0:
 rip = 0x400575 in print_facts (activation_record_demo.c:28); 
    saved rip 0x400551
 called by frame at 0x7fffffffddd0, caller of frame at 0x7fffffffdd80
 source language c.
 Arglist at 0x7fffffffdda0, args: num1=-8, num2=7
 Locals at 0x7fffffffdda0, Previous frame's sp is 0x7fffffffddb0
 Saved registers:
  rbp at 0x7fffffffdda0, rip at 0x7fffffffdda8
(gdb) up
#2  0x0000000000400551 in main () at activation_record_demo.c:16
16	    print_facts(i,j);
(gdb) info frame
Stack level 2, frame at 0x7fffffffddd0:
 rip = 0x400551 in main (activation_record_demo.c:16); 
    saved rip 0x7ffff7a33ea5
 caller of frame at 0x7fffffffddb0
 source language c.
 Arglist at 0x7fffffffddc0, args: 
 Locals at 0x7fffffffddc0, Previous frame's sp is 0x7fffffffddd0
 Saved registers:
  rbp at 0x7fffffffddc0, rip at 0x7fffffffddc8
(gdb) down
#1  0x0000000000400575 in print_facts (num1=-8, num2=7)
    at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) down
#0  max_of_two (j=7, k=7) at activation_record_demo.c:45
45	    return j;
(gdb) down
Bottom (innermost) frame selected; you cannot go down.
(gdb) s
46	}
(gdb) s
print_facts (num1=-8, num2=7) at activation_record_demo.c:31
31	    the_avg = avg_of_two(num1,num2);
(gdb) n
34	    printf("For the tow integers %d and %d, n",num1,num2);
(gdb) n
For the tow integers -8 and 7, 
35	    printf("the larger is %d and the average is %g. n",larger,the_avg);
(gdb) n
the larger is 7 and the average is -0.5. 
37	}
(gdb) c
Continuing.

Note the output of the frame commands.
It print a number followed by an address, followed by the caller function, and the source code file and line no.
The next line prints out the actual source code at that line.

Interestingly look at the address differences when i move up the stack using up/down.

For ex:

(gdb) info frame
Stack level 0, frame at 0x7fffffffdd80:
 rip = 0x4005e6 in max_of_two (activation_record_demo.c:45); 
    saved rip 0x400575
 called by frame at 0x7fffffffddb0
 source language c.
 Arglist at 0x7fffffffdd70, args: j=7, k=7
 Locals at 0x7fffffffdd70, Previous frame's sp is 0x7fffffffdd80
 Saved registers:
  rbp at 0x7fffffffdd70, rip at 0x7fffffffdd78
(gdb) frame
#0  max_of_two (j=7, k=7) at activation_record_demo.c:45
45	    return j;
(gdb) up
#1  0x0000000000400575 in print_facts (num1=-8, num2=7)
    at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);
(gdb) frame
#1  0x0000000000400575 in print_facts (num1=-8, num2=7)
    at activation_record_demo.c:28
28	    larger = max_of_two(num1,num2);

Here, i move up the stack twice. The stack address which originally was at

0x7fffffffdd80

moves to

0x0000000000400575

, At first look it seems bizarre, till you realize there’s a return and the first stack address located function returns.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s