调用门

9 篇文章 0 订阅
4 篇文章 0 订阅

门,顾名思义它是一扇门通向令一个地方,看一下门的结构里面有一个描述符和一个偏移值,门中定义了这扇门通向的目的地,当我们调用一个门的时候会到达这个地方:

门中的选择子:门中的偏移值,也即下图的selector:offset

这是我们代码中国定义的门的数据结构:

#define Gate(Selector,Offset,PCount,Attr)\
.2byte (Offset&0xffff);\
.2byte (Selector);\
.2byte (PCount&0x1f)|((Attr<<8)&0xff00);\
.2byte ((Offset>>16)&0xffff)

 

假设Selector_Gate_Call是调用门的描述符,执行lcall $Selector_Gate_Call,$0语句时偏移值($0)无意义,执行此语句后

CS=调用门中的选择子
IP=调用门中偏移值
CS:IP=全局描述符表中第(调用门中的选择子>>3)项描述符给出的段基址+调用门中偏移值


调用门的使用

定义调用门的描述符和选择子,如下面代码

/*门描述符*/
Descriptor_Gate_Call:Gate(Selector_Code_Gate,0,0,(DA_386CGate+DA_PL0))

/*调用门选择子*/
.set Selector_Gate_Call,(Descriptor_Gate_Call-GDT_START)

 

其中调用门中的选择子和描述符如下:

Descriptor_CODE_GATE:Descriptor(0x0,CodeGLen,DA_C+DA_32)

.set  Selector_Code_Gate,(Descriptor_CODE_GATE-GDT_START)

 

Descriptor_CODE_GATE的初始化和代码段如下:

InitDescrptor(Descriptor_CODE_GATE,LABEL_CODE_G);

LABEL_CODE_G: 

    movl $((80*12+0)*2),%edi/*第10行,0列*/ 
    movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/ 
    movb $'G',%al/*要显示的字符*/ 
    movw %ax,%gs:(%edi)    
jmp .
 .set CodeGLen,(. - LABEL_CODE_G)

 

这时lcall $Selector_Gate_Call,$0就会看到红色的'G’打印出来,和ljmp Selector_Code_Gate,$0的效果是一样的,但是这并不是多此一举的,这涉及的特权级转换的问题,这里我们先不关心这些。

 

/*初始全局描述符Descriptor_CODE_GATE*/ 
InitDescrptor(Descriptor_CODE_GATE,LABEL_CODE_G);

具体代码如下:

#define Descriptor(base,lim,attr)\
.word lim&0xffff;\
.word base&0xffff;\
.byte (base>>16)&0xff;\
.word ((lim>>8)&0xf00)|(attr&0x0f0ff);\
.byte ((base>>24)&0xff)  
  
/*  
*InitDescrptor(Descriptor,SegBase)初始化描述符函数  
*Descriptor:要初始化的描述符  
*SegBase:段基址  
*/  
#define InitDescrptor(Descriptor,SegBase)\
xor     %eax,%eax; \
mov     %cs,%ax  ; \
shl     $4,%eax  ; \
addl    $(SegBase), %eax ;\
movw    %ax, (Descriptor + 2);\
shr     $16, %eax;\
movb    %al, (Descriptor + 4);\
movb    %ah, (Descriptor + 7)  

#define Gate(Selector,Offset,PCount,Attr)\
.2byte (Offset&0xffff);\
.2byte (Selector);\
.2byte (PCount&0x1f)|((Attr<<8)&0xff00);\
.2byte ((Offset>>16)&0xffff)  

DA_386CGate = 0x8c
DA_PL0 = 0x00
DA_C   = 0x98  
DA_32  = 0x4000  
DA_DRW = 0x92  
SA_TIL = 0x4  
DA_LDT = 0x82  
  
.text  
.globl start  
.code16  
start:  
 jmpl $0x0, $code   
   
/**-----------------------------------------------------------------  
 * 全局描述符表: GDT  
 *-------------------------------*/       
GDT_START:    
Descriptor_DUMMY: Descriptor(0x0,0x0,0x0)  
Descriptor_CODE32:Descriptor(0x0,0xffffffff,DA_C+DA_32)  
Descriptor_VIDEO: Descriptor(0xb8000,0x0ffff,DA_DRW)  
Descriptor_CODE_GATE:Descriptor(0x0,CodeGLen,DA_C+DA_32)

/*门描述符*/ 
Descriptor_Gate_Call:Gate(Selector_Code_Gate,0,0,(DA_386CGate+DA_PL0))  
GDT_END:  
  
GdtPtr:  
    .word (GDT_END-GDT_START)-1 # so does gdt   
    .long GDT_START     # This will be rewrite by code.  

