<<

Procedures and the Call Stack

Topics • Procedures • Call stack • Procedure/stack instructions • Calling conventions • Register-saving conventions Why Procedures?

Why functions? Why methods?

int contains_char(char* haystack, char needle) { while (*haystack != '\0') { if (*haystack == needle) return 1; haystack++; } return 0; }

Procedural Abstraction Implementing Procedures

How does a caller pass arguments to a procedure?

How does a caller get a return value from a procedure?

Where does a procedure store local variables?

How does a procedure know where to return (what code to execute next when done)?

How do procedures share limited registers and memory?

3 Call Chain Example

yoo(…) Call Chain { • yoo • who(…) who(); { who • • • • • ru(); } ru(…) ru ru • • • { ru(); • • • • • } • }

4 First Try (broken)

yoo:

who: jmp who back:

done: jmp back stop:

What if I want to call a function multiple times? First Try (broken)

What if I want to call a function multiple times?

who: 1 2 ru: jmp ru back2: 6 3,7 5 jmp ru 4 back2: done: jmp back2 9 8 stop: Implementing Procedures

How does a caller pass arguments to a procedure?

How does a caller get a return value from a procedure?

Where does a procedure store local variables?

How does a procedure know where to return (what code to execute next when done)?

How do procedures share limited registers and memory? All these need separate storage per call! (not just per procedure) 7 Memory Layout

Addr Perm Contents Managed by Initialized

N 2 -1 Stack RW Procedure context Run-time

Programmer, Dynamic RW malloc/free, Run-time Heap data structures new/GC Global variables/ Compiler/ RW Startup Statics static data structures Assembler/ Compiler/ R String literals Startup Literals Assembler/Linker Compiler/ X Instructions Startup Text Assembler/Linker 0 Call Stack

We see organization. Stack “Bottom” Details differ across architectures, but big ideas are shared. higher Region of memory addresses Managed with stack discipline

%esp holds lowest stack address (address of "top" element) stack grows toward lower addresses Stack Pointer: %esp (not extra-sensory perception)

Stack “Top”

9 IA32 Call Stack: Push Stack “Bottom” pushl Src

higher addresses

stack grows toward lower addresses Stack Pointer: %esp

Stack “Top”

10 IA32 Call Stack: Push Stack “Bottom” pushl Src 1. Fetch value from Src 2. Decrement %esp by 4 (why 4?) higher addresses 3. Store value at new address given by %esp

stack grows toward lower addresses Stack Pointer: %esp -4

Stack “Top”

11 IA32 Call Stack: Pop Stack “Bottom” popl Dest

higher addresses

stack grows toward lower addresses Stack Pointer: %esp

Stack “Top”

12 IA32 Call Stack: Pop Stack “Bottom” popl Dest 1. Load value from address %esp 2. Write value to Dest higher addresses 3. Increment %esp by 4

stack grows toward lower addresses Stack Pointer: %esp

Stack “Top” Those bits are still there; we’re just not using them. 13 Call Chain Example Example

yoo(…) Call Chain { • yoo • who(…) who(); { who • • • • • amI(); } amI(…) amI amI • • • { amI(); • amI • • • • } amI(); • amI • }

Procedure amI is recursive (calls itself)

14 Stack frames support procedure calls. Contents Caller Frame Local variables Function arguments Base/Frame Pointer: Return information %ebp Temporary space Frame for current procedure Management %esp Stack Pointer Space allocated when procedure is entered “Set-up” code Stack “Top” Space deallocated upon return “Finish” code

Why not just give every procedure a permanent chunk of memory to hold its local variables, etc? 15 Example Stack

yoo(…) yoo %ebp { • yoo who • %esp who(); • amI amI • } amI

amI

16 Example Stack

who(…) yoo { • • • yoo who amI(); • • • %ebp amI(); amI amI who • • • %esp } amI

amI

17 Example Stack

amI(…) yoo { • yoo who • amI(); • amI amI who • } amI %ebp amI %esp amI

18 Example Stack

amI(…) yoo { • yoo who • amI(); • amI amI who • } amI amI

amI %ebp amI %esp

19 Example Stack

amI(…) yoo { • yoo who • amI(); • amI amI who • } amI amI

amI amI

%ebp amI %esp

20 Example Stack

amI(…) yoo { • yoo who • amI(); • amI amI who • } amI amI

amI %ebp amI %esp

21 Example Stack

amI(…) yoo { • yoo who • amI(); • amI amI who • } amI %ebp amI %esp amI

22 Example Stack

who(…) yoo { • • • yoo who amI(); • • • %ebp amI(); amI amI who • • • %esp } amI

amI

23 Example Stack

amI(…) yoo { • yoo who • • • amI amI who • } amI %ebp amI %esp amI

24 Example Stack

who(…) yoo { • • • yoo who amI(); • • • %ebp amI(); amI amI who • • • %esp } amI

amI

25 Example Stack

yoo(…) yoo %ebp { • yoo who • %esp who(); • amI amI • } amI

amI

How did we remember where to point %ebp when returning? 26 Procedure Control Flow Instructions

