首页
社区
课程
招聘
未解决 [求助]求解答一下杨季文老师黑皮书第10章保护模式T10-5.asm看不懂的几个地方,多谢
发表于: 2019-9-18 16:13 4155

未解决 [求助]求解答一下杨季文老师黑皮书第10章保护模式T10-5.asm看不懂的几个地方,多谢

2019-9-18 16:13
4155
首先,所贴的代码用masm5.0或者tasm2.0均可以编译生成t10-5.obj,然后再用tlink.exe /3 t10-5.obj可以正确生成exe文件。

 问题都在代码t10-5.asm的注释区用“看不懂的地方”标出来了 ,为了便于理解,我把书上对这个例子的说明也贴出来,这样方便大家快速了解整个代码,把时间集中在我提的几个问题上。




;T10-5.ASM
;演示任务切换和任务内特权级变换
	include 386SCD.ASM
	.386P
;全局描述符表
GDTSEG	SEGMENT	PARA	USE16
  GDT	LABEL	BYTE
dummy descriptor <>
normal descriptor <0ffffh,0,0,atdw,0>
normal_sel=normal-gdt
effgdt	label	byte
demotss	descriptor <demotsslen-1,demotssseg,,at386tss,>
demotss_sel=demotss-gdt
demoldtab descriptor <demoldtlen-1,demoldtseg,,atldt,>
demoldt_sel=demoldtab-gdt
;临时任务的任务状态段描述符
temptss descriptor <temptsslen-1,temptssseg,,at386tss+dpl2,>
temptss_sel=temptss-gdt
;临时任务代码段
tempcode descriptor <0ffffh,tempcodeseg,,atce,>
tempcode_sel=tempcode-gdt
;子程序代码段描述符
subr descriptor <subrlen-1,subrseg,,atce+d32,>
subr_sel=subr-gdt+rpl3

videobuff descriptor <0ffffh,0,0,0f00h+atdw+dpl3,0>
video_sel=videobuff-gdt
gdnum=($-effgdt)/(size descriptor)
gdtlen=$-gdt
gdtseg	ends

;演示任务的任务状态段
DemoTSSSEG	segment	para	use16
	dd	0			;链接字
	dd	DemoStack0LEN		;0级堆栈指针
	dw	DemoStack0_SEL,0
	dd	0			;1级堆栈指针
	dw	?,0
	dd	DemoStack2LEN		;2级堆栈指针
	DW	Demostack2_sel,0
	dd	0			;cr3
	dw	demobegin,0		;eip
	dd	0			;eflags
	dd	0			;eax
	dd	0			;ecx
	dd	0			;edx
	dd	0			;ebx
	dd	DemoStack2LEN		;esp
	dd	0			;ebp
	dd	0			;esi
	dd	0B8000H			;edi
	dw	video_sel,0		;es
	dw	DemoCode_sel,0		;cs
	dw	DemoStack2_sel,0	;ss
	dw	DemoData_sel,0		;ds
	dw	ToDLDT_SEL,0		;fs
	dw	ToTTSS_SEL,0		;gs
	dw	DemoLDT_SEL,0		;ldtr
	dw	0
	dw	$+2			;I/O许可位图指针
	db	0ffh			;I/O许可位图结束字节
DemoTSSLEN=$
DemoTSSSEG	ends

;演示任务的局部描述符表LDT
DemoLDTSEG segment para use16
demoldt	label byte
demostack0 descriptor <demostack0len-1,demostack0seg,,atdw+d32,>
demostack0_sel=(demostack0-demoldt)+til
demostack2 descriptor <demostack2len-1,demostack2seg,,atdw+d32+dpl2,>
demostack2_sel=(demostack2-demoldt)+til+rpl2
;演示代码段描述符
democode descriptor <democodelen-1,democodeseg,,atce+d32+dpl2,>
democode_sel=(democode-demoldt)+til+rpl2
;演示数据段描述符
demodata descriptor <demodatalen-1,demodataseg,,atdw+d32+dpl3,>
demodata_sel=(demodata-demoldt)+til
;把LDT作为普通数据段描述的描述符(DPL=2)
todldt descriptor <demoldtlen-1,demoldtseg,,atdw+dpl2,>
todldt_sel=(todldt-demoldt)+til
;把tss作为普通数据段描述的描述符(DPL=2)
TOTTSS descriptor <temptsslen-1,temptssseg,,atdw+dpl2,>
tottss_sel=(tottss-demoldt)+til
demoLDNUM=($-demoldt)/(size descriptor)
;指向子程序subrb的调用门(dpl=3)
tosubr gate <subrb,subr_sel,0,at386cgat+dpl3,0>
tosubr_sel=(tosubr-demoldt)+til+rpl2
;指向临时任务temp的任务门(DPL=3)
totempt gate <0,temptss_sel,0,attaskgat+dpl3,0>
totempt_sel=(totempt-demoldt)+til
demoldtlen=$-demoldt
demoldtseg ends
;演示任务的0级堆栈
DemoStack0SEG segment para use32
demostack0len=1024
	db demostack0len dup(0)
