• No results found

Ternary conditional operator

In document Reverse Engineering for Beginners pdf (Page 155-159)

The ternary conditional operator in C/C++ has the following syntax:

expression ? expression : expression

Here is an example:

const char* f (int a) {

return a==10 ? "it is ten" : "it is not ten"; };

12.3.1

x86

Old and non-optimizing compilers generate assembly code just as if anif/elsestatement was used: Listing 12.18: Non-optimizing MSVC 2008

$SG746 DB 'it is ten', 00H $SG747 DB 'it is not ten', 00H

tv65 = -4 ; this will be used as a temporary variable _a$ = 8

_f PROC

push ebp mov ebp, esp push ecx

; compare input value with 10

cmp DWORD PTR _a$[ebp], 10 ; jump to $LN3@f if not equal

jne SHORT $LN3@f

; store pointer to the string into temporary variable:

mov DWORD PTR tv65[ebp], OFFSET $SG746 ; 'it is ten' ; jump to exit

jmp SHORT $LN4@f $LN3@f:

; store pointer to the string into temporary variable:

mov DWORD PTR tv65[ebp], OFFSET $SG747 ; 'it is not ten' $LN4@f:

; this is exit. copy pointer to the string from temporary variable to EAX. mov eax, DWORD PTR tv65[ebp]

mov esp, ebp pop ebp

ret 0

_f ENDP

Listing 12.19: Optimizing MSVC 2008

$SG792 DB 'it is ten', 00H $SG793 DB 'it is not ten', 00H _a$ = 8 ; size = 4

_f PROC

; compare input value with 10

cmp DWORD PTR _a$[esp-4], 10

mov eax, OFFSET $SG792 ; 'it is ten' ; jump to $LN4@f if equal

je SHORT $LN4@f

mov eax, OFFSET $SG793 ; 'it is not ten' $LN4@f:

ret 0

_f ENDP

Newer compilers are more concise:

Listing 12.20: Optimizing MSVC 2012 x64

$SG1355 DB 'it is ten', 00H $SG1356 DB 'it is not ten', 00H a$ = 8

f PROC

; load pointers to the both strings

lea rdx, OFFSET FLAT:$SG1355 ; 'it is ten' lea rax, OFFSET FLAT:$SG1356 ; 'it is not ten' ; compare input value with 10

cmp ecx, 10

; if equal, copy value from RDX ("it is ten")

; if not, do nothing. pointer to the string "it is not ten" is still in RAX as for now. cmove rax, rdx

ret 0

f ENDP

12.3.2

ARM

Optimizing Keil for ARM mode also uses the conditional instructionsADRcc:

Listing 12.21: Optimizing Keil 6/2013 (ARM mode)

f PROC

; compare input value with 10 CMP r0,#0xa

; if comparison result is EQual, copy pointer to the "it is ten" string into R0 ADREQ r0,|L0.16| ; "it is ten"

; if comparison result is Not Equal, copy pointer to the "it is not ten" string into R0 ADRNE r0,|L0.28| ; "it is not ten"

BX lr

ENDP |L0.16|

DCB "it is ten",0 |L0.28|

DCB "it is not ten",0

Without manual intervention, the two instructionsADREQandADRNEcannot be executed in the same run.

Optimizing Keil for Thumb mode needs to use conditional jump instructions, since there are no load instructions that support conditional flags:

Listing 12.22: Optimizing Keil 6/2013 (Thumb mode)

f PROC

; compare input value with 10 CMP r0,#0xa ; jump to |L0.8| if EQual

BEQ |L0.8|

ADR r0,|L0.12| ; "it is not ten"

BX lr

|L0.8|

ADR r0,|L0.28| ; "it is ten"

BX lr

ENDP |L0.12|

DCB "it is not ten",0 |L0.28|

DCB "it is ten",0

12.3.3

ARM64

Optimizing GCC (Linaro) 4.9 for ARM64 also uses conditional jumps:

Listing 12.23: Optimizing GCC (Linaro) 4.9

f:

cmp x0, 10

beq .L3 ; branch if equal adrp x0, .LC1 ; "it is ten" add x0, x0, :lo12:.LC1

ret .L3:

adrp x0, .LC0 ; "it is not ten" add x0, x0, :lo12:.LC0

ret .LC0:

.string "it is ten" .LC1:

.string "it is not ten"

That is because ARM64 does not have a simple load instruction with conditional flags, likeADRccin 32-bit ARM mode or CMOVcc in x86 [ARM13a, p390, C5.5]. It has, however, “Conditional SELect” instruction (CSEL), but GCC 4.9 does not seem to be smart enough to use it in such piece of code.

12.3.4

MIPS

Unfortunately, GCC 4.4.5 for MIPS is not very smart, either:

Listing 12.24: Optimizing GCC 4.4.5 (assembly output)

$LC0:

.ascii "it is not ten\000" $LC1:

.ascii "it is ten\000" f:

li $2,10 # 0xa

; compare $a0 and 10, jump if equal: beq $4,$2,$L2

nop ; branch delay slot

; leave address of "it is not ten" string in $v0 and return: lui $2,%hi($LC0)

j $31

addiu $2,$2,%lo($LC0) $L2:

; leave address of "it is ten" string in $v0 and return: lui $2,%hi($LC1)

j $31

addiu $2,$2,%lo($LC1)

12.3.5

Let’s rewrite it in anif/elseway

const char* f (int a)

{

if (a==10)

return "it is ten"; else

return "it is not ten"; };

Interestingly, optimizing GCC 4.8 for x86 was also able to useCMOVccin this case: Listing 12.25: Optimizing GCC 4.8

.LC0:

.string "it is ten" .LC1:

.string "it is not ten" f:

.LFB0:

; compare input value with 10

cmp DWORD PTR [esp+4], 10

mov edx, OFFSET FLAT:.LC1 ; "it is not ten" mov eax, OFFSET FLAT:.LC0 ; "it is ten"

; if comparison result is Not Equal, copy EDX value to EAX ; if not, do nothing

cmovne eax, edx ret

Optimizing Keil in ARM mode generates code identical to listing.12.21. But the optimizing MSVC 2012 is not that good (yet).

12.3.6

Conclusion

In document Reverse Engineering for Beginners pdf (Page 155-159)