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

前面几篇博文终于把代码分析完了。这篇就来说说代码的编译、运行和调试。

1.代码的编译及写入镜像文件

之前我们都是在命令行输入命令进行编译和写入,源文件少的时候还不觉得麻烦,当源文件多了,就会觉得特别麻烦。有没有简单的方法呢?
当然有,就是用make工具。

1.1.什么是make工具

make是一个命令工具,它解释Makefile中的指令。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。

注意:make命令不仅仅用于编译程序。无论何时,当需要通过多个输入文件来生成输出文件时,我们都可以利用它来完成任务。

1.2.关于Makefile

Makefile有自己的书写格式、关键字、函数。像C语言有自己的格式、关键字和函数一样。而且在Makefile中可以使用系统shell所提供的任何命令来完成想要的工作。

以上只是非常简略地对makeMakefile进行介绍。关于他们的使用,可以搜索相关关资料来学习。

1.3.针对第13章源文件的Makefile

1.3.1.我的Makefile文件

BIN = c13_mbr.bin c13_core.bin c13.bin empty
A_DIR = /home/cjy/a.img
C_DIR = /home/cjy/c.imgall:$(BIN).PHONY:all cleanc13_mbr.bin:c13_mbr.asmnasm $< -o $@dd if=$@ of=$(A_DIR)c13_core.bin:c13_core.asmnasm $< -o $@dd if=$@ of=$(C_DIR) bs=512 seek=1 conv=notruncc13.bin:c13.asmnasm $< -o $@dd if=$@ of=$(C_DIR) bs=512 seek=50 conv=notruncempty:diskdata.txtdd if=$< of=$(C_DIR) bs=512 seek=100 conv=notrunctouch $@clean:$(RM) $(BIN)

这就是我自己的写的Makefile,至于为什么这样写,还有Makefile的入门知识,我以后会写博文来介绍。

1.3.2.使用说明

  1. 根据自己的Bochs的配置文件中A盘和C盘的路径修改A_DIR=C_DIR=后面的路径;
  2. 把修改后的内容保存为文本文件,命名为Makefile,放在第13章的目录下;如下图所示:
  3. 在命令行键入make,回车,坐等编译和写入完成。如下图所示:

    可以看到,我们需要的.bin文件都生成了,对A盘和C盘的写入也完成了。

2.运行结果

终于可以看结果了,我们启动Bochs,运行结果如图:

3.在源码的基础上修修改改

仅仅得到书上的结果是不够的,不爱折腾的程序员不是好程序员。

3.1.写代码就像写作文

我觉得写代码和写作文是一样一样的。想想我们大多数人学写作文的过程:开始不会写,怎么办?抄呗。(这个就是学习人家的源代码,跑出人家的结果。)再然后呢,我们不是全抄,而是在人家的基础上修改成自己的。(这个就是我们现在要做的事情,在人家代码的基础上加上自己的想法,看看结果会怎么样。)最后呢,我们不需要抄了,上了考场就可以自己写出来,结果得分还挺高。(这就是我们的终极目标,博采众长,自成一家。)

我针对第13章的代码,制作了自己的补丁包。有需要的朋友可以去下载。下载地址是:
http://download.csdn.net/detail/u013490896/9486717
或者
https://github.com/LeslieChe/from-real-mode-to-protected-mode

接下来,我会针对补丁包,对修改的部分加以讲解。

3.2.让字符显示出不同的颜色

看了上面的运行结果,你是否觉得颜色有点单调?好的,我们修改源码,把字符的属性作为参数传给过程。
首先我们定义一些常量,表示不同的颜色。

     ;字符属性(都是黑底)GREEN         equ 0x02RED           equ 0x04BLUE_LIGHT    equ 0x09YELLOW        equ 0x0e
put_string:   ;字符串显示例程;显示0终止的字符串并移动光标 ;输入:(1)  push 属性值;     (2)  DS:EBX=串地址

除了要把字符串的首地址传入DS:EBX之外,还要压入属性值。
在Beyond Compare软件中比较修改后和修改前的差异,如下图

另外,过程put_char有两个地方需要修改。第二个地方是一个小BUG.