demostack0seg ends

DemoStack2SEG segment para use32
demostack2len=512
	db demostack2len dup(0)
demostack2seg ends

;演示任务的数据段
DemoDataSEG segment para use32
message db 'Value=',0
demodatalen=$
demodataseg ends

subrseg	segment	para use32
	assume cs:subrseg
subrb	proc	far
	push	ebp
	mov	ebp,esp
	pushad
	mov	eax,[ebp+12]
	mov	esi,eax
	mov	ah,7
	jmp	short subr2
subr1:	stosw
subr2:	lodsb
	or	al,al
	jnz	subr1
	mov	edx,[ebp+16]
	mov	ecx,8
subr3:	rol	edx,4
	mov	al,dl
	call	htoasc
	stosw
	loop	subr3
	popad
	pop	ebp
	ret	8
subrb	endp

htoasc	proc
	and	al,0fh
	add	al,90h
	daa
	adc	al,40h
	daa
	ret
htoasc	endp
subrlen=$
subrseg	ends

DemoCodeSEG segment para use32
	assume	cs:DemoCodeSEG
DemoBegin:
	mov	fs:tosubr.dcount,2	;?看不懂的地方2,这里的段寄存器为啥是fs
	push	dword ptr gs:temptask.treip	;?看不懂的地方3,这里的段寄存器为啥是gs
	push	offset Message
	call32	tosubr_sel,0	;这个子函数看得懂
	assume	ds:temptssseg
	push	gs
	pop	ds				;看不懂的地方4,为啥要push gs然后pop ds
	mov	ax,normal_sel
	mov	temptask.trds,ax
	mov	temptask.tres,ax
	mov	temptask.trfs,ax
	mov	temptask.trgs,ax
	mov	temptask.trss,ax
	jump32	totempt_sel,0	;看不懂的地方5,这里的totempt_sel选择子对应的是段temptssseg,而段temptssseg是个没赋初值的结构 
                                                ;体,怎么跳转过去呢?
DemoCodeLEN=$
DemoCodeSEG ends

temptssseg segment para use16
temptask taskss <>
	 db 0ffh
temptsslen=$
temptssseg ends

tempcodeseg segment para use16
	assume cs:tempcodeseg
Virtual:
	mov	BX,tempTSS_SEL
	LTR	BX
	jump16	demotss_sel,0	;这里进行了切换到任务DemoTSSSEG,因为EIP是demobegin,这里跳转到demobegin
toreal:
	clts
	mov	eax,cr0
	and	eax,0fffffffeh
	mov	cr0,eax
	jump16	<seg real>,<offset real>
tempcodelen=$
tempcodeseg ends

RDataSEG segment para use16
VGDTR PDESC <GDTLEN-1,>
SPVAR	DW	?
SSVAR	DW	?
RDataSEG ends

RCodeSEG segment para use16
	assume cs:rcodeseg,ds:rdataseg,es:rdataseg ;看不懂的地方1,以前没见到过一个段同时和两个段寄存器对应的代码
start:
	mov	ax,Rdataseg
	mov	ds,ax
	cld
	call	init_gdt

	mov	ax,DemoLDTSEG
	mov	fs,ax
	mov	cx,DemoLDNUM
	mov	SI,OFFSET DemoLDT
	call	init_ldt

	mov	ssvar,ss
	mov	spvar,sp
	
	lgdt	qword ptr vgdtr
	cli
	mov	eax,cr0
	or	eax,1
	mov	cr0,eax
	jump16	<tempCode_sel>,<offset virtual>
Real:
	mov	ax,RDataSEG
	mov	ds,ax
	lss	sp,dword ptr spvar
	sti
	mov	ax,4c00h
	int	21h

init_gdt proc near
	push	ds
	mov	ax,gdtseg
	mov	ds,ax
	mov	cx,gdnum
	mov	si,offset effgdt
initg:	mov	ax,[si].basel
	movzx	eax,ax
	shl	eax,4
	shld	edx,eax,16
	mov	[si].basel,ax
	mov	[si].basem,dl
	mov	[si].baseh,dh
	add	si,size descriptor
	loop	initg
	pop	ds

	mov	bx,16
	mov	ax,gdtseg
	mul	bx
	mov	word ptr vgdtr.base,ax
	mov	word ptr vgdtr.base+2,dx
	ret
init_gdt endp

