Last modified on 15 July 2014, at 19:11

X86 Assembly/Control Flow

Almost all programming languages have the ability to change the order in which statements are evaluated, and assembly is no exception. The instruction pointer (EIP) register contains the address of the next instruction to be executed. To change the flow of control, the programmer must be able to modify the value of EIP. This is where control flow functions come in.

mov eip, label   ; wrong
jmp label        ; right


Comparison InstructionsEdit

test arg1, arg2 GAS Syntax
test arg2, arg1 Intel syntax


Performs a bit-wise logical AND on arg1 and arg2 the result of which we will refer to as Temp and sets the ZF(zero), SF(sign) and PF(parity) flags based on Temp. Temp is then discarded.


Operands

arg1

  • Register
  • Immediate

arg2

  • AL/AX/EAX (only if arg1 is immediate)
  • Register
  • Memory


Modified flags

  • SF <- MostSignificantBit(Temp)
  • If Temp == 0 ZF <- 1 else ZF <- 0
  • PF <- BitWiseXorNor(Temp[Max-1:0])
  • CF <- 0
  • OF <- 0
  • AF is undefined


cmp arg2, arg1 GAS Syntax
cmp arg1, arg2 Intel syntax


Performs a comparison operation between arg1 and arg2. The comparison is performed by a (signed) subtraction of arg2 from arg1, the results of which can be called Temp. Temp is then discarded. If arg2 is an immediate value it will be sign extended to the length of arg1. The EFLAGS register is set in the same manner as a sub instruction.

Note that the GAS/AT&T syntax can be rather confusing, as for example cmp $0, %rax followed by jl branch will branch if %rax < 0 (and not the opposite as might be expected from the order of the operands).


Operands

arg1

  • AL/AX/EAX (only if arg2 is immediate)
  • Register
  • Memory

arg2

  • Register
  • Immediate
  • Memory


Modified flags

  • SF <- MostSignificantBit(Temp)
  • If Temp == 0 ZF <- 1 else ZF <- 0
  • PF <- BitWiseXorNor(Temp[Max-1:0])
  • CF, OF and AF

Jump InstructionsEdit

The jump instructions allow the programmer to (indirectly) set the value of the EIP register. The location passed as the argument is usually a label. The first instruction executed after the jump is the instruction immediately following the label. All of the jump instructions, with the exception of jmp, are conditional jumps, meaning that program flow is diverted only if a condition is true. These instructions are often used after a comparison instruction (see above), but since many other instructions set flags, this order is not required.

See X86_Assembly/X86_Architecture#EFLAGS_Register for more information about the flags and their meaning.

Unconditional JumpsEdit

jmp loc

Loads EIP with the specified address (i.e. the next instruction executed will be the one specified by jmp).

Jump on EqualityEdit

je loc

ZF = 1

Loads EIP with the specified address, if operands of previous CMP instruction are equal. For example:

mov $5, ecx
mov $5, edx
cmp ecx, edx
je equal
; if it did not jump to the label equal, then this means ecx and edx are not equal.
equal:
; if it jumped here, then this means ecx and edx are equal

Jump on InequalityEdit

jne loc

ZF = 0

Loads EIP with the specified address, if operands of previous CMP instruction are not equal.

Jump if GreaterEdit

jg loc

ZF = 0 and SF = OF

Loads EIP with the specified address, if first operand of previous CMP instruction is greater than the second (performs signed comparison).

jge loc

SF = OF

Loads EIP with the specified address, if first operand of previous CMP instruction is greater than or equal to the second (performs signed comparison).

ja loc

CF = 0 and ZF = 0

Loads EIP with the specified address, if first operand of previous CMP instruction is greater than the second. ja is the same as jg, except that it performs an unsigned comparison.

jae loc

CF = 0

Loads EIP with the specified address, if first operand of previous CMP instruction is greater than or equal to the second. jae is the same as jge, except that it performs an unsigned comparison.

Jump if LessEdit

jl loc

The criteria required for a JL is that SF <> OF, loads EIP with the specified address, if the criteria is meet. So either SF or OF can be set but not both in order to satisfy this criteria. If we take the SUB(which is basically what a CMP does) instruction as an example, we have:

arg2 - arg1

With respect to SUB and CMP there are several cases that fulfill this criteria:

  1. arg2 < arg1 and the operation does not have overflow
  2. arg2 < arg1 and the operation has an overflow

In case 1) SF will be set but not OF and in case 2) OF will be set but not SF since the overflow will reset the most significant bit to zero and thus preventing SF being set. The SF <> OF criteria avoids the cases where:

  1. arg2 > arg1 and the operation does not have overflow
  2. arg2 > arg1 and the operation has an overflow
  3. arg2 == arg1

In case 1) neither SF nor OF are set, in case 2) OF will be set and SF will be set since the overflow will reset the most significant bit to one and in case 3) neither SF nor OF will be set.

Example The example code below runs the five cases outlined above and prints out whether SF and OF are equal or not:

;
; nasm -felf32 -g jlFlagsCheck.asm
; gcc -o jlFlagsCheck jlFlagsCheck.o
;
global main 
 
extern printf
 
section .data
	sfneofStr: db 'SF <> OF', 0xA, 0
	sfeqofStr: db 'SF == OF', 0xA, 0
 
section .bss
 
section .text
	main:
 
;
; Functions will follow the cdecl call convention
;
 
	;
	; arg2 < arg1 and no overflow
	;
	mov	eax, 1
	cmp	eax, 2
	call	checkSFNEOF
 
	;
	; arg2 < arg1 and overflow
	;
	mov	al, -2
	cmp	al, 127
	call	checkSFNEOF
 
	;
	; arg2 > arg1 and no overflow
	;
	mov	eax, 2
	cmp	eax, 1 
	call	checkSFNEOF
 
	;
	; arg2 > arg1 and overflow
	;
	mov	al, 127
	cmp	al, -1 
	call	checkSFNEOF
 
	;
	; arg2 == arg1
	;
	mov	eax, 2
	cmp	eax, 2 
	call	checkSFNEOF
 
	call	exit
 
;
; Check if SF <> OF, which means the condition for jump less would be meet. 
;
checkSFNEOF:
	push	ebp
	mov	ebp, esp
 
	jl	SFNEOF
	jmp	SFEQOF	
 
SFNEOF:
	push	dword sfneofStr 
	call	printf
	jmp	checkSFNEOFDone
 
SFEQOF:
	push	dword sfeqofStr 
	call	printf
 
checkSFNEOFDone:
 
	leave
	ret
 
exit:
				;
				; Call exit(3) syscall
				;	void exit(int status)
				;
	mov	ebx, 0		; Arg one: the status
	mov	eax, 1		; Syscall number:
	int 	0x80

The expected output is as follows:

SF <> OF
SF <> OF
SF == OF
SF == OF
SF == OF


jle loc

The criteria required for a JLE is that SF <> OF or ZF == 1, loads EIP with the specified address if the criteria is meet. See the JL section for a more detailed description of the criteria, the one point of departure would be that arg2 == arg1 fulfills the criteria for JLE since ZF == 1.


Example The example code below runs the five cases outlined previously and prints out whether SF and OF are equal or not and whether ZF == 1 or not:

;
; nasm -felf32 -g jleFlagsCheck.asm
; gcc -o jleFlagsCheck jleFlagsCheck.o
;
global main 
 
extern printf
 
section .data
	sfneofStr:	db 'SF <> OF and ZF %s 1', 0xA, 0
	sfeqofStr:	db 'SF == OF and ZF %s 1', 0xA, 0
	ne:	  	db '<>', 0
	eq:		db '==', 0
section .bss
 
section .text
	main:
 
;
; Functions will follow the cdecl call convention
;
	;
	; arg2 < arg1 and no overflow
	;
	mov	eax, 1
	cmp	eax, 2
	call	checkJLECriteria
 
	;
	; arg2 < arg1 and overflow
	;
	mov	al, -2
	cmp	al, 127
	call	checkJLECriteria
 
	;
	; arg2 == arg1
	;
	mov	eax, 2
	cmp	eax, 2 
	call	checkJLECriteria
 
	;
	; arg2 > arg1 and no overflow
	;
	mov	eax, 2
	cmp	eax, 1 
	call	checkJLECriteria
 
	;
	; arg2 > arg1 and overflow
	;
	mov	al, 127
	cmp	al, -1 
	call	checkJLECriteria
 
	call	exit
 
