【问题标题】:DS segment register in 32 bit architecture32位架构的DS段寄存器
【发布时间】:2016-01-18 12:06:33
【问题描述】:

我一直在研究如何通过反汇编 C 代码在 x86 架构中处理浮点运算。使用的操作系统是 64 位 linux,而代码是为 32 位机器编译的。

这里是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 <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

我难以理解的是浮点值传输到寄存器的行。具体来说:

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

但是浮点数存储在 0x8048520 和 0x8048530 中,这就像在 DS 中的值为 0。谁能向我解释这是为什么?

【问题讨论】:

标签: assembly x86 disassembly objdump memory-segmentation


【解决方案1】:

DS 在保护模式下的工作方式完全不同。它不是线性地址的移位部分,就像在实模式中一样,它是包含段基地址的段表的索引。操作系统内核维护段表,用户态代码不能。

也就是说,忽略 ds: 前缀。反汇编程序明确说明了默认行为,就是这样。该命令默认使用 DS 作为选择器;所以反汇编程序认为它会提到。操作系统会将 DS 初始化为对进程有意义的值,并且在整个进程中将使用相同的 DS 值。

【讨论】:

  • 简单地说,他们搞砸了 intel 语法反汇编。如果前缀确实存在于代码中,则应仅显示该前缀,并且应使用 [] 进行内存引用,例如mov eax, [0x804852c]。大概它显示了前缀,这样人们就不会将它与mov eax, 0x804852c 混淆,后者是立即数。
【解决方案2】:

由于代码是 32 位保护模式,因此 DS 寄存器用作表的索引,正如 Seva 所提到的。这称为 GDT 或 LDT,取决于它是全局的还是本地的进程。全局描述符表和本地描述符表。

每个条目都指定了许多不同的参数。其中包括所描述的内存区域的基础、限制和粒度、访问类型和特权级别。

完全有可能拥有两个在各方面都相同的描述符——它们显然会在表中具有不同的索引,并会导致 DS 的值不同。

--

它还允许您访问位于地址空间中任何位置的内存,就好像它位于内存的最底部一样。以显卡线性帧缓冲区的视频内存为例。不同的卡实现将其定位在不同的地址,但您仍然可以以完全透明的方式访问这些不同的区域,这要归功于描述符中的基本字段。

我的一张卡将内存定位在 0xE0000000,而另一张卡将其定位在 0xC0000000。现在我可以在查询卡片后将此地址保存到全局变量中,然后在任何绘图操作中加载此变量并将其添加到区域中计算的偏移量中。幸运的是,描述符机制让我们做得比这更好。

当我设置 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、16、24 和 32 的 DS 值(第一个条目未使用 - 每个条目的大小为 8 个字节)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-18
    • 2022-01-04
    • 2020-11-21
    • 2011-10-26
    • 2011-06-21
    • 2014-11-17
    • 2014-01-05
    相关资源
    最近更新 更多