这样修改后,我们调用put_string的时候,需要先压栈字符属性。如下图:

修改后的运行效果如下图:

3.3.对过程put_hex_dword的修改

3.3.1.配书源码讲解

之前的博文没有讲解这个过程,所以先说一下这个过程。
源码是:

201;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助
202put_hex_dword:                              ;在当前光标处以十六进制形式显示
203                                            ;一个双字并推进光标
204                                            ;输入:EDX=要转换并显示的数字
205                                            ;输出:无
206         pushad
207         push ds
208
209         mov ax,core_data_seg_sel           ;切换到核心数据段
210         mov ds,ax
211
212         mov ebx,bin_hex                    ;指向核心数据段内的转换表
213         mov ecx,8
214  .xlt:
215         rol edx,4
216         mov eax,edx
217         and eax,0x0000000f
218         xlat
219
220         push ecx
221         mov cl,al
222         call put_char
223         pop ecx
224
225         loop .xlt
226
227         pop ds
228         popad
229         retf
374         bin_hex          db '0123456789ABCDEF'

这段代码的原理很简单,EDX寄存器是32位的,从右到左,4位一组,一共分成8组。每组的值都在0x0~0xF之间,我们把它的值转换成对应的字符0~F;
第218行用了查表指令xlat,该指令要求事先在DS:EBX(32位模式)或者DS:BX(16位模式)处存放一张表格,指令执行时,用AL的值作为偏移量,从表格对应位置取回一个字节,传送到AL;举例来说,如果在DS:EBX处存放了第374行定义的表格,那么当AL=0的时候,执行xlat后,AL中的值就是字符0的ASCII码。
第215行用了循环左移指令rol,第一次循环将EDX的高4位移到最右边,和0x0000_000F相与,于是AL中就得到高四位对应的值,然后查表,就得到对应的字符。
第221~222,把这个字符打印到屏幕上(打印位置是当前光标所在处,并推进光标)。

3.3.2.我的修改

修改前,假设在用户程序中,我们要输出寄存器EAX的值,那么我们需要

    mov edx,eaxcall far [fs:put_hex_dword]

现在我希望可以这么用:

    push 'eax'push eaxcall far [fs:put_hex_dword]

也就是通过栈传递参数,第一个参数是字符串'eax',第二个参数是寄存器EAX的值。
执行效果如下(浅蓝色第一行):

也许有的朋友会奇怪,push 'eax'这种写法可以吗?
对于NASM编译器,这种写法是允许的。'eax'属于字符常数。
一个字符常数最多由包含在双引号或单引号中的四个字符组成。一个具有多个字符的字符常数会被序列化成小端序。

    mov eax,'abcd'

相当于

    mov eax,0x64636261

所以,我们可以把'eax'这种字符常数压入栈中(因为在32位模式下,所以默认按4个字节压入,最高位会补零),作为参数传递给过程。在过程中把这个参数的每个字符提取出来,显示在屏幕上。
下图显示这个过程的第一处改动:

从标号.p_char.ok之间的代码,就是从栈中依次取出我们要显示的字符(遇到0值为止),输出到屏幕。
.ok后面的2行,是为了打印等号=;

这个过程的第二处改动如下图:

3.3.3.本地Label

在源码中,会发现作者在很多地方都使用了以.开头的标号,这样的标号属于本地标号。
以下摘自NASM的官方手册
http://www.nasm.us/doc/nasmdoc3.html#section-3.9

NASM gives special treatment to symbols beginning with a period. A label beginning with a single period is treated as a local label, which means that it is associated with the previous non-local label. So, for example:

label1  ; some code .loop ; some more code jne     .loop ret label2  ; some code .loop ; some more code jne     .loop ret

In the above code fragment, each JNE instruction jumps to the line immediately before it, because the two definitions of .loop are kept separate by virtue of each being associated with the previous non-local label.

我觉得这样做可以方便用户,不用为给label起名字而伤脑筋。

3.4.符号表的重定位

我的博文
程序的加载和执行(三)——《x86汇编语言:从实模式到保护模式》读书笔记23
已经指出在重定位符号表的时候,有一个小BUG.
我准备加入调试打印信息,证明这确实是一个BUG,同时也证明我的修改是对的。

