2016-02-03 35 views
5

Il programma che sto invertendo fa semplice moltiplicazione tra il numero galleggiante e 8 byte intero:Come decompilare questo calcolo dell'assieme x87?

section .data 

va: dt 1.4426950408889634074 
vb: dd 0x42424242 
    dd 0x41414141 

section .text 
global main 

main: 
    fld tword[va] 
    fmul qword[vb] 
    ret 

risultati sotto gdb:

Breakpoint 1, 0x08048360 in main() 
(gdb) x/i $eip 
0x8048360 <main>:  fld TBYTE PTR ds:0x804953c 
0x8048366 <main+6>:  fmul QWORD PTR ds:0x8049546 
0x804836c <main+12>: ret 
(gdb) x/gx 0x8049546 
0x8049546 <vb>: 0x4141414142424242 
(gdb) si 
0x08048366 in main() 
0x0804836c in main() 
(gdb) info float 
=>R7: Valid 0x4014c726039c95268dc4 +3262848.902912714389 

sto cercando di ricreare questo programma in C (stesso 32bit ambiente):

#include <stdio.h> 

int main() { 

    unsigned long long vb = 0x4141414142424242LL; 
    float r, va = 1.4426950408889634074F; 

    r = va * vb; 
    printf("%f\n", r); 
} 

... ma ottengo risultati molto diversi:

$ ./test 
6783712964982603776.000000 

Cosa sto facendo male nel mio programma C?

+0

Che cos'è un amico x87? – m0skit0

+7

x87 è un sottoinsieme correlato al punto mobile del set di istruzioni dell'architettura x86. => https://en.wikipedia.org/wiki/X87 – MCan

+6

@ m0skit0 Uno migliore di un x86, ovviamente. – Neil

risposta

8

Nel codice asm si sta effettivamente moltiplicare due double s con l'istruzione fmul, non un float e un int. Per fare qualcosa di simile in C:

#include <stdio.h> 
#include <stdint.h> 
#include <string.h> 

int main() 
{ 
    uint64_t vbi = 0x4141414142424242ULL; // hex representation of double 
    double r, vb, va = 1.4426950408889634074; 

    memcpy(&vb, &vbi, sizeof(vb));  // copy hex to double 
    r = va * vb; 
    printf("va = %f, vb = %f, r = %f\n", va, vb, r); 
    return 0; 
} 

Risultato = va = 1.442695, vb = 2261634.517647, r = 3262848.902913.

LIVE DEMO

+0

Presume che 'long long int' e' double' abbiano la stessa larghezza. Può essere fatto in modo sicuro? –

+0

OK - è solo a scopo illustrativo, ma per rendere felici tutti lo cambierò in 'uint64_t'. –

+1

Questo è quello che stavo cercando, grazie! – MCan

3

Questo codice assembler non è fare ciò che si pensa che sta facendo:

main: 
    fld tword[va] 
    fmul qword[vb] 
    ret 

vi proponiamo semplice moltiplicazione tra il numero galleggiante e 8 byte integer. Questa è in realtà la moltiplicazione di un valore a virgola mobile doppio esteso di 10 byte per il doppio di 8 byte (non un intero di 8 byte) rappresentato da 0x4141414142424242. 0x4141414142424242 viene considerato come il bit di un valore a virgola mobile doppio di 8 byte dal codice, non un numero intero a 8 byte convertito in un valore a virgola mobile doppio.

Il codice per quello che avete creduto che stava accadendo avrebbe potuto guardato qualcosa come:

main: 
    fild qword[vb]  ; Convert 64-bit integer to an extended precision double in st0 
    fld tword[va]  ; st(0)=>st(1) st(0) = 10-byte float(va) 
    fmulp    ; Multiply st(0) and st(1). Result in st(0). 

Questo cancella solo la vostra errata interpretazione del codice assembler.