2016-02-03 58 views
5

Program jestem cofania robi proste mnożenie między liczbą typu float i 8 bajtów całkowita:Jak dekompilować obliczenia zespołu x87?

section .data 

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

section .text 
global main 

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

wyniku z 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 

próbuję odtworzyć ten program w C (sam 32bit środowisko):

#include <stdio.h> 

int main() { 

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

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

... ale ja się bardzo różne wyniki:

$ ./test 
6783712964982603776.000000 

Co robię źle w moim programie C?

+0

Co to jest kolega x87? – m0skit0

+7

x87 jest podzbiorem zmiennoprzecinkowym zestawu instrukcji architektury x86. => https://en.wikipedia.org/wiki/X87 – MCan

+6

@ m0skit0 Oczywiście lepiej niż x86. – Neil

Odpowiedz

8

W kodzie asm jesteś rzeczywiście pomnożenie dwóch double S z instrukcją fmul, a nie float i int. Zrobić coś podobnego w 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; 
} 

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

LIVE DEMO

+0

To zakłada, że ​​'long long int' i' double' mają tę samą szerokość. Czy można to bezpiecznie wykonać? –

+0

OK - to tylko w celach ilustracyjnych, ale aby wszyscy byli zadowoleni, zmienię go na "uint64_t". –

+1

Tego właśnie szukałem, dzięki! – MCan

3

Ten kod asemblera nie robi tego, co myślisz, że to robi:

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

Sugerujesz proste mnożenie między liczbą typu float i 8 bajtów całkowitej. Jest to w rzeczywistości mnożenie 10-bajtowej rozszerzonej podwójnej liczby zmiennoprzecinkowej przez 8-bajtowe podwójne (nie 8-bajtowe liczby całkowite) reprezentowane przez 0x4141414142424242. 0x4141414142424242 jest traktowany przez bity jako bity 8-bajtowej podwójnej liczby zmiennoprzecinkowej, a nie jako 8-bajtowa liczba całkowita przekształcona na podwójną wartość zmiennoprzecinkową.

Kod za to, co uważa się dzieje mogło wyglądał mniej więcej tak:

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). 

To właśnie czyści swój błędnej interpretacji kodu asemblera.