第575~583行,我加入了一些代码,用于打印将要比较的用户符号和内核符号。

执行完573行时候,DS:ESI指向了内核符号表的某个条目,ES:EDI指向了用户符号表的某个条目。红色代码就是把这两个条目打印到屏幕上,左边是用户符号,右边是内核符号。
过程put_usr_salt的代码如下:

输入:push 属性es:ebx 中是符号的起始地址
输出:无
64  put_usr_salt:  ;打印用户的符号
65          push ecx
66          mov ebp,esp
67          mov ch,[ebp+3*4]
68      .getc:    ;本地Label
69          mov cl,[es:ebx]
70          or cl,cl
71          jz .out
72          call put_char
73          inc ebx
74          jmp .getc
75      .out:
76          mov cl,0x20
77          call put_char
78          call put_char
79          call put_char
80          call put_char ;打印四个空格
81
82          pop ecx
83          retf 4

67:从栈中取得属性值
68~74:用于打印以0结尾的字符串。
76~80:用于打印4个空格。
过程put_core_salt的代码类似,这里不再赘述。

看一下执行效果吧:

左边黄色的是用户符号,右边红色的是内核符号。我们可以清晰地看到符号的比较过程:
@TerminateProgram比较了2次后匹配上了;
@ReadDiskData比较了2次后匹配上了;
@PrintDwordAsHexString比较了3次才匹配上。

这篇博文就到这里。下篇博文,会讲NASM的条件编译,Makefile的一些改动,另外还有13章的习题。敬请期待…

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

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

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

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

    程序的加载和执行(四)--<x86汇编语言:从实模式到保护模式>读书笔记24 通过本文能学到什么? 怎样跳转到用户程序 用户程序通过调用内核过程完成自己的功能 怎样从用户程序返回到内核 接 ...

  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. Java的加载与执行原理详解 Java程序从编写到最终运行经历了哪些过程

    前言 Java程序从编写到最终运行大概可概括为3个阶段:编写.编译.运行阶段. 一.编写阶段 程序员在硬盘某个位置新建一个xxx.java文件 使用记事本或者其他文本编辑器例如EditPlus打开xx ...

最新文章

  1. Glusterfs 分布式存储安装部署
  2. 正则表达式(开源框架)
  3. HTACCESS 伪静态书写规则
  4. 科大星云诗社动态20201206
  5. HTTP协议头部与Keep-Alive模式详解-Content-Length Transfer-Encoding
  6. vue地址栏输入路由跳转到首页_Vue路由跳转到新页面时 默认在页面最底部 而不是最顶部 的解决...
  7. compile php with openssl on mac osx error 填坑
  8. python读压缩文件内容_使用Python读写及压缩和解压缩文件的示例
  9. 让IIS7支持SSI功能(用来支持shtml)的方法
  10. linux usb重定向window,基于Linux的USB设备重定向研究.pdf
  11. android 微信朋友圈动画,使用Android 模仿微信朋友圈图片拖拽返回
  12. 数据库练习:分数排名
  13. http请求 302解决方法
  14. 电脑如何在线制作文件二维码?二维码怎么下载文件?
  15. dataframe 按条件筛选行
  16. iframe 动态 加载 src
  17. visual 创建c语言程序吗,visual studio怎么创建c语言
  18. canvas - 绘制图片,图片变模糊问题解决)
  19. APIDOC- API文档生成工具——node
  20. c语言中 1ul什么意思,c语言 #define 中的UL

热门文章

  1. NYOJ 137 取石子(三)
  2. DirectX11 初探XMVECOTRXMMATRIX
  3. Ajax请求,跨域小坑
  4. 第一阶段站立会议05
  5. C#.NET 大型企业信息化系统 - 防黑客攻击 - SSO系统加固优化经验分享
  6. My Goal For SE
  7. 两经纬度之间的距离计算
  8. APIO2010 特别行动队 斜率优化DP算法笔记
  9. Linq to XML的练习
  10. 嵌入式开发基础环境搭建