init_ldt proc
ildt:	mov	ax,fs:[si].basel
	movzx	eax,ax
	SHL	eax,4
	shld	edx,eax,16
	mov	fs:[si].basel,ax
	mov	fs:[si].basem,dl
	mov	fs:[si].baseh,dh
	add	si,size descriptor
	loop	ildt
	ret
init_ldt endp
RCodeSEG ends
	end start
把386scd.asm也贴上来,供参考
;符号常量等的定义
;存储段描述符/系统段描述符结构类型的定义
descriptor	struc
limitl		dw	0
basel		dw	0
basem		db	0
attributes	dw	0
baseh		db	0
descriptor	ends
;门描述符结构类型的定义
gate	struc
offsetl		dw	0
selector	dw	0
dcount		db	0
gtype		db	0
offseth		dw	0
gate	ends
;伪描述符结构类型的定义
pdesc	struc
limit	dw	0
base	dd	0
pdesc	ends
;TSS结构类型的定义
taskss	struc
	trlink	dw	?,0
	tresp0	dd	?
	trss0	dw	?,0
	tresp1	dd	?
	trss1	dw	?,0
	tresp2	dd	?
	trss2	dw	?,0
	trcr3	dd	?
	treip	dd	?
	treflag	dw	?,?
	treax	dd	?
	trecx	dd	?
	tredx	dd	?
	trebx	dd	?
	tresp	dd	?
	trebp	dd	?
	tresi	dd	?
	tredi	dd	?
	tres	dw	?,0
	trcs	dw	?,0
	trss	dw	?,0
	trds	dw	?,0
	trfs	dw	?,0
	trgs	dw	?,0
	trldt	dw	?,0
	trflag	dw	0
	triomap	dw	$+2
taskss	ends
;存储段描述符类型值说明
ATDR=90h
ATDW=92H
ATDWA=93H
ATCE=98H
ATCER=9AH
ATCCO=9CH	;存在的只执行一致代码段类型值
ATCCOR=9EH
;系统描述符和门描述符类型值说明
ATLDT=82h	;局部描述符表段值类型
ATTASKGAT=85H
AT386TSS=89H	;?386TSS类型值
AT386CGAT=8CH	;386调用门
AT386IGAT=8EH
AT386TGAT=8FH
;DPL和RPL值说明
DPL1=20H
DPL2=40H
DPL3=60H
RPL1=01H
RPL2=02H
RPL3=03H
IOPL1=1000H
IOPL2=2000H
IOPL3=3000H
;其他常量值说明
D32=4000H	;在描述可执行段的描述符中,表示32位代码段
TIL=04H	;描述符表标志
VFML=0002H
IFL=0200H
;32位偏移的段间转移宏指令
JUMP32	MACRO	selector,offsetv
	DB	0eaH
	DW	offsetv	;32位偏移
	DW	0
	DW	selector
	ENDM
CALL32	MACRO	selector,offsetv
	db	09ah
	dw	offsetv
	dw	0
	dw	selector
	endm
JUMP16	MACRO	selector,offsetv
	db	0eah
	dw	offsetv
	dw	selector
	endm
call16	MACRO	selector,offsetv
	db	9ah
	dw	offsetv
	dw	selector
	endm
再次感谢各位大大!

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-9-18 16:16 被lishua编辑 ,原因: 对原贴中罗嗦的地方进行删除。
收藏
免费 1
支持
分享
最新回复 (4)
雪    币: 1558
活跃值: (3442)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
内核不需要死扣全部代码,后面边边角角看不懂的地方更多了,只要看懂大概流程就可以了(我也看不懂,只知道任务切换的大概流程)
2019-9-18 16:59
0
雪    币: 822
活跃值: (290)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
说的也是,但是书也是从前往后看过来的,多了这么多看不懂的地方,总觉得心里不踏实。。
2019-9-20 09:07
0
雪    币: 217
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
 mov fs:tosubr.dcount,2  ;?看不懂的地方2,这里的段寄存器为啥是fs
 push    dword ptr gs:temptask.treip   ;?看不懂的地方3,这里的段寄存器为啥是gs
 push    offset Message
第一条指令是对调用门中压入参数的个数(为2个参数),后面两条指令,实际上就是这两个参数的内容。fs、gs这两个段寄存器都对调用门、临时任务门进行了别名处理,即将它们两个作为数据来初始化,否则,要压入的两个参数就没法初始化到门中相应字段。fs\gs在DemoTSSseg段中已初始化了,看你的贴的代码中的第56、57行。我也正在学习,在我的虚拟机上该程序运行正常。
2020-1-16 12:08
0
雪    币: 822
活跃值: (290)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
谢谢楼上
最后于 2020-1-29 20:57 被lishua编辑 ,原因:
2020-1-22 11:25
0
游客
登录 | 注册 方可回帖
返回
//