Assembly Language for x86 Processors 7th Edition
Chapter 8: Advanced Procedures
. Overview
Copyright By PowCoder代写 加微信 powcoder
Stack Frames
INVOKE, ADDR, PROC, and PROTO
Creating Multimodule Programs
Advanced Use of Parameters (optional)
Java Bytecodes (optional)
Stack Frames
Stack Parameters
Local Variables
ENTER and LEAVE Instructions
LOCAL Directive
WriteStackFrame Procedure
Stack Frame
Also known as an activation record
Area of the stack set aside for a procedure’s return address, passed parameters, saved registers, and local variables
Created by the following steps:
Calling program pushes arguments on the stack and calls the procedure.
The called procedure pushes EBP on the stack, and sets EBP to ESP.
If local variables are needed, a constant is subtracted from ESP to make room on the stack.
Stack Parameters
More convenient than register parameters
Two possible ways of calling DumpMem
Why need Stack Parameters?
Anatomy of C code
mov esi,OFFSET array
mov ecx,LENGTHOF array
mov ebx,TYPE array
call DumpMem
push TYPE array
push LENGTHOF array
push OFFSET array
call DumpMem
int AddTwo(int i, int j)
return i+j;
int f(int &i, bool b)
int n, m; // do…
int x = AddTwo(5, 6);
int y = f(x, true);
Passing Arguments by Value
Push argument values on stack
Call the called-procedure
Accept a return value in EAX, if any
Remove arguments from the stack if the called-procedure did not remove them
val1 DWORD 5
val2 DWORD 6
(val2) 6
(val1) 5 ESP
Stack prior to CALL
Passing Arguments by Value: AddTwo
sum DWORD ?
push 6 ; second argument
push 5 ; first argument
call AddTwo ; EAX = sum
mov sum,eax ; save the sum
AddTwo PROC
mov ebp,esp
int n = AddTwo( 5, 6 );
151.unknown
Passing by Reference
Push the offsets of arguments on the stack
Call the procedure
Accept a return value in EAX, if any
Remove arguments from the stack if the called procedure did not remove them
val1 DWORD 5
val2 DWORD 6
push OFFSET val2
push OFFSET val1
(offset val2) 00000004
(offset val1) 00000000 ESP
Stack prior to CALL
Stack after the CALL
value or addr of val2
value or addr of val1
return address
Passing Arguments by Reference: Swap
push offset val2
push offset val1
Swap(&val1, &val2);
An argument passed by reference consists of the address (offset) of an object:
Accessing Stack Parameters (C/C++)
C and C++ functions access stack parameters using constant offsets from EBP1.
Example: [ebp + 8]
EBP is called the base pointer or frame pointer because it holds the base address of the stack frame.
EBP does not change value during the function.
EBP must be restored to its original value when a function returns.
1 BP in Real-address mode
RET Instruction
Return from subroutine
Pops stack into the instruction pointer (EIP or IP). Control transfers to the target address.
Optional operand n causes n bytes to be added to the stack pointer after EIP (or IP) is assigned a value.
C Call : Caller releases stack
RET does not clean up the stack.
AddTwo_C PROC
mov ebp,esp
mov eax,[ebp + 12] ; second parameter
add eax,[ebp + 8] ; first parameter
ret ; caller cleans up the stack
AddTwo_C ENDP
STDCall : Procedure releases stack
The RET n instruction cleans up the stack.
AddTwo PROC
mov ebp,esp
mov eax,[ebp + 12] ; second parameter
add eax,[ebp + 8] ; first parameter
ret 8 ; clean up the stack
AddTwo ENDP
_Example2 PROC
call AddTwo
call DumpRegs ; sum is in EAX
_Example2 ENDP
Passing an Array by Reference (1 of 2)
The ArrayFill procedure fills an array with 16-bit random integers
The calling program passes the address of the array, along with a count of the number of array elements:
count = 100
array WORD count DUP(?)
push OFFSET array
push COUNT
call ArrayFill
Passing an Array by Reference (2 of 2)
ArrayFill PROC
mov ebp,esp
mov esi,[ebp+12]
mov ecx,[ebp+8]
ESI points to the beginning of the array, so it’s easy to use a loop to access each array element.
ArrayFill can reference an array without knowing the array’s name:
152.unknown
ArrayFill Procedure
ArrayFill PROC
mov ebp,esp
pushad ; save registers
mov esi,[ebp+12] ; offset of array, beginning
mov ecx,[ebp+8] ; array size
cmp ecx,0 ; ECX == 0?
je L2 ; yes: skip over loop
L1: mov eax,10000h ; get random 0 – FFFFh
call RandomRange ; from the link library
mov [esi],ax
add esi,TYPE WORD
L2: popad ; restore registers
ret 8 ; clean up the stack
ArrayFill ENDP
Local Variables
Only statements within subroutine can view or modify local variables
Storage used by local variables is released when subroutine ends
local variable name can have the same name as a local variable in another function without creating a name clash
Essential when writing recursive procedures, as well as procedures executed by multiple execution threads
Local Variables
To explicitly create local variables, subtract total size from ESP.
void MySub()
MySub PROC
mov ebp,esp
sub esp,8 ; create variables
mov DWORD PTR [ebp-4],10 ; X
mov DWORD PTR [ebp-8],20 ; Y
; … Do something
mov esp,ebp ; remove locals from stack
MySub ENDP LocalVars.asm
ENTER and LEAVE
ENTER instruction creates stack frame for a called procedure
pushes EBP on the stack (push ebp)
sets EBP to the base of the stack frame (mov ebp, esp)
reserves space for local variables (sub esp, n)
Syntax: ENTER numBytesReserved, nestingLevel (=0)
LEAVE instruction terminates the stack frame for a called procedure
restores ESP to release local variables (mov esp, ebp)
pops EBP for the caller (pop ebp)
LEAVE Instruction
Terminates the stack frame for a procedure.
MySub PROC
MySub ENDP
mov ebp,esp
sub esp,8 ; 2 local DWORDs
mov esp,ebp ; free local space
Equivalent operations
LOCAL Directive
The LOCAL directive declares a list of local variables
immediately follows the PROC directive
each variable is assigned a type
LOCAL varlist
MySub PROC
LOCAL var1:BYTE, var2:WORD, var3:SDWORD
Using LOCAL
LOCAL flagVals[20]:BYTE ; array of bytes
LOCAL pArray:PTR WORD ; pointer to an array
myProc PROC, ; procedure
LOCAL t1:BYTE, ; local variables
LOCAL Example
BubbleSort PROC
LOCAL temp:DWORD,
SwapFlag:BYTE
BubbleSort ENDP
BubbleSort PROC
push ebp ; enter 8, 0
mov ebp,esp
add esp,0FFFFFFF8h ; add -8 to ESP
mov esp,ebp ; leave
BubbleSort ENDP See LocalExample.asm
MASM generates:
LEA Instruction
LEA returns offsets of direct and indirect operands
OFFSET operator only returns constant offsets
LEA required when obtaining offsets of stack parameters & local variables
CopyString PROC,
count:DWORD
LOCAL temp[20]:BYTE
mov edi,OFFSET count ; invalid operand
mov esi,OFFSET temp ; invalid operand
lea edi,count ; ok
lea esi,temp ; ok
An example of C++ equivalent assembly code, see text
LEA Example
Suppose you have a Local variable at [ebp-8]
And you need the address of that local variable in ESI
You cannot use this:
mov esi, OFFSET [ebp-8] ; error
Use this instead:
lea esi,[ebp-8]
WriteStackFrame Procedure
Displays contents of current stack frame
Prototype:
WriteStackFrame PROTO,
numParam:DWORD, ; number of passed parameters
numLocalVal: DWORD, ; number of DWordLocal variables
numSavedReg: DWORD ; number of saved registers
All in Stack Frame
mov EBP,ESP
mov ESP,EBP
Prologue Epilogue
Parameter n
Parameter 1
Return Address
Local Variable 1
Local Variable m
Register 1
Register k
What is Recursion?
Recursively Calculating a Sum
Calculating a Factorial
What is Recursion?
The process created when . . .
A procedure calls itself
Procedure A calls procedure B, which in turn calls procedure A
Using a graph in which each node is a procedure and each edge is a procedure call, recursion forms a cycle:
Recursively Calculating a Sum
CalcSum PROC
cmp ecx,0 ; check counter value
jz L2 ; quit if zero
add eax,ecx ; otherwise, add to sum
dec ecx ; decrement counter
call CalcSum ; recursive call
CalcSum ENDP
The CalcSum procedure recursively calculates the sum of an array of integers. Receives: ECX = count. Returns: EAX = sum
Stack frame:
Calculating a Factorial (1 of 3)
int factorial(int n)
if(n == 0)
return n * factorial(n-1);
This function calculates the factorial of integer n. A new value of n is saved in each stack frame:
As each call instance returns, the product it returns is multiplied by the previous value of n.
154.unknown
Calculating a Factorial (2 of 3)
Factorial PROC
mov ebp,esp
mov eax,[ebp+8] ; get n
cmp eax,0 ; n > 0?
ja L1 ; yes: continue
mov eax,1 ; no: return 1
L1: dec eax
push eax ; Factorial(n-1)
call Factorial
; Instructions from this point on excursion when each recursive call returns.
ReturnFact:
mov ebx,[ebp+8] ; get n
mul ebx ; eax = eax * ebx
L2: pop ebp ; return EAX
ret 4 ; clean up stack
Factorial ENDP
Calculating a Factorial (3 of 3)
Suppose we want to calculate 12!
This diagram shows the first few stack frames created by recursive calls to Factorial
Each recursive call uses 12 bytes of stack space.
155.unknown
=> F: push ebp0, eax is 3
L1, eax=2, push 2
=> F: push ebp1, eax is 2
L1, eax=1, push 1
=> F: push ebp2, eax is 1
L1, eax=0, push 0
=> F: push ebp3, eax is 0
L2, pop ebp3
[ebp3+8] is 1, 1*1
L2, pop ebp2
[ebp2+8] is 2, 1*2
L2, pop ebp1
[ebp1+8] is 3, 2*3
L2, pop ebp0
RetuenMain
INVOKE, ADDR, PROC, and PROTO
INVOKE Directive
ADDR Operator
PROC Directive
PROTO Directive
Parameter Classifications
Example: Exchaning Two Integers
Debugging Tips
INVOKE Directive
In 32-bit mode, the INVOKE directive is a powerful replacement for Intel’s CALL instruction that lets you pass multiple arguments
INVOKE procedureName [, argumentList]
ArgumentList is an optional comma-delimited list of procedure arguments
Arguments can be:
immediate values and integer expressions
variable names
address and ADDR expressions
register names
Not in 64-bit mode!
INVOKE Examples
byteVal BYTE 10
wordVal WORD 1000h
; direct operands:
INVOKE Sub1,byteVal,wordVal
; address of variable:
INVOKE Sub2,ADDR byteVal
; register name, integer expression:
INVOKE Sub3,eax,(10 * 20)
; address expression (indirect operand):
INVOKE Sub4,[ebx]
ADDR Operator
myWord WORD ?
INVOKE mySub,ADDR myWord
Returns a near or far pointer to a variable, depending on which memory model your program uses:
Small model: returns 16-bit offset
Large model: returns 32-bit segment/offset
Flat model: returns 32-bit offset
Simple example:
Not in 64-bit mode!
PROC Directive (1 of 2)
Not in 64-bit mode!
The PROC directive declares a procedure
label PROC [attributes] [USES regList], paramList
The USES clause must be on the same line as PROC.
Attributes: distance, language type, visibility
ParamList is a list of parameters separated by commas.
label PROC, parameter1, parameter2, …, parameterN
Each parameter has the following syntax:
paramName : type
type must either be one of the standard ASM types (BYTE, SBYTE, WORD, etc.), or it can be a pointer to one of these types.
PROC Directive (2 of 2)
Alternate format permits parameter list to be on one or more separate lines:
label PROC,
The parameters can be on the same line . . .
param-1:type-1, param-2:type-2, . . ., param-n:type-n
Or they can be on separate lines:
param-1:type-1,
param-2:type-2,
param-n:type-n
comma required
Example: AddTwo Procedure
AddTwo PROC,
val1:DWORD, val2:DWORD
mov eax,val1
add eax,val2
AddTwo ENDP
___________________________
myData WORD 1000h
invoke AddTwo, 1, myData
AddTwo receives two integers and returns their sum in EAX.
See Params.asm
AddTwo PROC,
val1:DWORD, val2:DWORD
push ebp
mov ebp, esp
mov eax,val1
add eax,val2
ret 00008h
AddTwo ENDP
___________________________
sub esp, 002h
push myData
push +000000001h
call AddTwo
MASM Generates:
Example: FillArray
FillArray PROC,
pArray:PTR BYTE, fillVal:BYTE
arraySize:DWORD
mov ecx,arraySize
mov esi,pArray
mov al,fillVal
L1: mov [esi],al
FillArray ENDP
FillArray receives a pointer to an array of bytes, a single byte fill value that will be copied to each element of the array, and the size of the array.
PROTO Directive
Creates a procedure prototype
label PROTO paramList
Parameter list not permitted in 64-bit mode
Every procedure called by the INVOKE directive must have a prototype
PROTO Directive
Standard configuration: PROTO appears at top of the program listing, INVOKE appears in the code segment, and the procedure implementation occurs later in the program:
MySub PROTO ; procedure prototype
INVOKE MySub ; procedure call
MySub PROC ; procedure implementation
MySub ENDP
PROTO Example
Prototype for the ArraySum procedure, showing its parameter list:
Parameters are not permitted in 64-bit mode.
ArraySum PROC USES esi ecx,
ptrArray:PTR DWORD, ; points to the array
szArray:DWORD ; array size
ArraySum ENDP
ArraySum PROTO,
ptrArray:PTR DWORD, ; points to the array
szArray:DWORD ; array size
Assembly Time Argument Checking
mySub1 PROTO, p1:BYTE, p2:WORD, p3:PTR BYTE
invoke mySub1, byte_1, byte_1, ADDR byte_1
MASM Generates the following and no error detected:
0000001A 68 00000000 R * push OFFSET byte_1
0000001F A0 00000000 R * mov al, byte_1
00000024 0F B6 C0 * movzx eax, al
00000027 50 * push eax
00000028 A0 00000000 R * mov al, byte_1
0000002D 50 * push eax
0000002E E8 00000022 * call mySub1
Explain why use movzx and push eax?
See 8.4.4, P305-307 for details
Parameter Classifications
An input parameter is data passed by a calling program to a procedure.
The called procedure is not expected to modify the corresponding parameter variable, and even if it does, the modification is confined to the procedure itself.
An input-output parameter is a pointer to a variable containing input that will be both used and modified by the procedure.
The variable passed by the calling program is modified.
An output parameter is created by passing a pointer to a variable when a procedure is called.
The procedure does not use any existing data from the variable, but it fills in a new value before it returns.
Example: Exchanging Two Integers
Swap PROC USES eax esi edi,
pValX:PTR DWORD, ; pointer to first integer
pValY:PTR DWORD ; pointer to second integer
mov esi,pValX ; get pointers
mov edi,pValY
mov eax,[esi] ; get first integer
xchg eax,[edi] ; exchange with second
mov [esi],eax ; replace first integer
The Swap procedure exchanges the values of two 32-bit integers. pValX and pValY do not change values, but the integers they point to are modified.
Multimodule Programs
A multimodule program is a program whose source code has been divided up into separate ASM files.
Each ASM file (module) is assembled into a separate OBJ file.
All OBJ files belonging to the same program are linked using the link utility into a single EXE file.
This process is called static linking
Advantages
Large programs are easier to write, maintain, and debug when divided into separate source code modules.
When changing a line of code, only its enclosing module needs to be assembled again. Linking assembled modules requires little time.
A module can be a container for logically related code and data (think object-oriented here…)
encapsulation: procedures and variables are automatically hidden in a module unless you declare them public
Creating a Multimodule Program
Here are some basic steps to follow when creating a multimodule program:
Create the main module
Create a separate source code module for each procedure or set of related procedures
Create an include file that contains procedure prototypes for external procedures (ones that are called between modules)
Use the INCLUDE directive to make your procedure prototypes available to each module
Example: ArraySum Program
Let’s review the ArraySum program from Chapter 5.
Each of the four white rectangles will become a module. This will be a 32-bit application.
156.unknown
Sample Program output
Enter a signed integer: -25
Enter a signed integer: 36
Enter a signed integer: 42
The sum of the integers is: +53
INCLUDE File
INCLUDE Irvine32.inc
PromptForIntegers PROTO,
ptrPrompt:PTR BYTE, ; prompt string
ptrArray:PTR DWORD, ; points to the array
arraySize:DWORD ; size of the array
ArraySum PROTO,
ptrArray:PTR DWORD, ; points to the array
count:DWORD ; size of the array
DisplaySum PROTO,
ptrPrompt:PTR BYTE, ; prompt string
theSum:DWORD ; sum of the array
Saving and Restoring Registers
Push registers on stack just after assigning ESP to EBP
local registers are modified inside the procedure
MySub_ PROC
push ebp mov ebp,esp push ecx
push edx mov eax,[ebp+8]
; Do something…
pop edx pop ecx
MySub_ ENDP
Stack Affected by USES Operator
MySub1 PROC USES ecx edx
mov ebp,esp
mov eax,[ebp+8]
MySub1 ENDP
USES operator generates:
MySub1 PROC
mov ebp,esp
mov eax,[ebp+8]
Where is ebp+8 pointing to?
Passing 8-bit and 16-bit Arguments
Cannot push 8-bit values on stack
Pushing 16-bit operand may cause page fault or ESP alignment problem
incompatible with Windows API functions
Expand smaller arguments into 32-bit values, using MOVZX or MOVSX:
charVal BYTE ‘x’
movzx eax,charVal
call Uppercase
Passing 64-bit Arguments
Push high-order values on the stack first; work backward in memory
Results in little-endian ordering of data
longVal QWORD 1234567800ABCDEFh
push DWORD PTR longVal + 4 ; high doubleword
; 12345678
push DWORD PTR longVal ; low doubleword
call WriteHex64
What’s stack memory look like?
ef cd ab 00 78 56 34 12
Non-Doubleword Local Variables
Local variables can be different sizes
How created in the stack by LOCAL directive:
8-bit: assigned to next available byte
16-bit: assigned to next even (word) boundary
32-bit: assigned to next doubleword boundary
Local Byte Variable
Example1 PROC
LOCAL var1:BYTE
mov al,var1 ; [EBP – 1]
Example1 ENDP
As stack offsets default to 32 bits, decrement ESP by 4
Place var1 at [EBP-1] and leave three bytes below it unused (nu)
The Microsoft x64 Calling Convention
CALL subtracts 8 from RSP
First four parameters are placed in RCX, RDX, R8, and R9. Additional parameters are pushed on the stack.
Parameters less than 64 bits long are not zero extended
Return value in RAX if <= 64 bits
Java Bytecodes
Stack-oriented instr
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com