我尝试了解Linux上i386和amd64体系结构的内存分段过程。看来这与段寄存器%fs%gs%cs%ss%ds%es密切相关。

有人可以解释在用户程序和内核程序中如何使用这些寄存器吗?

评论

您需要特定于操作系统

是的,你是对的。我将问题编辑为特定于Linux。

我建议您阅读分步汇编语言-duntemann.com/assembly.html

Google的Native Client技术使用分段来实施沙箱。您可以查看实施以获取更多见解。

#1 楼

内核角度:

我将尝试从内核角度回答,涵盖各种操作系统。

内存分段是访问内存区域的旧方法。
所有主要包括OSX,Linux(版本0.1)和Windows(版本NT)的Windows操作系统现在正在使用分页,这是访问内存的更好方法(IMHO)。

英特尔一直在向后引入兼容性。因此,它的处理器(IA-64除外,我们看到了它是如何发生故障的...)
因此,在初始状态(重置后),处理器以称为实模式的模式启动,在此模式下,通过默认情况下支持旧版软件。
在操作系统启动过程中,处理器被更改为保护模式,然后进入启用的分页。

分页之前,段寄存器的使用方式类似于this


在实模式下,每个逻辑地址都直接指向物理内存中的位置,每个逻辑地址由两个16位部分组成:逻辑地址的br />段部分包含
段的基址,其粒度为16个字节,即,段可以从
物理地址0、16、32,...,220开始-16。逻辑地址的偏移量部分包含段内的偏移量,即,物理地址可以分别计算为physical_address := segment_part × 16 + offset(如果启用了地址线A20),qbr12079q(如果为A20)关闭)。每个段的大小为216个字节。 [Wikipedia]


让我们看看一些示例(286-386时代):

286体系结构引入了4个段:CS(代码段)DS(数据段) )SS(堆栈段)ES(额外段)
386体系结构引入了两个新的通用段寄存器FS,GS。

典型的汇编操作码(采用Intel语法)如下所示:

mov dx, 850h
mov es, dx     ; Move 850h to es segment register
mov es:cx, 15h ; Move 15 to es:cx


