2015-07-21 9 views
37

Disclaimer: Sono appena iniziato con l'assembly x86. Ho imparato un po 'di SPIM all'università, ma non vale la pena menzionarlo.Manual Assembly vs GCC

Ho pensato di iniziare con quella che probabilmente è la funzione più semplice in libc, abs(). Abbastanza semplice in C:

long myAbs(long j) { 
    return j < 0 ? -j : j; 
} 

La mia versione in assemblea:

.global myAbs 
    .type myAbs, @function 
    .text 

myAbs: 
    test %rdi, %rdi 
    jns end 
    negq %rdi 
end: 
    movq %rdi, %rax 
    ret 

(Questo non funziona per gli interi a 32 bit, probabilmente perché RAX è un registro a 64 bit e il segno è probabilmente nella posizione sbagliata - Devo indagare su questo).

Ora qui è quello che fa gcc (GCC -O2 -S myAbs.c):

 .file "myAbs.c" 
     .section  .text.unlikely,"ax",@progbits 
.LCOLDB0: 
     .text 
.LHOTB0: 
     .p2align 4,,15 
     .globl myAbs 
     .type myAbs, @function 
myAbs: 
.LFB0: 
     .cfi_startproc 
     pushq %rbp 
     .cfi_def_cfa_offset 16 
     .cfi_offset 6, -16 
     movq %rsp, %rbp 
     .cfi_def_cfa_register 6 
     subq $4144, %rsp 
     orq  $0, (%rsp) 
     addq $4128, %rsp 
     movq %rdi, %rdx 
     sarq $63, %rdx 
     movq %fs:40, %rax 
     movq %rax, -8(%rbp) 
     xorl %eax, %eax 
     movq %rdi, %rax 
     xorq %rdx, %rax 
     subq %rdx, %rax 
     movq -8(%rbp), %rcx 
     xorq %fs:40, %rcx 
     jne  .L5 
     leave 
     .cfi_remember_state 
     .cfi_def_cfa 7, 8 
     ret 
.L5: 
     .cfi_restore_state 
     call [email protected] 
     .cfi_endproc 
.LFE0: 
     .size myAbs, .-myAbs 
     .section  .text.unlikely 
.LCOLDE0: 
     .text 
.LHOTE0: 
     .ident "GCC: (Gentoo Hardened 5.1.0 p1.2, pie-0.6.3) 5.1.0" 
     .section  .note.GNU-stack,"",@progbits 

Perché questa grande differenza? GCC produce sostanzialmente più istruzioni. Non riesco a immaginare che questo non sarà più lento del mio codice. Mi manca qualcosa? O sto facendo qualcosa di gravemente sbagliato qui?

+2

Forse il tuo GCC non si sente male? Il mio produce [meno istruzioni] (https://goo.gl/wG2v1X). (E Clang ama [mosse condizionali] (https://goo.gl/xpxKyi).) –

+7

"GCC: (Gentoo Hardened 5.1.0 p1.2, pie-0.6.3) 5.1.0" - Penso che questo sia la chiave. Il compilatore C rinforzato incorpora una protezione per lo smantellamento dello stack o qualcosa di simile. – davmac

+2

Molte delle chiamate iniziali sono per impostare lo stack e salvare l'indirizzo di ritorno (cosa che non si sta facendo). Sembra che ci sia qualche protezione dello stack in corso.Forse è possibile ottimizzare le impostazioni del compilatore per sbarazzarsi di alcuni sovraccarichi. – carloabelli

risposta

40

Per coloro che si chiedono che cosa il codice generato da cui proviene, prima nota che quando GCC compila myAbs con la protezione dello stack si trasformano in questa forma

long myAbs(long j) { 
    uintptr_t canary = __stack_chk_guard; 

    register long result = j < 0 ? -j : j; 

    if ((canary = canary^__stack_chk_guard) != 0) 
     __stack_chk_fail(); 
} 

Il codice per eseguire semplicemente j < 0 ? -j : j; è

movq %rdi, %rdx  ;RDX = j 
movq %rdi, %rax  ;RAX = j 
sarq $63, %rdx  ;RDX = 0 if j >=0, 0fff...ffh if j < 0 
xorq %rdx, %rax  ;Note: x xor 0ff...ffh = Not X, x xor 0 = x 
         ;RAX = j if j >=0, ~j if j < 0 
subq %rdx, %rax  ;Note: 0fff...ffh = -1 
         ;RAX = j+0 = j if j >= 0, ~j+1 = -j if j < 0 
         ;~j+1 = -j in two complement 

Analizzando il codice generato otteniamo

pushq %rbp 
    movq %rsp, %rbp  ;Standard prologue 

    subq $4144, %rsp  ;Allocate slight more than 4 KiB  
    orq  $0, (%rsp)  ;Perform a useless RW operation to test if there is enough stack space for __stack_chk_fail 

    addq $4128, %rsp  ;This leave 16 byte allocated for local vars 

    movq %rdi, %rdx  ;See above 
    sarq $63, %rdx  ;See above 

    movq %fs:40, %rax  ;Get the canary 
    movq %rax, -8(%rbp) ;Save it as a local var 
    xorl %eax, %eax  ;Clear it 

    movq %rdi, %rax  ;See above 
    xorq %rdx, %rax  ;See above 
    subq %rdx, %rax  ;See above 

    movq -8(%rbp), %rcx ;RCX = Canary 
    xorq %fs:40, %rcx  ;Check if equal to the original value 
    jne  .L5    ;If not fail 

    leave 
    ret 
.L5: 
    call [email protected] ;__stack_chk_fail is noreturn 

Quindi tutte le istruzioni aggiuntive sono per l'implementazione dello Stack Smashing Protector.

Grazie a FUZxxl per indicare l'uso delle prime istruzioni dopo il prologo.

+0

Il codice sta sottraendo 4144 e quindi aggiungendo 4128 a '% rsp' per assicurarsi che sia disponibile molto più spazio di stack. In caso contrario, è possibile aggirare il controllo dello stack esaurendo per primo lo stack (causando il blocco del processo per un motivo diverso che non è un controllo di stack non riuscito). – fuz

+0

@FUZxxl Potresti elaborare? Vuoi dire che dovrebbe proteggere da un eccesso di stack? Sono perplesso da quelle istruzioni –

+1

Consideriamo il caso in cui lo stack è quasi pieno e qualcuno sovrascrive il canarino. In questo caso, il codice potrebbe rilevare che il controllo dello stack non è riuscito, ma non c'è spazio sufficiente per eseguire '__stack_chk_fail', quindi invece di stampare un avvertimento da big-ass che il controllo dello stack fallisce, il codice si blocca solo con un errore di segmentazione, nascondendo il fatto che qualcuno abbia cercato di entrare nel programma. La sequenza di sottrazione, scrittura e aggiunta assicura che rimangano circa 4 KiB di spazio nello stack, il che dovrebbe essere sufficiente per eseguire "__stack_chk_fail". – fuz

6

Molte delle chiamate iniziali sono per impostare lo stack e salvare l'indirizzo di ritorno (cosa che non si sta facendo). Sembra che ci sono alcuni protezione dello stack in corso. Forse è possibile ottimizzare le impostazioni del compilatore per sbarazzarsi di alcuni sovraccarichi.

Forse aggiungere flag al compilatore come: -fno-stack-protector potrebbe ridurre questa differenza.

Sì, probabilmente è più lento del tuo assembly scritto a mano, ma offre molta più protezione e probabilmente vale la pena di essere sovraccaricato.

Per quanto riguarda il motivo per cui la protezione dello stack esiste ancora nonostante si tratti di una funzione foglia see here.