I have been studying how floating point operations are handled in an x86 architecture by disassembling C code. The OS used is a 64 bit linux, while the code was compiled for a 32 bit machine.
Here is the C source code:
#include <stdio.h>
#include <float.h>
int main(int argc, char *argv[])
{
float a, b;
float c, d;
printf("%u\n",sizeof(float));
a = FLT_MAX;
b = 5;
c = a / b;
d = (float) a / (float) b;
printf("%f %f \n",c,d);
return 0;
}
And here is the disassembled version of the main function of the 32 bit exe:
804841c: 55 push ebp
804841d: 89 e5 mov ebp,esp
804841f: 83 e4 f0 and esp,0xfffffff0
8048422: 83 ec 30 sub esp,0x30
8048425: c7 44 24 04 04 00 00 mov DWORD PTR [esp+0x4],0x4
804842c: 00
804842d: c7 04 24 20 85 04 08 mov DWORD PTR [esp],0x8048520
8048434: e8 b7 fe ff ff call 80482f0 <printf@plt>
8048439: a1 2c 85 04 08 mov eax,ds:0x804852c
804843e: 89 44 24 2c mov DWORD PTR [esp+0x2c],eax
8048442: a1 30 85 04 08 mov eax,ds:0x8048530
8048447: 89 44 24 28 mov DWORD PTR [esp+0x28],eax
804844b: d9 44 24 2c fld DWORD PTR [esp+0x2c]
804844f: d8 74 24 28 fdiv DWORD PTR [esp+0x28]
8048453: d9 5c 24 24 fstp DWORD PTR [esp+0x24]
8048457: d9 44 24 2c fld DWORD PTR [esp+0x2c]
804845b: d8 74 24 28 fdiv DWORD PTR [esp+0x28]
804845f: d9 5c 24 20 fstp DWORD PTR [esp+0x20]
8048463: d9 44 24 20 fld DWORD PTR [esp+0x20]
8048467: d9 44 24 24 fld DWORD PTR [esp+0x24]
804846b: d9 c9 fxch st(1)
804846d: dd 5c 24 0c fstp QWORD PTR [esp+0xc]
8048471: dd 5c 24 04 fstp QWORD PTR [esp+0x4]
8048475: c7 04 24 24 85 04 08 mov DWORD PTR [esp],0x8048524
804847c: e8 6f fe ff ff call 80482f0 <printf@plt>
8048481: b8 00 00 00 00 mov eax,0x0
8048486: c9 leave
8048487: c3 ret
8048488: 66 90 xchg ax,ax
804848a: 66 90 xchg ax,ax
804848c: 66 90 xchg ax,ax
804848e: 66 90 xchg ax,ax
What I have trouble understanding is the lines where the floating point values are transferred to the registers. Specifically:
mov eax,ds:0x804852c
mov eax,ds:0x8048530
In my understanding, the instructions should be equal to mov eax,[0x804852c] and mov eax,[0x8048530] respectively since in 32 bit mode the ds register usually points to the whole 32 bit space and is usually 0. However when I check register values the ds is not 0. It has the value
ds 0x2b
Given that value, shouldn't the calculation be
0x2b *0x10 + 0x8048520
However the floats are stored in 0x8048520 and 0x8048530 which is like having a value of 0 in DS. Can anyone explain to me why is this?
DS in protected mode works completely differently. It's not a shifted part of the linear address, like in real mode, it's an index into a segment table which contains the base address of the segment. The OS kernel maintains the segment table, userland code can't.
That said, ignore the ds: prefix. The disassembler is explicitly spelling out the default behavior, that's it. This command uses DS as the selector by default; so the disassembler thought it'd mention. The OS would initialize DS to something that makes sense for the process, and the same value of DS will be used throughout the whole process.