使用分页(保护模式)段寄存器不再用于寻址内存位置。在保护模式下,将(segment_part × 16 + offset`) mod 220替换为16位选择器,选择器的13个高位(第3位至第15位)包含描述符表中条目的索引。下一位(位2)指定
是与GDT还是LDT一起使用。选择器的最低两位
(位1和位0)被组合以定义请求的特权
;其中值为0的优先级最高,值为
3的优先级最低。 [wikipedia]


段仍然用于在GDT中强制执行硬件安全性由英特尔
x86系列处理器从80286开始,目的是定义程序执行期间使用的各种内存区域的特征,包括基地址,大小和访问特权。 />,例如可执行性和可写性。这些内存区域在Intel术语中称为
段。 [wikipedia]


因此,实际上,处于保护模式的段寄存器用于将索引存储到GDT。

多个操作系统,例如Windows和Linux ,将某些细分用于内部使用。例如,Windows x64使用segment_part寄存器来访问TLS(线程本地存储),而在Linux中,它用于访问cpu特定的内存。

用户角度:

从用户角度,在最近使用分页的操作系统中,内存以所谓的“平面模式”工作。
每个进程都以线性方式访问其自己的内存(4GB),因此基本上不需要段寄存器。

它们仍然是寄存器,因此它们当然可以用于其他各种组装操作。

评论


不错的答案,但是我会在所有主要操作系统上加点澄清...使用分页。我不了解linux,但是正如您稍后在回答中指出的那样,NT仍然使用段来进行保护(至少在x86上)。特权级别或环链接到段。因此,将地址空间细分为(至少)四个段。地址空间仍然看起来像是4gb平面,但是较低的2(或3)GB是具有环3访问权限的一个网段,较高的2(或1)是具有环0访问权限的网段。

–洛伦佐·德马特(LorenzoDematté)
13年5月23日在8:45



其他两个段是用户空间的低端和高端(0-2GB)的(短-64kb长)区域。它们有助于捕获空指针并标记“可用用户空间的完成”。总的来说,这种内存管理技术称为“页面分割”,即IIRC:有多个段(只有几个),每个段都被页面化。

–洛伦佐·德马特(LorenzoDematté)
13年5月23日在8:46



@LorenzoDematté您是正确的...随时根据您的见解编辑我的答案。

–烛光
13年5月23日在10:21

是的,这就是为什么我认为没有必要进行编辑且注释足够的原因:)

–洛伦佐·德马特(LorenzoDematté)
13年5月23日在12:44

movs,850h;将850h移至es段寄存器中上面的指令不存在因为不可能用立即数加载段寄存器。只能用其他非段寄存器的内容,RAM位置的内容或从堆栈的堆栈中弹出一个值mov es:cx,15h来加载一个段寄存器。将15移到es:cx上面的指令也不存在。因为不能将CX用作地址寄存器,所以只能将ECX用作地址寄存器。

–user2127
13年5月30日在5:36



#2 楼

FS指向异常处理链,CS和DS从OS中填充了代码和数据段。 SS是电池/电池组段。据我所知,GS和ES是免费的。
内核或用户模式无关紧要(XLAT,MOVS等某些指令使用了它们,因此您必须在以同样的方式),但以防万一我在谈论用户空间中的编程。

我以前没有注意到,但是您使用的是%fs而不是FS,所以您可能是意思是Linux,这是另一回事了(在保护/实模式下,您也可能会更加清楚)。您还可以从stackexchange上的其他答案中看到,Linux显然在FS和GS中为您提供了“线程本地存储”和“处理器数据区域”。 CS,DS和SS仍应为代码/数据/堆栈。

为了便于说明,我不知道在Mac上如何使用这些寄存器。

对于64位,这取决于:如果不处于兼容模式(可以在其中执行64位和32位代码),则DS,ES和SS会被忽略,并且诸如POP SS的指令会给出错误。没有分段(内存模型是平面的),应该没有实模式(但我认为您仅指保护模式?),如果我没记错的话,也就没有硬件任务切换。
有是有关64位模式下CS,FS和GS(尤其是隐藏部分)的更多详细信息,但是由于它不经常使用,最好省略它们。

您可以查看AMD手册系列处理器,尤其是在64位旧版模式下:http://developer.amd.com/resources/documentation-articles/developer-guides-manuals/

评论


没有发布图片的“足够的声誉”很奇怪...

– lunadir
13年5月4日在12:31

安腾(IA-64)具有段寄存器对我来说是新的。你可以解释吗?!

– 0xC0000022L♦
13年5月4日在16:31

好吧,是的,实际上不应该有任何段,因为没有段...无论如何,在从ia-32到ia-64编程的过渡过程中,段寄存器都映射到了应用寄存器GR16-GR17(或者这样写) )

– lunadir
13年5月4日在16:58

Linux从未使用过分段,而是一直使用分页。关于Windows,因为NT,它也使用分页,所以除非您在进行DOS编程/反转,否则此处描述的内容不再相关。

–烛光
13年5月5日在1:06

我的一个碰巧是正在反转DOS二进制文件,因此找到此信息非常宝贵。

– L0j1k
15年7月6日在5:57

#3 楼

我写了一个Windows特定问题的答案,该问题被标记为重复并关闭,并且关闭标志引用了此线程,因此我在此处发布了答案

OS win7 sp1 32位计算机
内核转储使用sysinternals的livekd

16位段寄存器包含
13位选择器
表描述符的1位
requester_privilege_level的2位

>
Selector        tl  rpl 
0000000000000----0---00 


,因此将cs和fs转换为二进制将

kd> r cs;r fs
cs=00000008  = 0b 00001 0 00
fs=00000030  = 0b 00110 0 00


2位rpl表示0、1、2、3振铃(因此00 = 0 =环零)

gdt = 1位表示0,1(0表示GDT,1表示LDT)

全局描述符表和局部描述符表

高13位代表段选择器

因此cs = 0x08具有段选择器0b 001 = 0x1即gdtr @ 1
&fs = 0x30具有段选择器0f 0b 110 = 0x6,即gdtr @ 6

内核cs,fs与用户cs,fs
不同,可以从windbg的dg命令中注意到

kd> dg @cs  <<<<<<<--- kernel 
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0008 00000000 ffffffff Code RE Ac 0 Bg Pg P  Nl 00000c9b

0:000> dg @cs <<<<<<<<----user 
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
001B 00000000 ffffffff Code RE Ac 3 Bg Pg P  Nl 00000cfb

kd> dg @fs <<<<<<<<------- kernel
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0030 82f6dc00 00003748 Data RW Ac 0 Bg By P  Nl 00000493

0:000> dg @fs
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
003B 7ffdf000 00000fff Data RW Ac 3 Bg By P  Nl 000004f3


您可以从osdevwiki_gdtrobert-collins_ddj_article

收集有关gdt的足够信息,以在此处使用livekd手动进行操作

使用windbg,您可以获得描述符和任务门寄存器

kd> rM 100
gdtr=80b95000   gdtl=03ff idtr=80b95400   idtl=07ff tr=0028  ldtr=0000


每个gdtr条目都是64位,因此您可以拥有7f gdtr条目,因为您看到gdtl是3ff 0x80 * 0x08-1 = 0x400-1 = 0x3ff(索引从0开始而不是1) )等

kd> dq @gdtr+8 l1    gdtr@1 = gdtr+0n1*0x8 =0n8  = 0x8    
80b95008  00cf9b00`0000ffff = gdtr+0n6*0x8 =0n48 = 0x30    
kd> dq @gdtr+30 l1   
80b95030  824093f6`dc003748   
kd> dq @gdtr+38 l1   
80b95038  7f40f3fd`e0000fff   


手动对游戏的最后两个gdtr条目进行位游戏

-------------------------------------------------------------------------------------------
gdtrentry        [63:     [55:  [51:  [47:          [39:                  [15:             
                  56]      52]   48]   40]           16]                    0]             
                 base     gdrs  L     p d  t     Base     Base             Limit           
                 Hi       rb0y  h     r l  y     Mid      Low                              
-------------------------------------------------------------------------------------------
bit position     66665555 5555  5544  4 44 44444 33333333 3322222222221111 1111110000000000
                 32109876 5432  1098  7 65 43210 98765432 1098765432109876 5432109876543210
-------------------------------------------------------------------------------------------
824093f6dc003748 10000010 0100  0000  1 00 10011 11110110 1101110000000000 0011011101001000
as hex           0x82     0100  0     1 0  0x13  0xF6     0xDC00           0x3748          
--------------------------------------- ---------------------------------------------------
7f40f3fde0000fff 01111111 0100  0000  1 11 10011 11111101 1110000000000000 0000111111111111
as hex           0x7F     0100  0     1 3  0x13  0xFD     0xE000           0x0FFF          
-------------------------------------------------------------------------------------------


评论


这个答案最初是为这个问题而写的。

–绿色环保
17年6月14日在7:40

@blabb:奇怪!您为什么不发布关于其他问题的答案?这个问题是特定于Linux的,而您的答案是特定于Windows的。以某种方式,您的答案会更好地适合另一个问题。你不觉得吗(不过,很不错的答案!)

–恐怖
17年6月14日在8:33

啊,另一个问题已经解决了!!!那我投票赞成重新开放。

–恐怖
17年6月14日在8:34

@perror是,该问题已作为重复问题被关闭,并且注释引用了该线程,因此我不得不在此处发布

– blabb
17年6月15日,下午3:06