Procedure call: call label 1. Push return address on stack 2. Jump to label

Return address: Address of instruction after call. Example: 804854e: e8 3d 06 00 00 call 8048b90

8048553: 50 pushl %eax

Procedure return: ret 1. Pop return address from stack 2. Jump to address 27 Procedure Call/Return: 1

804854e: e8 3d 06 00 00 call 8048b90

8048553: 50 pushl %eax

call 8048b90

0x110 0x10c 0x108 123

%esp 0x108

%eip 0x804854e

%eip = instruction pointer = 28 Procedure Call/Return: 2

804854e: e8 3d 06 00 00 call 8048b90

8048553: 50 pushl %eax

call 8048b90

0x110 0x110 0x10c 0x10c 0x108 123 0x108 123 0x104 0x8048553

%esp 0x108 %esp 0x1080x104

%eip 0x804854e %eip 0x804854e0x8048553

%eip = instruction pointer = program counter 29 Procedure Call/Return: 3

804854e: e8 3d 06 00 00 call 8048b90

8048553: 50 pushl %eax

call 8048b90 PC-relative address

0x110 0x110 0x10c 0x10c 0x108 123 0x108 123 0x104 0x8048553

%esp 0x108 %esp 0x1080x104

%eip 0x804854e %eip 0x8048553 + 0x000063d 0x8048b90 %eip: program counter 30 Procedure Call/Return: 4

8048591: c3 ret

ret

0x110 0x110 0x10c 0x10c 0x108 123 0x108 123 0x104 0x8048553 0x8048553

%esp 0x104 %esp 0x1040x108

%eip 0x8048591 %eip 0x80485910x8048553

%eip: program counter 31 IA32/Linux Stack Frame

… Caller Frame Arguments to callee Return Address Caller's base pointer

Saved Registers + Callee Local Variables Frame Stack Registers Base/Frame pointer %ebp Stack pointer %esp Arguments for next call

33 Revisiting swap Calling swap from call_swap int zip1 = 02481; call_swap: int zip2 = 98195; • • • pushl $zip2 # Global Var void call_swap() { pushl $zip1 # Global Var swap(&zip1, &zip2); call swap } • • •

void swap(int *xp, int *yp) { int t0 = *xp; Resulting • int t1 = *yp; Stack • *xp = t1; • *yp = t0; } &zip2 &zip1 Rtn adr %esp

34 Revisiting swap

swap: pushl %ebp movl %esp,%ebp Set pushl %ebx Up void swap(int *xp, int *yp) { movl 12(%ebp),%ecx int t0 = *xp; movl 8(%ebp),%edx int t1 = *yp; movl (%ecx),%eax Body *xp = t1; movl (%edx),%ebx *yp = t0; movl %eax,(%edx) } movl %ebx,(%ecx)

movl -4(%ebp),%ebx movl %ebp,%esp Finish popl %ebp ret

35 swap Setup #1

Entering Stack Resulting Stack

• • • %ebp • frame • %esp •

&zip2 yp call_swap &zip1 xp %ebp Rtn adr Rtn adr %esp Old %ebp

swap: pushl %ebp movl %esp,%ebp Set pushl %ebx Up

36 swap Setup #2

Entering Stack Resulting Stack

• • • %ebp • frame • %esp •

&zip2 yp call_swap &zip1 xp %ebp Rtn adr Rtn adr %esp Old %ebp

swap: pushl %ebp movl %esp,%ebp Set pushl %ebx Up

37 swap Setup #3

Entering Stack Resulting Stack

• • • %ebp • frame • %esp •

&zip2 yp call_swap &zip1 xp %ebp Rtn adr Rtn adr %esp Old %ebp Old %ebx swap: pushl %ebp movl %esp,%ebp Set pushl %ebx Up

38 swap Body

Entering Stack Resulting Stack

• • • %ebp • frame • %esp Offset relative • to new %ebp &zip2 12 yp call_swap &zip1 8 xp %ebp Rtn adr 4 Rtn adr %esp Old %ebp Old %ebx

movl 12(%ebp),%ecx # get yp movl 8(%ebp),%edx # get xp . . . Body

39 swap Finish #1

Finishing Stack Resulting Stack

• • • • frame • •

yp yp call_swap xp %ebp xp %ebp Rtn adr %esp Rtn adr %esp Old %ebp Old %ebp Old %ebx Old %ebx

movl -4(%ebp),%ebx movl %ebp,%esp Finish popl %ebp ret 40 swap Finish #2

Finishing Stack Resulting Stack

• • • • frame • •

yp yp call_swap xp %ebp xp %ebp Rtn adr %esp Rtn adr %esp Old %ebp Old %ebp Old %ebx

movl -4(%ebp),%ebx movl %ebp,%esp Finish popl %ebp ret 41 swap Finish #3

Finishing Stack Resulting Stack

• • • • frame • •

yp yp call_swap xp %ebp xp %ebp Rtn adr %esp Rtn adr %esp Old %ebp Old %ebx

