CSE 351 - Spring 2010 - Section 4

Questions

Why does the stack grow downward?

Historically, program code and other sections were loaded starting at address 0x0 (this is also basic human nature). Therefore, to give the stack as much room as possible to grow without conflicting with these other sections, it was placed at the opposite end of memory and grows downwards. The end.

Are shifts of unsigned ints always logical?

Yes. Here is a short program to discover this for yourself.

int main(void) {
unsigned int a = 0xffffffff;
a >>= 1;
return 0;
}

And here is the way to print formatted strings in gdb.

(gdb) printf "%x\n", a
7fffffff

Do little-endian architectures do a left-shift instead of a right-shift for the >> operator, and vice versa?

No, little-endian processors handle left-shift and right-shift correctly in hardware, so that programmers don't have to think about the number representation. Here is the disassembly of the program above to demonstrate this:

Dump of assembler code for function main:
0x00001fe6 <main+0> push %ebp
0x00001fe7 <main+1> mov %esp,%ebp
0x00001fe9 <main+3> sub $0x18,%esp
0x00001fec <main+6> movl $0xffffffff,-0xc(%ebp)
0x00001ff3 <main+13> lea -0xc(%ebp),%eax
0x00001ff6 <main+16> shrl (%eax)
0x00001ff8 <main+18> mov $0x0,%eax
0x00001ffd <main+23> leave
0x00001ffe <main+24> ret
End of assembler dump.

Compiling Loops

#include <stdio.h>

int main(void) {
int i, total;
total = 1;
for (i = 0; i < 10; i++) {
total = total * 2;
}
printf("%d\n", total);
return 0;
}

gcc on Mac OS X produces the following disassembly (using gdb).

Dump of assembler code for function main:
0x00001fae <main+0> push %ebp
0x00001faf <main+1> mov %esp,%ebp
0x00001fb1 <main+3> push %ebx
0x00001fb2 <main+4> sub $0x24,%esp
0x00001fb5 <main+7> call 0x1fba <main+12>
0x00001fba <main+12> pop %ebx
0x00001fbb <main+13> movl $0x1,-0xc(%ebp)
0x00001fc2 <main+20> movl $0x0,-0x10(%ebp)
0x00001fc9 <main+27> jmp 0x1fd5 <main+39>Jump to test in the middle
0x00001fcb <main+29> lea -0xc(%ebp),%eaxLoop beginning
0x00001fce <main+32> shll (%eax)Loop body
0x00001fd0 <main+34> lea -0x10(%ebp),%eax
0x00001fd3 <main+37> incl (%eax)Loop update
0x00001fd5 <main+39> cmpl $0x9,-0x10(%ebp)Loop test
0x00001fd9 <main+43> jle 0x1fcb <main+29>Conditional jump to loop beginning
0x00001fdb <main+45> mov -0xc(%ebp),%eax
0x00001fde <main+48> mov %eax,0x4(%esp)
0x00001fe2 <main+52> lea 0x41(%ebx),%eax
0x00001fe8 <main+58> mov %eax,(%esp)
0x00001feb <main+61> call 0x3005 <dyld_stub_printf>
0x00001ff0 <main+66> mov $0x0,%eax
0x00001ff5 <main+71> add $0x24,%esp
0x00001ff8 <main+74> pop %ebx
0x00001ff9 <main+75> leave
0x00001ffa <main+76> ret
End of assembler dump.

Procedure Calls and Stack Frames

Here is a simple program that will allow us to examine the locations of function arguments and return values.

#include <stdio.h>

int func(int a, int b) {
return a+b;
}

int main(void) {
int d = func(22,33);
printf("%d\n", d);
return 0;
}

Here is the disassembly of the main and func functions in gdb.

Dump of assembler code for function func:
0x00001faa <func+0> push %ebp
0x00001fab <func+1>> mov %esp,%ebp
0x00001fad <func+3>> sub $0x8,%esp
0x00001fb0 <func+6>> mov 0xc(%ebp),%eax
0x00001fb3 <func+9>> add 0x8(%ebp),%eax
0x00001fb6 <func+12>> leave
0x00001fb7 <func+13>> ret
End of assembler dump.

This is what the call stack looks like by line func+3.

%ebp+12a = 22
%ebp+8b = 33
%ebp+4return address = 0x00001fd9
%ebpold %ebp = 0xbffff338
%ebp-4 
%ebp-8== %esp, from the instruction at func+3 above

The return value a+b is placed into the %eax register.

You can also use the print command in gdb to call functions and display their return values.

anti-hero-1:sections buy-ppham$ gdb call
GNU gdb 6.3.50-20050815 (Apple version gdb-966) (Tue Mar 10 02:43:13 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"...Reading symbols for shared libraries ... done

(gdb) start
Breakpoint 1 at 0x1fc3: file call.c, line 9.
Starting program: /Users/buy-ppham/src/CSE351/sections/call
funReading symbols for shared libraries ++. done

Breakpoint 1, main () at call.c:9
9 int d = func(22,33);
(gdb) print func(11,22)
$1 = 33
(gdb) print func(33,44)
$2 = 77
(gdb)

Arrays

Where do local arrays get stored? Here's a program that will let us find out!

#include <stdio.h>

int* arrayfunc(int i, int j) {
int array[10][10];
int x;
printf("Address of array: %x\n", array);
printf("Address of x: %x\n", &x);
printf("Address of array[%d][%d]: %x\n", i, j, &array[i][j]);
}

int main(void) {
arrayfunc(2,3);
}

Compiling and running this program gives us:

anti-hero-3:sections buy-ppham$ gcc -o arrays -g arrays.c
anti-hero-3:sections buy-ppham$ ./arrays
Address of array: bffff220
Address of x: bffff21c
Address of array[2][3]: bffff27c

Using a hex calculator, you can discover that the difference between the beginning of the array and array[2][3] is 92 bytes.

Divide 92 by 4, because the size of an int is 4 bytes, and you get 23. This is the expected offset since each row of the array contains 10 items.

23 = (2*10) + 3