专业:网络空间安全 姓名:杨芷钰 学号:22920172204263
在命令行输入如下命令:
[~]
moocos-> cd moocos
[~/moocos]
moocos-> cd ucore_lab/related_info/lab0
[~/moocos/ucore_lab/related_info/lab0]
moocos-> gcc -S -m32 lab0_ex1.c
copy
得到一个命名为lab0_ex1.s的文件。
理解分析lab0_ex1.c和lab0_ex1.s的内容以及两个文件之间的关系。
lab0_ex1.c的内容如下:
int count=1;
int value=1;
int buf[10];
void main()
{
asm(
"cld \n\t"
"rep \n\t"
"stosl"
:
: "c" (count), "a" (value) , "D" (buf[0])
:
);
}
copy
其中asm()表示汇编代码的开始和结束,括号内的内容是具体的GCC内联汇编指令代码。
GCC扩展内联汇编的基本格式如下:
asm [volatile]
(
Assembler Template
:Output Operands
[:Input Operands
[:Clobbers]]
)
copy
在asm()中的汇编代码,如果有多行汇编,则每一行都要加上"\n\t"。其中 的“\n”是换符,"\t”是tab符,在每条命令的结束加这两个符号,是为了让GCC把内联汇编代码翻译成一般的汇编代码时能够保证换行和留有一定的空格。
asm()中的cld,rep,stosl这几条语句的功能是向buf中写上count个value值。
冒号后的语句指明输入,输出和被改变的寄存器。通过冒号以后的语句,编译器就知道指令需要和改变哪些寄存器。其中符号"c" (count)指示要把count的值放入ecx寄存器; "a" (value) 指示要把value的值放入eax寄存器, "D" (buf[0])指示要把buf[0]的值放入寄存器edi。
lab0_ex1.S的主要内容如下:
movl count, %edx
movl value, %eax
movl buf, %ebx
movl %edx, %ecx
movl %ebx, %edi
#APP
cld
rep
stosl
#NO_APP
copy
前5句movl操作:将count的值存入edx再将edx的值存入ecx,实际上就是将count的值存入ecx寄存器;将value的值存入eax寄存器;将buf[0]的值存入ebx,再将ebx的值存入edi,实际上就是将buf[0]的值存入edi寄存器。这5行代码对应lab0_ex1.c中的: "c" (count), "a" (value) , "D" (buf[0])。
每次执行movl指令后,数据传送后esi和edi寄存器会自动改变,为另一次传送做准备。esi和edi可能随着标志DF的不同自动递增或者自动递减,如果DF标志为0则movl指令后esi和edi会递增,反之会递减。为了设置DF标志,可以使用一下指令:cld将DF标志清零。
rep指令用于按照特定次数重复执行字符串指令,有ecx寄存器控制,但不需要额外的loop指令。
stos指令的含义是将寄存器(ax/eax)里的内容(一个字或一个字节)存储(store)到内存单元(地址es:di)。同时CPU自动会根据DF的值自动递增或者递减edi中的值,如果设置了DF,即DF=1(direction flag),那么edi会在该指令执行后减小,如果没有设置DF,即DF=0,那么edi的值会增加。stosl的含义是从eax存四个字节到edi指定的位置,edi的位置再存储后向后或向前移位4个。因为前面执行过cld操作,因此代码中stosl的含义应是从eax存四个字节到edi指定的位置,edi的位置再存储后向后移位4个。
由上述分析代码可以看出,lab0_ex1.s文件是由lab0_ex1.c文件通过GCC编译器转化后输出的汇编文件,实际上就是将c语言转化成了AT&T汇编语言。
在查找资料过程中发现汇编文件.s结尾与.S文件存在区别,记录如下:
.s结尾文件:小写的 s文件,在后期阶段不会再进行预处理操作了,所以我们不能在其内写上预处理语句,一般是 .c 文件经过汇编器处理后的输出。 例如上述操作中,通过 GCC 编译器就可以指定 -S 选项进行输出,且是经过预处理器处理后的了。
.S结尾的文件:大写的 S 文件,还会进行预处理、汇编等操作,所以我们可以在这里面加入预处理的命令。编译器在编译汇编大 S 文件之前会进行预处理操作。
在命令行输入如下命令:
[~]
cd /home/moocos/moocos/ucore_lab/related_info/lab0
[~/moocos/ucore_lab/related_info/lab0]
moocos-> gcc -g -m32 lab0_ex2.c
copy
得到文件a.out。
尝试用gdb调试程序lab0_ex2.c。
截图如下:
但是因为lab0_ex2.c是一个正确代码,因此我按照ucore实验指导书中写了一段错误代码并调试如下,以便更清楚的看到调试的功能和过程:
代码:
/*bugging.c*/
#include <stdio.h>
#include <stdlib.h>
static char buff[256];
static char *string;
int main ()
{
/*int i;
for(i=0; i<256; i++)
{
buff[i] = 0;
}
string = buff;*/ //注释部分为更正后增加的的正确代码
printf("Please input a string: ");
gets (string);
printf ("\nYour string is: %s\n", string);
}
copy
调试截图:
gdb a.out:加载a.out可执行文件。
run:执行装入a.out的命令。
where:查看程序出错的地方。
list:查看错误附近的代码。
break 15:在第15行设置断点,看看是否在15行出错。重新运行程序,到15行停止,这时程序正常,然后执行单步命令next。
程序确实出错,能够导致gets函数出错的因变量string,用print命令查看string的值。
由此可知string指向的是一个无效指针,修改程序,重新编译程序,将看到正确程序运行结果,如下所示:
在命令行输入如下命令:
[~/moocos/ucore_lab/related_info/lab0]
moocos-> gcc -g -m32 lab0_ex3.c 2>&1|tee make.log
copy
如果得到提示gcc error,请尝试读取make.log并修复错误,如果gcc编译成功,你将会得到文件a.out,并尝试回答以下问题:
对于如下代码段:
...
#define STS_CG32 0xC // 32-bit Call Gate
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define SETGATE(gate, istrap, sel, off, dpl) { \
(gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \
(gate).gd_ss = (sel); \
(gate).gd_args = 0; \
(gate).gd_rsv1 = 0; \
(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \
(gate).gd_s = 0; \
(gate).gd_dpl = (dpl); \
(gate).gd_p = 1; \
(gate).gd_off_31_16 = (uint32_t)(off) >> 16; \
}
/* Gate descriptors for interrupts and traps */
struct gatedesc {
unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment
unsigned gd_ss : 16; // segment selector
unsigned gd_args : 5; // # args, 0 for interrupt/trap gates
unsigned gd_rsv1 : 3; // reserved(should be zero I guess)
unsigned gd_type : 4; // type(STS_{TG,IG32,TG32})
unsigned gd_s : 1; // must be 0 (system)
unsigned gd_dpl : 2; // descriptor(meaning new) privilege level
unsigned gd_p : 1; // Present
unsigned gd_off_31_16 : 16; // high bits of offset in segment
};
...
copy
如果在其他代码段中有如下语句:
unsigned intr;
intr=8;
SETGATE(intr, 0,1,2,3);
copy
请问执行上述指令后,intr的值是多少?
在输入命令"gcc -g -m32 lab0_ex3.c 2>&1|tee make.log"后出现编译错误,make.log内容如下:
lab0_ex3.c: In function ‘main’:
lab0_ex3.c:48:5: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 2 has type ‘struct gatedesc’ [-Wformat=]
printf("gintr is 0x%llx\n",gintr);
^
copy
由make.log的内容可以看出出错的原因在于gintr的类型为结构体,而需要输出的数据类型为long long unsigned int,因此只需要做改动如下:
printf("gintr is 0x%llx\n",*(long long unsigned*)&(gintr));//改后的代码
//printf("gintr is 0x%llx\n",gintr); //原错误代码
copy
更正后重新编译代码并运行,结果截图如下:
通过阅读代码,可知结构体gatedesc在内存中存储如下:
gatedesc共占8个字节的内存,即64位,因为intr类型是unsigned,因此intr所占内存32位,对应位置应该是getedesc的低32位,即表格的前两行。
若执行如下代码:
unsigned intr;
intr=8;
SETGATE(intr, 0,1,2,3);
copy
通过阅读SETGATE的代码可知执行代码后gd_off_15_0为2,gd_ss=1,因此intr值的二进制表示应为0000 0000 0000 0001 0000 0000 0000 0010。转化为16进制为00010002,转化为10进制表示为65538。
注:若要执行上述代码,需要对SETGATE的定义改动如下:
#define SETGATE(gate, istrap, sel, off, dpl) { \
((gatedesc*)(&gate))->gd_off_15_0 = (uint32_t)(off) & 0xffff; \
((gatedesc*)(&gate))->gd_ss = (sel); \
((gatedesc*)(&gate))->gd_args = 0; \
((gatedesc*)(&gate))->gd_rsv1 = 0; \
((gatedesc*)(&gate))->gd_type = (istrap) ? STS_TG32 : STS_IG32; \
((gatedesc*)(&gate))->gd_s = 0; \
((gatedesc*)(&gate))->gd_dpl = (dpl); \
((gatedesc*)(&gate))->gd_p = 1; \
((gatedesc*)(&gate))->gd_off_31_16 = (uint32_t)(off) >> 16; \
}
copy
用在related_info/lab0/list.h中定义的结构和函数来实现一个小应用程序完成一个基于此链表的数据对象的访问操作。 可参考related_info/lab0/lab0_ex4.c。
基于此链表的数据对象访问操作代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
struct entry {
list_entry_t node;
int num;
};
int main() {
struct entry tail;
list_entry_t* p = &tail.node;
list_init(p);
tail.num = 0;
int i;
for (i = 1; i != 10; i ++) {
struct entry * e = (struct entry *)malloc(sizeof(struct entry));
e->num = i;
list_add(p, &(e->node));
p = list_prev(p);
}
//reverse list all node
while ((p = list_next(p)) != &tail.node)
printf("%d\n", ((struct entry *)p)->num);
return 0;
}
copy
并将list.h做如下修改:
static inline void
list_add(list_entry_t *listelm, list_entry_t *elm) {
list_add_before(listelm, elm);
}
copy
代码运行结果如下:
学习时间 126分钟
操作时间 3分钟
按键次数 11次
实验次数 28次
报告字数 8053字
是否完成 完成