程序的加载和执行(四)——《x86汇编语言:从实模式到保护模式》读书笔记24


通过本文能学到什么?

  • 怎样跳转到用户程序
  • 用户程序通过调用内核过程完成自己的功能
  • 怎样从用户程序返回到内核

接着上篇博文说。

1.跳转到用户程序

截取内核程序的后半部分代码(文件名:c13_core.asm):

570         call load_relocate_program
571
572         mov ebx,do_status
573         call sys_routine_seg_sel:put_string
574
575         mov [esp_pointer],esp               ;临时保存堆栈指针
576
577         mov ds,ax
578
579         jmp far [0x10]                      ;控制权交给用户程序(入口点)
580                                             ;堆栈可能切换
581
582  return_point:                              ;用户程序返回点
583         mov eax,core_data_seg_sel           ;使ds指向核心数据段
584         mov ds,eax
585
586         mov eax,core_stack_seg_sel          ;切换回内核自己的堆栈
587         mov ss,eax
588         mov esp,[esp_pointer]
589
590         mov ebx,message_6
591         call sys_routine_seg_sel:put_string
592
593         ;这里可以放置清除用户程序各种描述符的指令
594         ;也可以加载并启动其它程序
595
596         hlt
597            

570:调用过程load_relocate_program,关于此过程的详细讲解,可以参考我的博文:
程序的加载和执行(二)——《x86汇编语言:从实模式到保护模式》读书笔记22
程序的加载和执行(三)——《x86汇编语言:从实模式到保护模式》读书笔记23
在这个过程的末尾,有

517         mov ax,[es:0x04]

注意,这时候ES是用户头部段的选择子,如下图所示,这句话就是取出头部偏移0x04处的头部段选择子,赋值给AX

其实这么做有点绕弯子,因为ES自身的值就是头部段选择子,所以这句话可以修改为:

        mov ax,es

也就是说,这个过程把用户程序头部段的选择子保存在AX中,作为返回参数。
再复习一下这个过程的输入和返回参数。

387   load_relocate_program:                   ;加载并重定位用户程序
388                                            ;输入:ESI=起始逻辑扇区号
389                                            ;返回:AX=指向用户程序头部的选择子 

572~573:调用过程put_string,在屏幕上输出”Done.”,并且换行,表示加载和重定位工作已经完成。

369         do_status        db  'Done.',0x0d,0x0a,0

575:通过把当前ESP的值写入内核数据段来保存内核的栈指针。内核数据段第378行,保留了一个双字,专门用来保存内核栈指针。

378         esp_pointer      dd 0              ;内核用来临时保存自己的栈指针 

当内核把控制权交给用户程序后,用户程序应该切换到自己的栈。当从用户程序返回到内核的时候,内核需要从这个内存位置还原自己的栈指针。
577:将用户头部段的选择子传送到DS,也就是说用户程序应该明白,从内核那里接过控制权的时候,DS指向了用户程序的头部段。
579:如上图所示,偏移0x10处,绿色部分就是用户程序的入口。一个华丽的间接远转移,终于跳到了用户程序。

2.用户程序的执行

截取用户程序的部分代码(文件名:c13.asm)。

7   SECTION header vstart=0
8
9          program_length   dd program_end          ;程序总长度#0x00
10
11         head_len         dd header_end           ;程序头部的长度#0x04
12
13         stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
14         stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
15                                                  ;以4KB为单位
16
17         prgentry         dd start                ;程序入口#0x10
18         code_seg         dd section.code.start   ;代码段位置#0x14
19         code_len         dd code_end             ;代码段长度#0x18
20
21         data_seg         dd section.data.start   ;数据段位置#0x1c
22         data_len         dd data_end             ;数据段长度#0x20
40  ;===============================================================================
41  SECTION data vstart=0
42
43           buffer times 1024 db  0         ;缓冲区
44
45           message_1         db  0x0d,0x0a,0x0d,0x0a
46                             db  '**********User program is runing**********'
47                             db  0x0d,0x0a,0
48           message_2         db  '  Disk data:',0x0d,0x0a,0
49
50  data_end:
51
52  ;===============================================================================
53        [bits 32]
54  ;===============================================================================
55  SECTION code vstart=0
56  start:
57           mov eax,ds
58           mov fs,eax
59
60           mov eax,[stack_seg]
61           mov ss,eax
62           mov esp,0
63
64           mov eax,[data_seg]
65           mov ds,eax
66
67           mov ebx,message_1
68           call far [fs:PrintString]
69
70           mov eax,100                         ;逻辑扇区号100
71           mov ebx,buffer                      ;缓冲区偏移地址
72           call far [fs:ReadDiskData]          ;段间调用
73
74           mov ebx,message_2
75           call far [fs:PrintString]
76
77           mov ebx,buffer
78           call far [fs:PrintString]           ;too.
79
80           jmp far [fs:TerminateProgram]       ;将控制权返回到系统
81
82  code_end:

用户程序从第57行开始执行。注意,此时DS指向用户程序的头部段。
57~58:把DS赋值给FS,令FS指向头部段。因为后面要令DS指向用户程序的数据段。
60~62:用户栈段的初始化,并且令ESP=0;这样就完成了栈的切换。
64~65:令DS指向用户数据段。
67~68:调用内核提供的例程put_string;本质上是一个16位间接绝对远调用。

24;-------------------------------------------------------------------------------
25         ;符号地址检索表
26         salt_items       dd (header_end-salt)/256 ;#0x24
27
28         salt:                                     ;#0x28
29         PrintString      db  '@PrintString'
30                     times 256-($-PrintString) db 0
31
32         TerminateProgram db  '@TerminateProgram'
33                     times 256-($-TerminateProgram) db 0
34
35         ReadDiskData     db  '@ReadDiskData'
36                     times 256-($-ReadDiskData) db 0

当内核对用户程序的符号表完成重定位后,PrintString处就拥有了内核例程put_string的入口地址(低地址处是4字节的偏移地址,高地址处是2字节的段选择子);

80           jmp far [fs:TerminateProgram]       ;将控制权返回到系统

执行这条指令的时候,处理器根据[fs:TerminateProgram]进行内存寻址,得到偏移地址和段选择子,然后压栈CS,再压栈EIP,再然后把刚才取得的偏移地址和段选择子赋值给EIP和CS,于是程序的执行流就转移到内核代码段中的put_string过程了。说得通俗点,就是用户程序调用了内核的代码,完成了自己的功能。这有点像Linux中的系统调用。

如果你对call far指令不熟悉的话,可以参考我的博文:
call、ret、retf 指令详解

70~72:调用内核过程read_hard_disk_0,从硬盘读取一个扇区。

    read_hard_disk_0:                       ;从硬盘读取一个逻辑扇区;EAX=逻辑扇区号;DS:EBX=目标缓冲区地址;返回:EBX=EBX+512

用户程序传入的逻辑扇区号是100,当然这个值也可以是别的,这里仅仅是举个例子。目标缓冲区地址是数据段内buffer标号处,这里定义了1024字节的0。为了用户程序可以顺利运行并且能看到效果,我们需要在100扇区写点东西。在配书代码中,提供了一个文本文件diskdata.txt,它的大小刚好是512字节。用UltraEdit软件打开后,如下图所示:

可以看到,这个文本刚好是512字节,最后一个字节是字符]
77~78:显示从刚才硬盘读出来的内容。
有一个细节需要说明,内核过程put_string要求字符串必须以0终止,不然会无尽地显示下去。可是我们这个文件是以]结尾的,这是否会影响显示呢?答案是不会。因为目标缓冲区buffer标号处,定义了1024字节的0,当把目标文件读到这里后,前512字节被覆盖,字符]后面有512个0,所以显示到]为止。
到这个时候,用户程序的工作算是完成了,但是还差最后一步,把控制权交给系统。

3.返回到内核

80:一个潇洒的jmp far,跳到内核过程return_point,以把控制权返回给内核。注意,jmpcall的区别是:前者有去无回,后者有去有回。
我们再穿越到内核的代码:

582    return_point:                            ;用户程序返回点
583         mov eax,core_data_seg_sel           ;使ds指向核心数据段
584         mov ds,eax
585
586         mov eax,core_stack_seg_sel          ;切换回内核自己的堆栈
587         mov ss,eax
588         mov esp,[esp_pointer]
589
590         mov ebx,message_6
591         call sys_routine_seg_sel:put_string
592
593         ;这里可以放置清除用户程序各种描述符的指令
594         ;也可以加载并启动其它程序
595
596         hlt

583~584:DS重新指向内核数据段;
586~588:切换到内核的栈,并恢复之前保存的ESP的值。
对于一个内核来说,接下来应该回收前一个用户程序所占用的内存,并启动下一个用户程序。不过因为是初学,我们就不搞那么复杂了,于是596行,让处理器处于停机状态。从此世界安静了。

下一篇博文,我们会讲本章代码的编译、运行和调试。敬请期待……

