0

Compiling the following C program with GCC 6.4.1 using -O0 and -O2 gives the following results for the main() function.

int main(int argc, char *argv[]) {
  if (argc == 2) {
    printf("Checking License: %s\n", argv[1]);
    if (strcmp(argv[1], "AAAA-Z10N-42-OK") == 0) {
      printf("Access Granted!\n");
    } else {
      printf("WRONG!\n");
    }
  } else {
    printf("Usage: <key>\n");
  }
  return 0;
}

-O0

0x0000000000400586 <+0>:    push   rbp
0x0000000000400587 <+1>:    mov    rbp,rsp
0x000000000040058a <+4>:    sub    rsp,0x10
0x000000000040058e <+8>:    mov    DWORD PTR [rbp-0x4],edi
0x0000000000400591 <+11>:   mov    QWORD PTR [rbp-0x10],rsi
0x0000000000400595 <+15>:   cmp    DWORD PTR [rbp-0x4],0x2
0x0000000000400599 <+19>:   jne    0x4005ec <main+102>
0x000000000040059b <+21>:   mov    rax,QWORD PTR [rbp-0x10]
0x000000000040059f <+25>:   add    rax,0x8
0x00000000004005a3 <+29>:   mov    rax,QWORD PTR [rax]
0x00000000004005a6 <+32>:   mov    rsi,rax
0x00000000004005a9 <+35>:   mov    edi,0x400690
0x00000000004005ae <+40>:   mov    eax,0x0
0x00000000004005b3 <+45>:   call   0x400470 <printf@plt>
0x00000000004005b8 <+50>:   mov    rax,QWORD PTR [rbp-0x10]
0x00000000004005bc <+54>:   add    rax,0x8
0x00000000004005c0 <+58>:   mov    rax,QWORD PTR [rax]
0x00000000004005c3 <+61>:   mov    esi,0x4006a6
0x00000000004005c8 <+66>:   mov    rdi,rax
0x00000000004005cb <+69>:   call   0x400480 <strcmp@plt>
0x00000000004005d0 <+74>:   test   eax,eax
0x00000000004005d2 <+76>:   jne    0x4005e0 <main+90>
0x00000000004005d4 <+78>:   mov    edi,0x4006b6
0x00000000004005d9 <+83>:   call   0x400460 <puts@plt>
0x00000000004005de <+88>:   jmp    0x4005f6 <main+112>
0x00000000004005e0 <+90>:   mov    edi,0x4006c6
0x00000000004005e5 <+95>:   call   0x400460 <puts@plt>
0x00000000004005ea <+100>:  jmp    0x4005f6 <main+112>
0x00000000004005ec <+102>:  mov    edi,0x4006cd
0x00000000004005f1 <+107>:  call   0x400460 <puts@plt>
0x00000000004005f6 <+112>:  mov    eax,0x0
0x00000000004005fb <+117>:  leave  
0x00000000004005fc <+118>:  ret    

-O2

0x0000000000400490 <+0>:    cmp    edi,0x2
0x0000000000400493 <+3>:    push   rbx
0x0000000000400494 <+4>:    je     0x4004a4 <main+20>
0x0000000000400496 <+6>:    mov    edi,0x4006bd
0x000000000040049b <+11>:   call   0x400460 <puts@plt>
0x00000000004004a0 <+16>:   xor    eax,eax
0x00000000004004a2 <+18>:   pop    rbx
0x00000000004004a3 <+19>:   ret    
0x00000000004004a4 <+20>:   mov    rbx,rsi
0x00000000004004a7 <+23>:   mov    rsi,QWORD PTR [rsi+0x8]
0x00000000004004ab <+27>:   mov    edi,0x400680
0x00000000004004b0 <+32>:   xor    eax,eax
0x00000000004004b2 <+34>:   call   0x400470 <printf@plt>
0x00000000004004b7 <+39>:   mov    rdi,QWORD PTR [rbx+0x8]
0x00000000004004bb <+43>:   mov    esi,0x400696
0x00000000004004c0 <+48>:   call   0x400480 <strcmp@plt>
0x00000000004004c5 <+53>:   test   eax,eax
0x00000000004004c7 <+55>:   jne    0x4004d5 <main+69>
0x00000000004004c9 <+57>:   mov    edi,0x4006a6
0x00000000004004ce <+62>:   call   0x400460 <puts@plt>
0x00000000004004d3 <+67>:   jmp    0x4004a0 <main+16>
0x00000000004004d5 <+69>:   mov    edi,0x4006b6
0x00000000004004da <+74>:   call   0x400460 <puts@plt>
0x00000000004004df <+79>:   jmp    0x4004a0 <main+16>

The question, as I've stated in the title, is why is GCC clearing eax with

xor eax,eax

instead of the more explicit

mov eax,0x0

in this case. It doesn't really matter here as it is reached at most once per program execution, but let us assume this happened in a critical part of the application. Is there any reason for xoring being faster than moving zero into a register?

Bernardo Sulzbach
  • 1,293
  • 10
  • 26
  • 3
    `xor reg, reg` has a shorter encoding than `mov reg, imm`, because `imm` needs to be 32-bit to clear a 32- or 64 bit register entirely. – EOF Sep 29 '17 at 03:45
  • 7
    xor is the canonical way to zero registers in x86 asm. For experienced programmers, it's just as explicit as `mov`. (And `mov` looks wrong!) – Peter Cordes Sep 29 '17 at 03:46

0 Answers0