.set	selector_Code32,(Descriptor_CODE32-GDT_START)
.set	Selector_Video,(Descriptor_VIDEO-GDT_START)     
.set  Selector_Code_Gate,(Descriptor_CODE_GATE-GDT_START)
/*调用门选择子*/
.set	Selector_Gate_Call,(Descriptor_Gate_Call-GDT_START)
 
msg:  
 .string "Hello world!"  
   
   
code:  
    mov     %cs,%ax     
    mov     %ax,%ds   
    mov     %ax,%es      
    mov     %ax,%ss    
    mov  $0x8000,%sp    
    /*显示HelloWorld字符串*/  
    mov $msg   ,%ax  
    mov %ax    ,%bp  
    mov $12    ,%cx  
    mov $0x1301,%ax  
    mov $0x000c,%bx  
    mov $0     ,%dl  
      
    int $0x10  
      
  
/*初始全局描述符Descriptor_CODE32*/  
InitDescrptor(Descriptor_CODE32,LABEL_SEG_CODE32);  
  
/*初始全局描述符Descriptor_CODE_GATE*/  
InitDescrptor(Descriptor_CODE_GATE,LABEL_CODE_G);  

/*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/         
    lgdt GdtPtr  
  
/*关中断*/  
    cli  
  
/*打开地址线A20*/  
    inb $0x92,%al  
    or  $0x02,%al  
    outb %al,$0x92  
  
/*设置cr0寄存器,切换到保护模式*/  
    movl %cr0,%eax  
    or   $1,%eax  
    movl %eax,%cr0  
  
  
/*真正进入保护模式,执行此命令后CS=0x8,IP=0*/  
    ljmp $selector_Code32,$0  
  

LABEL_SEG_CODE32:  
.align  32  
.code32  
  
    movw $Selector_Video,%ax  
    movw %ax,%gs/* 视频段选择子(目的)*/  
    movl $((80*11+79)*2),%edi/*第11行,79列*/  
    movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/  
    movb $'P',%al/*显示的字符*/  
    movw %ax,%gs:(%edi)  
       
    #ljmp $Selector_Gate_Call,$0
    lcall $Selector_Gate_Call,$0  
  
      
jmp .  
      
LABEL_CODE_G:  

    movl $((80*12+0)*2),%edi/*第10行,0列*/  
    movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/  
    movb $'G',%al/*要显示的字符*/  
    movw %ax,%gs:(%edi)  
  	
jmp .
 
.set CodeGLen,(. - LABEL_CODE_G)  
  
.org 0x1fe, 0x90   
.word 0xaa55 

执行结果如下图,可以看到红色的‘G’打印出来


在LABEL_CODE_G中我们打印完字符就让程序挺在了这里,这时候如下两段代码的效果是一样的。

ljmp $Selector_Gate_Call,$0
 lcall $Selector_Gate_Call,$0

但ljmp和lcall是不一样的,它们都会改变寄存器CS和IP实现程序的跳转,但lcall还会影响堆栈 ,段内跳转回将ip如栈段间跳转回将cs和ip入栈,等到lret执行时这个ip(或者cs和ip)会从堆栈中弹出。

下面我们就实现一个带返回的调用门:

#define Descriptor(base,lim,attr)\
.word lim&0xffff;\
.word base&0xffff;\
.byte (base>>16)&0xff;\
.word ((lim>>8)&0xf00)|(attr&0x0f0ff);\
.byte ((base>>24)&0xff)  
  
/*  
*InitDescrptor(Descriptor,SegBase)初始化描述符函数  
*Descriptor:要初始化的描述符  
*SegBase:段基址  
*/  
#define InitDescrptor(Descriptor,SegBase)\
xor     %eax,%eax; \
mov     %cs,%ax  ; \
shl     $4,%eax  ; \
addl    $(SegBase), %eax ;\
movw    %ax, (Descriptor + 2);\
shr     $16, %eax;\
movb    %al, (Descriptor + 4);\
movb    %ah, (Descriptor + 7)  

#define Gate(Selector,Offset,PCount,Attr)\
.2byte (Offset&0xffff);\
.2byte (Selector);\
.2byte (PCount&0x1f)|((Attr<<8)&0xff00);\
.2byte ((Offset>>16)&0xffff)  

DA_386CGate = 0x8c
DA_PL0 = 0x00
DA_C   = 0x98  
DA_32  = 0x4000  
DA_DRW = 0x92  
SA_TIL = 0x4  
DA_LDT = 0x82  
  
.text  
.globl start  
.code16  
start:  
 jmpl $0x0, $code   
   
/**-----------------------------------------------------------------  
 * 全局描述符表: GDT  
 *-------------------------------*/       