程序的加载和执行(四)——《x86汇编语言:从实模式到保护模式》读书笔记24相关推荐

  1. 程序的加载和执行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25

    程序的加载和执行(五)--<x86汇编语言:从实模式到保护模式>读书笔记25 前面几篇博文终于把代码分析完了.这篇就来说说代码的编译.运行和调试. 1.代码的编译及写入镜像文件 之前我们都 ...

  2. 程序的加载和执行(六)——《x86汇编语言:从实模式到保护模式》读书笔记26

    程序的加载和执行(六)--<x86汇编语言:从实模式到保护模式>读书笔记26 通过本文能学到什么? NASM的条件汇编 用NASM编译的时候,通过命令行选项定义宏 Makefile的条件语 ...

  3. 程序的加载和执行(一)——《x86汇编语言:从实模式到保护模式》读书笔记21

    程序的加载和执行(一) 本文及之后的几篇博文是原书第13章的学习笔记. 本章主要是学习一个例子,对应的代码分为3个文件: ;代码清单13-1;文件名:c13_mbr.asm;文件说明:硬盘主引导扇区代 ...

  4. 程序的加载和执行(三)——《x86汇编语言:从实模式到保护模式》读书笔记23

    程序的加载和执行(三)--读书笔记23 接着上次的内容说. 关于过程load_relocate_program的讲解还没有完,还差创建栈段描述符和重定位符号表. 1.分配栈空间与创建栈段描述符 462 ...

  5. html 执行外部js的函数,javascript – Chrome扩展程序:加载并执行外部脚本

    我无法在我的chrome扩展程序中加载和执行外部js-script.看起来和 this question一样,但我仍然无法弄清楚为什么它在我的情况下不起作用. 我的想法是,我希望在我的内容脚本中有一些 ...

  6. 小程序动画加载只执行一次的问题

    问题 最近, 想做个小程序的圆盘抽奖出来, 想要实现的效果是点击一次就旋转一次. 不过每次只有第一次点击有效, 再次点击就没有任何动画效果. 代码如下 rotate: function() {// 创 ...

  7. 任务和特权级保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记27

    本文及后面的几篇文章是原书第14章的读书笔记. 1.LDT(局部描述符表) 在之前的学习中,不管是内核程序还是用户程序,我们都是把段描述符放在GDT中.但是,为了有效实施任务间的隔离,处理器建议每个任 ...

  8. X86汇编语言从实模式到保护模式13:保护模式程序的动态加载和执行

    目录 1. 引入保护模式对程序加载与执行的影响 1.1 对应用程序的影响 1.2 对操作系统的影响 1.3 本章程序总体结构 2. MBR加载内核过程分析 2.1 内核头部段分析 2.1.1 内核总长 ...

  9. 第四回-MBR加载与执行实验

    本回我们进行一次实验性的实操环节,机器上电运行相应BIOS代码,然后BIOS加载硬盘的MBR分区内容,并执行MBR代码.本次实验使用Bochs虚拟机模拟整个x86架构的真实机器.实验过程中我们需要为B ...

最新文章

  1. java中普通代码块,构造代码块,静态代码块的区别及代码示例
  2. 在线白板,基于socket.io的多人在线协作工具
  3. 网上有打印按键怎么设置下载_打印机共享怎么设置 如何设置打印机共享【详细攻略】...
  4. 如何判断线程运行结束
  5. python 修改字符串 循环_python – 模式匹配并用if else循环替换字符串
  6. [Java基础]System类的常用方法
  7. 石家庄学院c语言试题,谁会高级语言程序设计?要求用C语言,帮帮我把,愁死我啦...
  8. 王昶衡(帮别人名字作诗)
  9. label标签 for属性
  10. java sqlite 池_java – SQLite连接池
  11. C#:System.Data.SQLite数据库介绍
  12. vs 2015 密钥
  13. 禁忌搜索算法求解TSP问题python实现
  14. Python实现简易TCP服务器
  15. Excel VBA快速去除Excel中的所有公式
  16. 学好UI设计必备软件
  17. 定位“良心优品”,国民手机Z5能否让联想重回辉煌?
  18. SPCA5XX摄像头驱动源码分析
  19. office之PPT插入页码没有反应
  20. 利用adb设置安卓http代理

热门文章

  1. Ubuntu下Qt中使用pcl库
  2. NYOJ 920 Trees
  3. CLR Essential Types
  4. 浅谈python异步IO,同步IO,线程与进程~
  5. LVS NAT/DR
  6. 策略模式(stragegy)
  7. 区域增长——初步学习
  8. 删除一个目录及其子目录下的所有.svn文件
  9. Hdu 1283 钱币兑换问题
  10. Kooboo 全文索引研究