;
; Check the criteria for JLE, either SF <> OF or ZF == 1
;
checkJLECriteria:
	push	ebp
	mov	ebp, esp
 
	jz	ZFOne
	jmp	ZFZero
 
ZFOne:
	push	dword eq
	jmp	SFOFCheck
 
ZFZero:
	push	dword ne
 
SFOFCheck:
 
	jl	SFNEOF
	jmp	SFEQOF	
 
SFNEOF:
	push	dword sfneofStr 
	call	printf
	jmp	checkJLECriteriaDone
 
SFEQOF:
	push	dword sfeqofStr 
	call	printf
 
checkJLECriteriaDone:
 
	leave
	ret
 
exit:
				;
				; Call exit(3) syscall
				;	void exit(int status)
				;
	mov	ebx, 0		; Arg one: the status
	mov	eax, 1		; Syscall number:
	int 	0x80

The expected output is as follows:

SF <> OF and ZF <> 1
SF <> OF and ZF <> 1
SF == OF and ZF == 1
SF == OF and ZF <> 1
SF == OF and ZF <> 1


jb loc

CF = 1

Loads EIP with the specified address, if first operand of previous CMP instruction is less than the second. jb is the same as jl, except that it performs an unsigned comparison.

jbe loc

CF = 1 or ZF = 1

Loads EIP with the specified address, if first operand of previous CMP instruction is less than or equal to the second. jbe is the same as jle, except that it performs an unsigned comparison.

Jump on OverflowEdit

jo loc

OF = 1

Loads EIP with the specified address, if the overflow bit is set on a previous arithmetic expression.

jno loc

OF = 0

Loads EIP with the specified address, if the overflow bit is not set on a previous arithmetic expression.

Jump on ZeroEdit

jz loc

ZF = 1

Loads EIP with the specified address, if the zero bit is set from a previous arithmetic expression. jz is identical to je.

jnz loc

ZF = 0

Loads EIP with the specified address, if the zero bit is not set from a previous arithmetic expression. jnz is identical to jne.

Jump on SignEdit

js loc

SF = 1

Loads EIP with the specified address, if the sign bit is set from a previous arithmetic expression.

jns loc

SF = 0

Loads EIP with the specified address, if the sign bit is not set from a previous arithmetic expression.

Function CallsEdit

call proc

Pushes the address of the next opcode onto the top of the stack, and jumps to the specified location. This is used mostly for subroutines.

ret [val]

Loads the next value on the stack into EIP, and then pops the specified number of bytes off the stack. If val is not supplied, the instruction will not pop any values off the stack after returning.

Loop InstructionsEdit

loop arg

The loop instruction decrements ECX and jumps to the address specified by arg unless decrementing ECX caused its value to become zero. For example:

 mov ecx, 5
 start_loop:
 ; the code here would be executed 5 times
 loop start_loop


loop does not set any flags.

loopx arg

These loop instructions decrement ECX and jump to the address specified by arg if their condition is satisfied (that is, a specific flag is set), unless decrementing ECX caused its value to become zero.

  • loope loop if equal
  • loopne loop if not equal
  • loopnz loop if not zero
  • loopz loop if zero

Enter and LeaveEdit

enter arg

Creates a stack frame with the specified amount of space allocated on the stack.

leave

destroys the current stack frame, and restores the previous frame. Using Intel syntax this is equivalent to:

mov esp, ebp
pop ebp

This will set EBP and ESP to their respective value before the function prologue began therefore reversing any modification to the stack that took place during the prologue.

Other Control InstructionsEdit

hlt

Halts the processor. Execution will be resumed after processing next hardware interrupt, unless IF is cleared.

nop

No operation. This instruction doesn't do anything, but wastes an instruction cycle in the processor. This instruction is often represented as an XCHG operation with the operands EAX and EAX.

lock

Asserts #LOCK prefix on next instruction.

wait

Waits for the FPU to finish its last calculation.