GDT_START:    
Descriptor_DUMMY: Descriptor(0x0,0x0,0x0)  
Descriptor_CODE32:Descriptor(0x0,0xffffffff,DA_C+DA_32)  
Descriptor_VIDEO: Descriptor(0xb8000,0x0ffff,DA_DRW)  
Descriptor_CODE_GATE:Descriptor(0x0,CodeGLen,DA_C+DA_32)
Descriptor_LDT:   Descriptor(0x0,LDTLen - 1, DA_LDT)

/*门描述符*/ 
Descriptor_Gate_Call:Gate(Selector_Code_Gate,0,0,(DA_386CGate+DA_PL0))  
GDT_END:  
  
GdtPtr:  
    .word (GDT_END-GDT_START)-1 # so does gdt   
    .long GDT_START     # This will be rewrite by code.  

/**-----------------------------------------------------------------  
 * GDT中的选择子
 *-------------------------------*/ 
.set	selector_Code32,(Descriptor_CODE32-GDT_START)
.set	Selector_Video,(Descriptor_VIDEO-GDT_START)     
.set  Selector_Code_Gate,(Descriptor_CODE_GATE-GDT_START)
.set	SelectorLDT,(Descriptor_LDT-GDT_START)
/*调用门选择子*/
.set	Selector_Gate_Call,(Descriptor_Gate_Call-GDT_START)

/**-----------------------------------------------------------------
 * 局部描述符表: LDT
 *-------------------------------*/
LDT_START:
/*                       段基址    段界限      属性*/
Descriptor_LDT_CODEA: Descriptor(0, CodeALen - 1, DA_C + DA_32)# Code, 32 位

.set    LDTLen,(. - LDT_START)

#LDT 选择子
.set  SelectorLDTCodeA,(0x0 + SA_TIL)
 
msg:  
 .string "Hello world!"  
   
   
code:  
    mov     %cs,%ax     
    mov     %ax,%ds   
    mov     %ax,%es      
    mov     %ax,%ss    
    mov  $0x8000,%sp    
    /*显示HelloWorld字符串*/  
    mov $msg   ,%ax  
    mov %ax    ,%bp  
    mov $12    ,%cx  
    mov $0x1301,%ax  
    mov $0x000c,%bx  
    mov $0     ,%dl  
      
    int $0x10  
      
  
/*初始全局描述符Descriptor_CODE32*/  
InitDescrptor(Descriptor_CODE32,LABEL_SEG_CODE32); 

/*初始全局描述符Descriptor_LDT*/
InitDescrptor(Descriptor_LDT,LDT_START);
  
/*初始全局描述符Descriptor_CODE_GATE*/  
InitDescrptor(Descriptor_CODE_GATE,LABEL_CODE_G);  

/*初始LDT 中的描述符Descriptor_LDT_CODEA*/
InitDescrptor(Descriptor_LDT_CODEA,LABEL_CODE_A);

/*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/         
    lgdt GdtPtr  
  
/*关中断*/  
    cli  
  
/*打开地址线A20*/  
    inb $0x92,%al  
    or  $0x02,%al  
    outb %al,$0x92  
  
/*设置cr0寄存器,切换到保护模式*/  
    movl %cr0,%eax  
    or   $1,%eax  
    movl %eax,%cr0  
  
  
/*真正进入保护模式,执行此命令后CS=0x8,IP=0*/  
    ljmp $selector_Code32,$0  
  


LABEL_SEG_CODE32:  
.align  32  
.code32  
  
    movw $Selector_Video,%ax  
    movw %ax,%gs/* 视频段选择子(目的)*/  
    movl $((80*11+79)*2),%edi/*第11行,79列*/  
    movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/  
    movb $'P',%al/*显示的字符*/  
    movw %ax,%gs:(%edi)  
       
    lcall $Selector_Gate_Call,$0  
  

    movw $SelectorLDT,%ax
		lldt %ax/*加载lgtr*/

		ljmp	$SelectorLDTCodeA,$0	/* 跳入局部任务 LABEL_CODE_A*/
      
jmp .  
      
LABEL_CODE_G:  

    movl $((80*12+0)*2),%edi/*第10行,0列*/  
    movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/  
    movb $'G',%al/*要显示的字符*/  
    movw %ax,%gs:(%edi)  
		
		lret
.set CodeGLen,(. - LABEL_CODE_G)  

LABEL_CODE_A:
	movw $0x10,%ax
	movw %ax,%gs/* 视频段选择子(目的)*/
	movl $((80*12+10)*2),%edi/*第10行,0列*/
	movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/
	movb $'A',%al/*要显示的字符*/
	movw %ax,%gs:(%edi)
	
loop3:
	jmp loop3
.set CodeALen,(. - LABEL_CODE_A)
  
.org 0x1fe, 0x90   
.word 0xaa55 


其结果是打印完'G'后返回,然后又打印出'A'如下图:

 

门也是一个描述符,我们放到了GDT中,我们通过门的选择子访问门,门通过它记录的选择子和偏移值又把我们传送到了目的地。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值