2015-10-20 115 views
0

我一直在研究如何通過拆解C代碼在x86架構中處理浮點操作。使用的操作系統是一個64位的Linux,而代碼是爲32位機器編譯的。32位架構的DS段寄存器

這裏是C源代碼:

#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; 
} 

這裏是32位的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 <[email protected]> 
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 <[email protected]> 
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 

什麼我有麻煩理解是行哪裏浮點值被傳送到寄存器。具體做法是:

mov eax,ds:0x804852c 
mov eax,ds:0x8048530 

在我的理解,所述指令應等於MOV EAX,[0x804852c]和MOV EAX,[0x8048530]分別因爲在32位模式ds寄存器通常指向整個32位的空間和通常是0。但是當我檢查寄存器值的ds是不爲0,具有

ds    0x2b 

鑑於值,不應計算是

0x2b *0x10 + 0x8048520 

然而FLOA值ts存儲在0x8048520和0x8048530中,就像DS中的值爲0一樣。任何人都可以向我解釋這是爲什麼?

+0

http://stackoverflow.com/questions/3819699/what-does-ds40207a-mean-in-assembly –

回答

5

保護模式下的DS工作原理完全不同。它不是線性地址的移位部分,就像在實模式中一樣,它是包含段基地址的段表的索引。 OS內核維護段表,用戶級代碼不能。

也就是說,忽略ds:前綴。反彙編器明確地說明了默認行爲,就是這樣。該命令默認使用DS作爲選擇器;所以反彙編者認爲它會提到。操作系統會將DS初始化爲對於該過程有意義的事情,並且在整個過程中將使用相同的DS值。

+0

簡單地說,他們搞砸了intel語法反彙編。應該只顯示前綴,如果它真的存在於代碼中,並且它應該使用'[]'作爲內存引用。 'mov eax,[0x804852c]'。假設它顯示前綴,以便人們不會將它與'mov eax,0x804852c'混淆,後者是直接的。 – Jester

0

由於代碼是32位保護模式,因此如Seva所述,DS寄存器用作表中的索引。這稱爲GDT或LDT,取決於它是全局還是本地流程。全局描述符表&本地描述符表。

每個條目指定了許多不同的參數。這些包括所描述的內存區域的基準,限制和粒度,訪問類型和特權級別。

完全可能有兩種描述符在每種方式中都是相同的 - 這些顯然會在表中具有不同的索引,並會導致DS的不同值。

-

它還允許您訪問位於內存中的任何地方的地址空間,就好像它是在內存中的最底層。拿卡片的線性幀緩衝區的視頻內存爲例。不同的卡片實現將把它定位在不同的地址,但是由於描述符中的基本字段,您仍然可以以完全透明的方式訪問這些不同的區域。

我有一張卡片位於內存0xE0000000,而另一張卡片位於0xC0000000。現在我可能查詢卡後,將此地址保存到一個全局變量,然後在任何繪圖操作加載此var並將其添加到該區域中計算的偏移量。幸運的是,描述符機制使我們能夠做得比這更好。

當我設置GDT時,我使用從卡片返回的值來指定將由表中特定位置中的描述符引用的內存區域的基址,從而使繪圖代碼不知道或不在意幀緩衝區駐留在物理內存中。

訪問這很簡單,只要

push es 
mov ax, LinearFrameBufferSel 
mov es, ax 

雖然指定其中存儲器位於,我可以硬編碼數據被裝載作爲GDT像這樣:

; point to memory r/w at E000 0000 - this should not be hard-coded! we should get the value from the video card, using VBE extension functions 
    ; accessed with ds=40 
LinearFrameBufferSel equ $ - gdt 
    dw 0xffff     ; limit low  ; [0-15] - index 40 
    dw 0x0000     ; base low  ; [0-15] 
    db 0x00     ; base middle ; [16-23] 
    db 0x92     ; access  ; 
    db 0xCF     ; granularity ; flags(4) - limit(4) [16-19] 
    db 0xE0     ; base hi 

    ; ;  point to memory r/w at 000A 0000     ; index 48 
    ; ; accessed with ds=48 
BankedVidMemSel equ $ - gdt 
    dw 0xffff     ; limit low  ; [0-15] 
    dw 0x0000     ; base low  ; [0-15] 
    db 0x0A     ; base middle ; [16-23] 
    db 0x92     ; access  ; 
    db 0xCF     ; granularity ; flags(4) - limit(4) [16-19] 
    db 0x00     ; base hi 

    ; ;  point to memory r/w at 000B 8000     ; index 56 
    ; ; accessed with ds=56 
TextVidMemSel equ $ - gdt 
    dw 0xffff     ; limit low  ; [0-15] 
    dw 0x8000     ; base low  ; [0-15] 
    db 0x0B     ; base middle ; [16-23] 
    db 0x92     ; access  ; 
    db 0xCF     ; granularity ; flags(4) - limit(4) [16-19] 
    db 0x00     ; base hi 

VideoBackBufferSel equ $ - gdt        ; point to memory 0x800000 lower than 0xE0000000 (= 8meg lower than 3 gig) 
    dw 0xffff     ; limit low  ; [0-15] 
    dw 0x0000     ; base low  ; [0-15] 
    db 0x20     ; base middle ; [16-23] 
    db 0x92     ; access  ; 
    db 0xCF     ; granularity ; flags(4) - limit(4) [16-19] 
    db 0x00     ; base hi 

快速和骯髒,但不令人滿意。一個更好的做法是申報表中,然後使用一個輔助功能,以針對任何特定條目設置的值:

static void init_gdt() 
{ 
    gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1; 
    gdt_ptr.base = (u32int)&gdt_entries; 

    gdt_set_gate(0, 0, 0, 0, 0);    // Null segment 
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment 
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment 
    gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment 
    gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment 

    gdt_flush((u32int)&gdt_ptr); 
} 

所有這些描述符指向的內存的同一區域,但將需要8 DS值,16,24和32(第一個條目未被使用 - 每個條目的大小爲8個字節)