Linux X86保护模式分段验证
来源:互联网 发布:qq魅力值软件 编辑:程序博客网 时间:2024/05/29 09:04
Linux X86保护模式分段验证
机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU转换成物理地址才能够被访问到。例如可以写一个简单的C函数获取变量的逻辑地址:
void show_var_addr()
{
int a = 0;
printf(“var a’s address = 0x%08x\n”, (int)&a);
}
程序打印出: var a’s address =0x80495b0, 0x80495b0则为逻辑地址,必须加上隐含的DS 数据段的基地址,才能构成线性地址。也就是说 0x80495b0 是当前任务的DS数据段内的偏移。
那么段基值怎么获取呢?在8086分段中,我们知道段基值存放在段寄存器cs,ds,ss等段寄存器中;而在x86保护模式分段中,段基值存放在段选择符中,每一个段选择符由8个字节组成,这8个字节包含的信息有段基值,段所属特权级等信息。而段描述符存放全局段描述符表GDT中或局部段描述符LDT中,GDT中共有32个段描述符表项,故而占用256个字节。在linux中,绝大部分使用GDT, 而LDT使用较少,具体原因这里先不分析。32个段描述符存放在GDT中,只要知道GDT的基地址,以及段选择符索引就可以获取段选择符,进而获取段基址。
其中,GDT基地址存放在寄存器gdtr中,gdtr寄存器位宽为64bit,如图:
图(1)gdtr寄存器
gdtr寄存器共64位,其中limit代表GDT表长界限,从0开始;address代表着表的基地址,(注意是线性地址)。
段选择符索引存在段寄存器中称为段选择子。段寄存器位宽为16bit,如图:
图(2)段选择子
端选择子包含三部分:段描述符索引(index),TI,请求特权级(RPL),段选择符索引代表着该段选择符在GDT或LDT表中的位置,TI确定段选择符在GDT表还是在LDT表中,TI = 1,则选择符在GDT中,否则在LDT中。PRL代表段选择子的特权级,共有4个特权级(0级,1级,2级,3级)。任务中每一个段都有一个特定的级别,每当程序试图访问某一个段的时候,将该程序拥有的特权级与端的特权级进行比较,以决定能否访问该段。
故而通过address + 8 * index即可计算出段描述符的地址。其中8代表段描述符的长度,为8字节,具体每一字节的含义如下:
图(3)端描述符
BASE代表段的基地址,共32位,分3部分存储在端描述符中;G代表粒度标志,如果该位为0,则段大小以字节为单位,否则以4096字节的倍数计;Limit共20位,决定段的长度,如果G为0,则段的大小在1字节到1MB之间变化,否则将在4KB到4GB之间变化;DPL为描述符特权级字段,用于限制段的存取,表示访问这个段要求CPU的最小优先级,因此DPL设为0的段只能当CPL为0时,才能够访问。
已知Linux中,设置GDT的第12和13项段描述符是 __KERNEL_CS 和__KERNEL_DS,第14和15项段描述符是__USER_CS 和__USER_DS。内核任务使用__KERNEL_CS和__KERNEL_DS,所有的用户任务共用__USER_CS和__USER_DS,也就是说不需要给每个任务再单独分配段描述符。内核段描述符和用户段描述符虽然起始线性地址和长度都一样,但DPL(描述符特权级)是不一样的。__KERNEL_CS和__KERNEL_DS 的DPL值为0(最高特权),__USER_CS 和__USER_DS的DPL值为3。可以通过以下程序来验证段选择子与段选择符:
内核态程序:
int gdt_test_init_module(void)
{
int i;
short cs;
struct desc_ptr gdt_descr;
printk(KERN_INFO "--------\n");
/* test!!! */
asm volatile("mov %%cs, %0" : "=m" (cs));
printk(KERN_INFO "cs = %d\n", cs);
asm volatile("sgdt %0" : "=m" (gdt_descr));
printk(KERN_INFO "size = %d, addr = 0x%08x\n", gdt_descr.size, gdt_descr.address);
char* start = gdt_descr.address + 8 * ((cs & 0xFFF8) >> 3); //cs段选择符首地址
for(i = 0; i < 8; i++) {
printk(KERN_INFO "%x ", *(start + i) & 0xFF);
}
printk(KERN_INFO "-----------\n");
}
通过dmesg观察内核打印:
[166206.645046] --------
[166206.645048] cs = 96
[166206.645050] size = 255, addr = 0xf7bdc000
[166206.645051] ff
[166206.645052] ff
[166206.645053] 0
[166206.645053] 0
[166206.645054] 0
[166206.645055] 93
[166206.645055] cf
[166206.645056] 0
[166206.645057] -----------
可以看出内核态CS段选择子值为96,即为0000000001100000,其中index为二进制1100即为12, TI为0即段选择符在GDT中,RPL为0即为最高级别。 段选择子为8字节数据,即为调试信息中的:0x00CF93000000FFFF,通过图(2)可以看出,该段的基地址为0x00000000.
用户态程序:
#include <stdio.h>
typedef struct {
unsigned short size __attribute__((packed));
unsigned int address __attribute__((packed));
} gdt_t;
int main(void)
{
gdt_t gdt;
short cs;
int i;
printf("--------\n");
asm volatile("mov %%ds, %0" : "=m" (cs));
printf("cs = %d\n", cs);
asm volatile("sgdt %0" : "=m" (gdt));
printf("size = %d, addr = 0x%08x\n", gdt.size, gdt.address);
char* start = gdt.address + 8 * ((cs & 0xFFF8) >> 3);
for(i = 0; i < 8; i++) {
printf("%x \n", *(start + i) & 0xFF);
}
printf("-----------\n");
return 0;
}
程序打印结果:
--------
cs = 123
size = 255, addr = 0xf7bdc000
段错误 (核心已转储)
可以看出,用户态CS寄存器为123,即0000000001111011,其中index为二进制1111即为15, TI为0即段选择符在GDT中,RPL为3即为用户级别。GDT的基地址为0xf7bdc000,可以看出应用程序试图读GDT的时候,程序出现了段错误。这个原因主要是用户态程序的活动范围为0~0xC0000000, 超过这个范围的地址,用户程序无读写权限,故而出现了段错误。但是可以记录段选择符索引,通过内核态程序获取。可以得出该段的段基址也是0x00000000.
所以说,Linux中逻辑地址等于线性地址。为什么这么说呢?因为Linux所有的段(用户代码段、用户数据段、内核代码段、内核数据段)的线性地址都是从0x00000000 开始,长度4G,而线性地址=段基地址+逻辑地址,而所有的段基地址为0x00000000, 也就是说逻辑地址等于线性地址。
- Linux X86保护模式分段验证
- 80x86处理器保护模式下的分段机制
- <<Linux内核完全剖析 --基于0.12内核>>学习笔记 第4章 80x86保护模式及其编程 4.3 分段机制
- 80X86 保护模式
- 80x86保护模式
- 80x86保护模式
- x86 保护模式编程
- x86保护模式笔记
- x86保护模式
- x86 保护模式寻址
- X86 Linux分段机制实现
- linux内核 80x86保护模式及其编程
- X86处理器进入保护模式
- 80x86保护模式实现
- x86架构中的保护模式
- 80x86保护模式(1)
- 80x86保护模式(2)
- 80x86保护模式(3)
- android4.0触摸屏滑鼠模式解决方法-添加idc文件
- 面向对象(二)——装箱、equals、单例类、接口、模式
- Android 数据访问之External Storage 数据保存在sd卡 demo+笔记
- java线程题目
- jquery struts2 文件下载
- Linux X86保护模式分段验证
- HBase框架
- 最新Visual Studio Tools For Unity 安装使用解析
- Spring设值注入
- hbase-zookeeper
- 字符集:ASCII ANSI UNICODE UTF GB2312 ...
- google play v3支付功能,加中文注解
- byte
- Git系列之分支管理策略