你真的了解段寄存器吗?

段寄存器通常有CS DS SS ES,80386后引入了2个额外的段寄存器FSGS

大量的书籍上,都描述了段寄存器是16位的,这是一种非常不严谨的说法!

这些段寄存器除了有16位的可见部分,还有不可见的隐藏部分,称为描述符缓存“descriptor cache”隐藏寄存器“shadow register”[1]。当一个段选择符(segment selector)装入段寄存器的可见部分,处理器同时也把该段描述符的其它数据装入到段寄存器的隐藏部分,这包括段开始的基地址、段长度、访问控制信息等。这些信息缓存到段寄存器中,避免了处理器在转址(translate address)时花费额外的总线周期从段选择符表中读入数据。处理器指令中可以明示使用哪些段寄存器,这将替换掉默认使用的段寄存器。 ————维基百科

通常我们看到的段寄存器只有16位,这16位本质只是一个段选择子,更多的段描述信息需要通过该选择子在GDT全局描述符表中找到对应的描述符表项进而读取段的全部信息。
在这里插入图片描述

段寄存器中16位2字节段选择子的结构如下:
在这里插入图片描述
在这里插入图片描述

GDT表中64位8字节段描述符结构:
在这里插入图片描述

对于一个段寄存器描述的一个段,该段具有段属性Attribute段基址Base以及段限长Limit,段寄存器中能看到的16位段选择子就是为了能够查找上述三个属性。

在这里插入图片描述

下面进行三个属性的基本探测,能够证明他们确实存在,而且当进行段寄存器赋值的时候,不止是简单的16位段选择子的改变,同时也涉及段属性、段基址以及段限长的改变。

Attribute

#include <iostream>

using namespace std;

int result;
int main()
{
	__asm
	{
		mov ax, cs //cs是可读 可执行 但是不可写
		mov ds, ax 
		mov dword ptr ds:[result], eax 
	}
	cout << result << endl;
	return 0;
}

Base

#include <iostream>

using namespace std;

int result;
int main()
{
	__asm
	{
		mov ax, fs // 如果换成ss就是读取0地址处导致程序运行失败,fs寄存器的段基础不是0,所以能够进行读取
		push gs // 保存gs的值
		mov gs, ax 
		mov eax, gs:[0]
		mov dword ptr ds:[result], eax
		pop gs // 恢复gs的值
	}
	cout << result << endl;
	return 0;
}

Limit

#include <iostream>

using namespace std;

int result;
int main()
{
	__asm
	{
		mov ax, fs // 如果换成ss就是读取0地址处导致程序运行失败,fs寄存器的段基础不是0,所以能够进行读取
		push gs // 保存gs的值
		mov gs, ax 
		mov eax, gs:[0xffc] // fs段限长为0xfff
		mov dword ptr ds:[result], eax
		pop gs // 恢复gs的值
	}
	cout << result << endl;
	return 0;
}

从段描述符的结构中,我们看到描述段限长Limit 低4字节0-15位以及高字节16-19位,但是Base Address被分为了三段低4字节16-31位、高4字节0-7位以及高4字节24-31位,这是由于历史原因,因intelCPU也是一步步发展过来的,为了能够向下兼容,所以出现了这种破碎的场景;剩下的部分都是段的描述属性了。

接下来详细介绍段描述符的各个部分属性!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值