2015-10-20 98 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个字节)

相关问题