movl -4(%ebp),%ebx movl %ebp,%esp Finish popl %ebp ret 42 swap Finish #4

Finishing Stack Resulting Stack

• • • • frame • •

yp yp call_swap xp %ebp xp %ebp Rtn adr %esp %esp Old %ebp Old %ebx

movl -4(%ebp),%ebx movl %ebp,%esp Finish popl %ebp ret 43 Disassembled swap

080483a4 : 80483a4: 55 push %ebp 80483a5: 89 e5 mov %esp,%ebp 80483a7: 53 push %ebx 80483a8: 8b 55 08 mov 0x8(%ebp),%edx 80483ab: 8b 4d 0c mov 0xc(%ebp),%ecx 80483ae: 8b 1a mov (%edx),%ebx 80483b0: 8b 01 mov (%ecx),%eax 80483b2: 89 02 mov %eax,(%edx) 80483b4: 89 19 mov %ebx,(%ecx) 80483b6: 5b pop %ebx mov %ebp,%esp 80483b7: c9 leave pop %ebp 80483b8: c3 ret

Calling Code 8048409: e8 96 ff ff ff call 80483a4 804840e: 8b 45 f8 mov 0xfffffff8(%ebp),%eax

relative address (little endian) 44 swap Finish #4

Finishing Stack Resulting Stack

• • • • frame • •

yp yp call_swap xp %ebp xp %ebp Rtn adr %esp %esp Old %ebp Old %ebx Observation movl -4(%ebp),%ebx Saved & restored register %ebx movl %ebp,%esp Finish but not %eax, %ecx, or %edx popl %ebp ret 45 Register Saving Conventions When procedure yoo calls who: yoo is the caller who is the callee

Will register contents still be there after a procedure call? yoo: who: • • • • • • movl $12345, %edx movl 8(%ebp), %edx call who ? addl $98195, %edx addl %edx, %eax • • • • • • ret ret

47 Register Saving Conventions When procedure yoo calls who: yoo is the caller who is the callee

Will register contents still be there after a procedure call? Conventions Caller Save Caller saves temporary values in its frame before calling Callee Save Callee saves temporary values in its frame before using

48 IA32/Linux Register Saving Conventions %eax, %edx, %ecx Caller saves prior to call %eax Caller-Save %edx if needs values after call Tempor ar ies %ecx %eax %ebx also used to return Callee-Save %esi integer value Tempor ar ies %edi %ebx, %esi, %edi %esp Callee saves if will use Special %ebp %esp, %ebp special form of callee save restored to original values before returning

49 A Puzzle

C function body: *p = d; Write the function return x - c; header, types, and order of parameters. assembly: movsbl 12(%ebp),%edx movl 16(%ebp),%eax movl %edx,(%eax) movswl 8(%ebp),%eax movl 20(%ebp),%edx subl %eax,%edx movl %edx,%eax

movsbl = move sign-extending a byte to a long (4-byte) movswl = move sign-extending a word (2-byte) to a long (4-byte) Example: Pointers to Local Variables

Top-Level Call Recursive Procedure int sfact(int x) { void s_helper int val = 1; (int x, int *accum) { s_helper(x, &val); if (x <= 1) { return val; return; } } else { int z = *accum * x; *accum = z; s_helper (x-1,accum); } sfact(3) val = 1 } s_helper(3, &val) val = 3 s_helper(2, &val) val = 6 s_helper(1, &val) val = 6. Pass pointer to update location

51 Creating & Initializing Pointer

Must store val in memory (stack); int sfact(int x) { registers do not have addresses. int val = 1; s_helper(x, &val); return val; } 8 x 4 Rtn adr %esp 0 Old %ebp %ebp Initial part of sfact -4 val = 1 %esp __sfact:sfact: -8 pushl %%ebpebp # Save %%ebpebp Temp. movl %%esp,%ebpesp,%ebp # Set %%ebpebp -12 UnusedSpace subl $16,%esp # Add 16 bytes -16 %esp movl 8(%8(%ebp),%edxebp),%edx # edx = x movl $1,-4(%4(%ebp)ebp) # val = 1

52 Passing Pointer

Must store val in memory (stack); int sfact(int x) { registers do not have addresses. int val = 1; s_helper(x, &val); return val; }

Stack at time of call: 8 x 4 Rtn adr 0 Old %ebp %ebp Calling s_helper from sfact -4 valval =x= !1 leal -4(%ebp),%eax # Compute &val -8 pushl %eax # Push on stack -12 Unused pushl %edx # Push x -16 call s_helper # call %esp movl -4(%ebp),%eax # Return val &val • • • # Finish x

53 IA32/Linux Procedure Summary call, ret, push, pop

Stack discipline fits procedure call / return.* Caller Frame If P calls Q, Q returns before P, including recursion. Arguments

Return Addr Conventions support arbitrary function calls. %ebp Old %ebp Safely store per-call values in local stack frame and callee-saved registers Saved Registers Push function arguments at top of stack before call + Result returned in %eax Local Variables

Argument Build %esp *Take 251 to learn about languages where it doesn't. 54