1

I have the following very simple X86 assembly:

shr cx, 1
jae short <addr>

So basically, the jump will be taken if the Carry flag is not set. In the case of shr, CF will be set to the value of the bit shifted out of the register.

I was wondering how the same will look like in C (I know it has to be something like if ((num >>> 1) <SOME COMPARISON>), as obviously you cannot check for the carry flag, and the value of the shifted bit is unknown.

I understand I'm probably missing something pretty obvious, but still can't figure out the answer.

Update: Thanks to all the comments and answers, although they don't really show what I need. I understand what the code does (checks for odd/even, then jumps if it's one of those). My question is really what kind of C code can generate exactly the instructions I specified (doesn't matter if 16/32/64 bits), which is shift + jump. I'm starting to think that either this is just ASM (which is unlikely, given the amount of times I've found the construct, but certainly possible) or some C code that does everything separate, but gets really optimized at some point, like bit rotation in modern compilers

  • This is equivalent to checking if the considered value is odd or even. And, it jumps if odd. – perror Mar 22 '18 at 8:08
2

Okay: JAE is synonym to JNC (jump if no carry flag).

And the docs for SHR say:”Bits shifted beyond the destination are first shifted into the CF flag.”

So you jump when the rightmost bit (before shifting) is 0.

For simplicity without the changing of cx:

if (cx & 1 == 0) ...         
  • This is only part of the solution as the shift also divides CX by 2 which could be relevant in the following program flow. – Uwe Plonus Apr 10 '18 at 8:33
  • You are correct. That is what I was trying to say with “for simplicity without the changing of cx”. – manduca Apr 10 '18 at 10:51
1

normally jae will be used when comparing unsigned type with a negative type

jge will be used when comparing an unsigned type with a positive type

you can try short , byte , char etc etc instead of unsigned int in the code below to get the cx register instead of eax register in the disassenbly that follows the code

for example this code will get a jae as below

#include <windows.h>
#include <stdio.h>
int  main (void)
{
    unsigned int a = 1;
    unsigned int b = ( ( (a >> 1) < -1 )  ? 0x1337 : 0xdead );
    return b;    
}

disassembly

cdb -c "g shotcmp!main;uf ." shotcmp.exe

Microsoft (R) Windows Debugger Version 10.0.16299.15 X86

0:000> cdb: Reading initial command 'g shotcmp!main;uf .'

shotcmp!main:
012a6830 55              push    ebp
012a6831 8bec            mov     ebp,esp
012a6833 83ec0c          sub     esp,0Ch
012a6836 c745f801000000  mov     dword ptr [ebp-8],1
012a683d 8b45f8          mov     eax,dword ptr [ebp-8]
012a6840 d1e8            shr     eax,1  <<<<<<<<<<<<<<<<
012a6842 83f8ff          cmp     eax,0FFFFFFFFh <<<<<<<<<<<<<<<<
012a6845 7309            jae     shotcmp!main+0x20 (012a6850) <<<<<<<<<<<<<<

shotcmp!main+0x17:
012a6847 c745fc37130000  mov     dword ptr [ebp-4],1337h
012a684e eb07            jmp     shotcmp!main+0x27 (012a6857)

shotcmp!main+0x20:
012a6850 c745fcadde0000  mov     dword ptr [ebp-4],0DEADh

shotcmp!main+0x27:
012a6857 8b4dfc          mov     ecx,dword ptr [ebp-4]
012a685a 894df4          mov     dword ptr [ebp-0Ch],ecx
012a685d 8b45f4          mov     eax,dword ptr [ebp-0Ch]
012a6860 8be5            mov     esp,ebp
012a6862 5d              pop     ebp
012a6863 c3              ret
0:000>

an even or odd testing code in c and asm the asm function uses jae to branch while c use je to branch testing for carry flag in c is not done usually

#include <stdio.h>
char *str[] = {"odd","even"} ;
void c_eve_or_odd( int num){
    (num & 1 ) ? printf("%4d%5s\t",num,str[0]) : printf ("%4d%5s\t",num,str[1]) ;  
}
void asm_eve_or_odd(int num) {
    __asm {
        mov ecx , num
        and ecx,1
        shr ecx ,1
        jae myeven
    }
    printf("%4d%5s\t",num,str[0]);
    goto doexit;
myeven:
    printf ("%4d%5s\t",num,str[1]);
    goto doexit;
doexit: 
    return;   
}
void main(void) {
    for(int i =-5; i<=5; i++) { 
        c_eve_or_odd(i);
        asm_eve_or_odd(i);
        printf("\n"); 
    }
}

enter image description here

just to satisfy my curiosity i grepped all windows binary in system32 for the pattern and i couldn't find one instance of shr cx,1 jae there are indeed shr cx ,1 but none followed by a jae

just a followup

grep all exe in c:\windows\system32\ for shr cx,1 sort and print uniq

for /f %i in ( 'dir /b *.exe ' ) do grep -obUaPH \x66\xd1\xe9 %i  >> checkforshrcx1.txt
uniq -w  5 checkforshrcx1.txt | sort

appidpolicyconverter.exe:18846:f╤Θ
autochk.exe:247427:f╤Θ
autofmt.exe:236801:f╤Θ
cleanmgr.exe:51672:f╤Θ
conhost.exe:86983:f╤Θ
dxcap.exe:479138:f╤Θ
krnl386.exe:34661:f╤Θ
ntkrnlpa.exe:52959:f╤Θ
ntoskrnl.exe:72750:f╤Θ
ntvdm.exe:215256:f╤Θ
PresentationSettings.exe:105027:f╤Θ
RelPost.exe:79287:f╤Θ
rstrui.exe:186600:f╤Θ
smss.exe:12952:f╤Θ
sppsvc.exe:2635363:f╤Θ
TVWSetup.exe:5441147:f╤Θ

grep all exe in c:\windows\system32\ for shr cx,1 followed by jae sort and print uniq

for /f %i in ( 'dir /b *.exe ' ) do grep -obUaPH \x66\xd1\xe9\x73 %i  >> checkforshrcx1jae.txt
ls -l checkforshrcx1jae.txt
-rw-rw-rw-  1  0 0 2018-03-23 10:53 checkforshrcx1jae.txt

cat checkforshrcx1jae.txt
  • Thank you for your answer! However, as you can see in my code, there is no extra cmp instruction. With the cmp is obvious to understand, but I still can't figure out what my example C code can be. – Edu Garcia Mar 21 '18 at 6:44
  • shr shifts the discarded bit into cf flag and checking carry flag is not easily codable in c ( are you sure if this snippet is not a part of handmade asm inlined etc i edited my answer and added a c evenorodd versus asm_evenoradd asm code uses jae and both code are comaparably similar – blabb Mar 21 '18 at 21:11
  • Well, I'm analyzing some binary code, I cannot assume this was not hand made, but my current assumption is that the code was C and heavily optimized. I just can't think of that piece of code that this might have come from, and given the answers I guess nobody thinks this came from C either. – Edu Garcia Mar 22 '18 at 4:38
  • well goodluck and update the question or write a self answer if you find out just to whet my curiosity i grepped for the pattern in all windows exes and i didnt happen to find an exe that used the construct – blabb Mar 23 '18 at 5:33
  • Thanks for all your help and great examples. Well, this actually came from an MS-DOS executable, so maybe new compilers do different tricks. I'm trying to find out what compiler was used for this, but that's also becoming a bit difficult. I'll update here if I ever find out, thanks again! – Edu Garcia Mar 23 '18 at 9:35
0

I cannot say if this is certanly the case here, but shift right is basicly divide by 2 so posted code is actualy testing for odd numbers.

0

You cannot write C code which produces exactly the given assembly sequence.

C is a higher level language which does also manage more than assembly.

Basically the given instruction sequence in assembly

shr cx, 1
jae short <addr>

Does the following

cx_temp = cx;
cx >>= 1;
if (cx_temp & 0x01) {
}

The problem is that such shortcuts like in assembly are not available in higher level languages as each assembly is different and higher level languages (like C) hide this differences to you and therefore create assembly level constructs that are more extensive than needed...

  • The question I'm trying to answer is what would the original C code (if this was indeed C) could look like. It might as well be like your example, if you think a C compiler could take what you wrote and optimize it to look like my assembly code. – Edu Garcia Apr 10 '18 at 10:50
0

Is the value of cx used again in both branches? If not, this can just be an optimization where the compiler is basically allowed to change the value of variables willy-nilly if they are never used again.

So something like:

int f(int x) {
    if((x&1)==0) {
        x>>=1;
        //Do stuff with x
    }
    return 0;
}

could compile to something like what you have. In one branch of the C code, from a high-level perspective x shouldn't be changed. But since x is never used again in that branch, the compiler is free to change it if that helps with optimization.

Even if cx is used again later, this kind of optimization is still possible, it's just a little harder to find a specific example.

  • I'll need to check the specific example again, but I think this code is done in multiple places in a loop, so the CX value is maintained and shifted until there is a 0 there, so I'd say yes, the value is reused. I'll reply back if it's not. – Edu Garcia Apr 11 '18 at 1:33
  • @EduGarcia Either way, posting more of the code would help. It's hard to say what code may have compiled to that with such a small snippet. – Chris Apr 11 '18 at 1:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.