• No results found

Passing parameters on the stack

In document PC Assembly Language (Page 80-85)

4.5 Calling Conventions

4.5.1 Passing parameters on the stack

Parameters to a subprogram may be passed on the stack. They are pushed onto the stack before the CALL instruction. Just as in C, if the parameter is to be changed by the subprogram, the address of the data must be passed, not the value. If the parameter’s size is less than a double word, it must be converted to a double word before being pushed.

The parameters on the stack are not popped off by the subprogram, instead they are accessed from the stack itself. Why?

• Since they have to be pushed on the stack before the CALL instruction, the return address would have to be popped off first (and then pushed back on again).

• Often the parameters will have to be used in several places in the subprogram. Usually, they can not be kept in a register for the entire subprogram and would have to be stored in memory. Leaving them

ESP + 4 Parameter ESP Return address

Figure 4.1:

ESP + 8 Parameter ESP + 4 Return address ESP subprogram data

Figure 4.2:

on the stack keeps a copy of the data in memory that can be accessed at any point of the subprogram.

Consider a subprogram that is passed a single parameter on the stack. When using indirect ad-dressing, the 80x86 proces-sor accesses different seg-ments depending on what registers are used in the indirect addressing expres-sion. ESP (and EBP) use the stack segment while EAX, EBX, ECX and EDX use the data segment.

However, this is usually unimportant for most pro-tected mode programs, be-cause for them the data and stack segments are the same.

When the subprogram is invoked, the stack looks like Figure 4.1. The pa-rameter can be accessed using indirect addressing ([ESP+4]2).

If the stack is also used inside the subprogram to store data, the number needed to be added to ESP will change. For example, Figure 4.2 shows what the stack looks like if a DWORD is pushed the stack. Now the parameter is at ESP + 8 not ESP + 4. Thus, it can be very error prone to use ESP when referencing parameters. To solve this problem, the 80386 supplies another register to use: EBP. This register’s only purpose is to reference data on the stack. The C calling convention mandates that a subprogram first save the value of EBP on the stack and then set EBP to be equal to ESP. This allows ESP to change as data is pushed or popped off the stack without modifying EBP. At the end of the subprogram, the original value of EBP must be restored (this is why it is saved at the start of the subprogram.) Figure 4.3 shows the general form of a subprogram that follows these conventions.

Lines 2 and 3 in Figure 4.3 make up the general prologue of a subprogram.

Lines 5 and 6 make up the epilogue. Figure 4.4 shows what the stack looks like immediately after the prologue. Now the parameter can be access with [EBP + 8] at any place in the subprogram without worrying about what else has been pushed onto the stack by the subprogram.

After the subprogram is over, the parameters that were pushed on the stack must be removed. The C calling convention specifies that the caller code must do this. Other conventions are different. For example, the Pascal calling convention specifies that the subprogram must remove the

parame-2It is legal to add a constant to a register when using indirect addressing. More complicated expressions are possible too. This topic is covered in the next chapter

1 subprogram_label:

2 push ebp ; save original EBP value on stack

3 mov ebp, esp ; new EBP = ESP

4 ; subprogram code

5 pop ebp ; restore original EBP value

6 ret

Figure 4.3: General subprogram form

ESP + 8 EBP + 8 Parameter ESP + 4 EBP + 4 Return address

ESP EBP saved EBP

Figure 4.4:

ters. (There is another form of the RET instruction that makes this easy to do.) Some C compilers support this convention too. The pascal keyword is used in the prototype and definition of the function to tell the compiler to use this convention. In fact, the stdcall convention that the MS Windows API C functions use also works this way. What is the advantage of this way?

It is a little more efficient than the C convention. Why do all C functions not use this convention, then? In general, C allows a function to have vary-ing number of arguments (e.g., the printf and scanf functions). For these types of functions, the operation to remove the parameters from the stack will vary from one call of the function to the next. The C convention allows the instructions to perform this operation to be easily varied from one call to the next. The Pascal and stdcall convention makes this operation very difficult. Thus, the Pascal convention (like the Pascal language) does not allow this type of function. MS Windows can use this convention since none of its API functions take varying numbers of arguments.

Figure 4.5 shows how a subprogram using the C calling convention would be called. Line 3 removes the parameter from the stack by directly manipu-lating the stack pointer. A POP instruction could be used to do this also, but would require the useless result to be stored in a register. Actually, for this particular case, many compilers would use a POP ECX instruction to remove the parameter. The compiler would use a POP instead of an ADD because the ADD requires more bytes for the instruction. However, the POP also changes ECX’s value! Next is another example program with two subprograms that use the C calling conventions discussed above. Line 54 (and other lines) shows that multiple data and text segments may be declared in a single

1 push dword 1 ; pass 1 as parameter

2 call fun

3 add esp, 4 ; remove parameter from stack

Figure 4.5: Sample subprogram call

source file. They will be combined into single data and text segments in the linking process. Splitting up the data and code into separate segments allow the data that a subprogram uses to be defined close by the code of the subprogram.

sub3.asm

1 %include "asm_io.inc"

2

3 segment .data

4 sum dd 0

5

6 segment .bss

7 input resd 1

8

9 ;

10 ; pseudo-code algorithm

11 ; i = 1;

12 ; sum = 0;

13 ; while( get_int(i, &input), input != 0 ) {

14 ; sum += input;

15 ; i++;

16 ; }

17 ; print_sum(num);

18 segment .text

19 global _asm_main

20 _asm_main:

21 enter 0,0 ; setup routine

22 pusha

23

24 mov edx, 1 ; edx is ’i’ in pseudo-code

25 while_loop:

26 push edx ; save i on stack

27 push dword input ; push address of input on stack

28 call get_int

29 add esp, 8 ; remove i and &input from stack

30

41 push dword [sum] ; push value of sum onto stack

42 call print_sum

43 pop ecx ; remove [sum] from stack

44

50 ; Parameters (in order pushed on stack)

51 ; number of input (at [ebp + 12])

52 ; address of word to store input into (at [ebp + 8])

53 ; Notes:

54 ; values of eax and ebx are destroyed

55 segment .data

56 prompt db ") Enter an integer number (0 to quit): ", 0

57

71 mov [ebx], eax ; store input into memory

72

73 pop ebp

74 ret ; jump back to caller

75

76 ; subprogram print_sum

77 ; prints out the sum

78 ; Parameter:

79 ; sum to print out (at [ebp+8])

80 ; Note: destroys value of eax

81 ;

In document PC Assembly Language (Page 80-85)