通过以下两条语句,将工作目录转到实验代码存放处,其中gcc -S选项是为了生成汇编文件,-m32选项则是指定编译为32位应用程序,结果如图所示,新生成了一个.s汇编文件
cd related_info/lab0
gcc -S -m32 lab0_ex1.c
copy
.c文件中代码如下,首先定义两个变量,一个数组,然后向buf中写上count个value值。
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
经过编译的文件代码如下,开头部分是对变量的声明
.file "lab0_ex1.c"
.globl count
.data
.align 4
.type count, @object
.size count, 4
count:
.long 1
.globl value
.align 4
.type value, @object
.size value, 4
value:
.long 1
.comm buf,40,32
.text
.globl main
.type main, @function
copy
然后进入main函数部分,首先将要用的寄存器的值先压入堆栈,对数据进行保护,随后将相关的数据,count、value、buf存入寄存器
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %edi
pushl %ebx
.cfi_offset 7, -12
.cfi_offset 3, -16
movl count, %edx
movl value, %eax
movl buf, %ebx
movl %edx, %ecx
movl %ebx, %edi
copy
然后进入操作阶段,首先cld命令对DF寄存器进行复位,决定接下来的操作向着内存增大的方向进行,随后rep表示重复,再结合stosl命令就可以实现向buf中写上count个value值
#APP
# 6 "lab0_ex1.c" 1
cld
rep
stosl
# 0 "" 2
#NO_APP
popl %ebx
.cfi_restore 3
popl %edi
.cfi_restore 7
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
copy
最后是一些关于当前操作系统、使用的gcc编译器的注释说明
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
copy
两者关系为,C语言文件经过编译后生成汇编文件,从而被计算机识别,实现对硬件的直接操控
观察实验2代码,发现是打印hello world的程序
#include <stdio.h>
int
main(void)
{
printf("Hello, world!\n");
return 0;
}
copy
通过一下语句就可以运行,显然程序没有错误
gcc -g -m32 lab0_ex2.c
./a.out
copy
结果如下,然后用gdb来调试
点击运行,结果正常且没有报错,再输入where命令寻找出错的地方,显示没有错误。
然后在程序里面加上一些语句,使得其能够通过编译,但是运行会出错,下图中会出现分母为0的错误
经过编译后,进入gbd调试模式,输入run的时候,hello world可以正常输出,但是后面报错,提示算术表达式错误,用where语句查找出错的地方,发现是第七行
随后用break 7语句在第七行设置断点,则下一次运行run的时候,遇到断点就会暂停,然后用next语句单条执行,随后就报出算术表达式错误
输入如下指令进行编译,代码中的 “2>&1”要拆开来看,对于& 1 更准确的说应该是文件描述符 1,而1标识标准输出,stdout。对于2 ,表示标准错误,stderr。所以2>&1 的意思就是将标准错误重定向到标准输出。tee命令用于读取标准输入的数据,并将其内容输出成文件,将错误日志保存到make.log方便后期查看
cd related_info/lab0
gcc -g -m32 lab0_ex3.c 2>&1|tee make.log
copy
打开make.log,内容如下发现是48行处,printf语句内部出现了问题
lab0_ex3.c: In function \u2018main\u2019:
lab0_ex3.c:48:5: warning: format \u2018%llx\u2019 expects argument of type \u2018long long unsigned int\u2019, but argument 2 has type \u2018struct gatedesc\u2019 [-Wformat=]
printf("gintr is 0x%llx\n",gintr);
^
copy
打开具体代码查看,首先定义了一个关于结构体gatedesc的操作,输入一个结构体变量,三个常数,就可以改变结构体内相关参数的值,随后是结构体的具体定义,可以看出这个结构体长度为64位
#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
随后进入主函数部分,第一行就是对无符号数intr取地址,再强制转换指针类型,再取出这个指针所指的变量,c语言编译器就是通过指针类型来弄清楚所指的这个变量到底长度多少个字节。随后用SETGATE改变结构体内部参数的值
gintr=*((struct gatedesc *)&intr);
SETGATE(gintr, 0,1,2,3);
intr=*(unsigned *)&(gintr);
printf("intr is 0x%x\n",intr);
printf("gintr is 0x%llx\n",gintr);
copy
由于printf内的参数%llx功能是输出有符号64位16进制整数,所以对printf后面的gintr参数进行强制类型转换,仿照intr的转换语句,使得printf后面参数变为long long unsigned类型,加在其下一行,再次编译发现没有错误了,随后直接./a.out查看结果
printf("gintr is 0x%llx\n",*((long long unsigned *) & gintr));
copy
实验时,这里犯了一个错误,一开始将代码修改为,由于gintr本身就是一个结构体,long long unsigned整数显然是不能赋值给结构体的
gintr=*((long long unsigned *) & gintr)
copy
首先查看一下ex4.c代码,首先定义了链表节点的结构体,包括一个list_entry对象以及一个数值num,主函数中,首先定义链表的头结点,经过初始化后,头结点的前驱与后继均为头结点自身,其数值为0.随后9次循环,不断调用list_add函数,逐步建立起链表。
struct entry {
list_entry_t node;
int num;
};
int main() {
struct entry head;
list_entry_t* p = &head.node;
list_init(p);
head.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_next(p);
}
//reverse list all node
while ((p = list_prev(p)) != &head.node)
printf("%d\n", ((struct entry *)p)->num);
return 0;
}
copy
在list.h中,有list_entry双向链表数据结构的具体定义,结构体内有一个前驱指针和一个后继指针,随后是所有操作函数的说明,这里就不附上具体的代码了,在.c文件中可以直接使用
struct list_entry {
struct list_entry *prev, *next;
};
typedef struct list_entry list_entry_t;
/* *
* list_init - initialize a new entry
* @elm: new entry to be initialized
* */
static inline void
list_init(list_entry_t *elm) {
elm->prev = elm->next = elm;
}
copy
为了实现基于此链表的数据对象的访问操作,可以在ex4.cd的基础上增加相关的查找、插入、删除、修改子函数
其中查找函数如下,加入了对索引范围的判断,打印该节点的数值,然后直接返回该节点,由于我们是对list_entry这个类进行操作,而它又是entry类的一个组成部分,所以在printf语句中要将指针类型进行强制转换,才能访问entry类中的num数据。
static inline list_entry_t * list_search(list_entry_t * head)
{
int index,i;
int length=10;
printf("input the node's index:");
scanf("%d",&index);
while(index<=0 || index>length)
{
printf("index error,input the node's index:");
scanf("%d",&index);
}
for (i=0;i<index;i++)
{
head = list_next(head);
}
printf("%d\n", ((struct entry *)head)->num); //仿照原本主程序中的输出函数
return head;
}
copy
注意自己编写的子函数返回值类型要写成static inline list_entry_t *,而不能简单地写成struct list_entry_t *
/* *
* list_next - get the next entry
* @listelm: the list head
**/
static inline list_entry_t *
list_next(list_entry_t *listelm) {
return listelm->next;
}
copy
通过测试,发现运行成功
修改函数以及测试的结果如下
void list_change(list_entry_t * head)
{
list_entry_t *p;
p=list_search(head);
printf("input num:");
scanf("%d",&((struct entry *)p)->num);
}
copy
插入函数以及测试结果
void list_insert(list_entry_t * head)
{
list_entry_t *p;
p=list_search(head);
struct entry * e = (struct entry *)malloc(sizeof(struct entry));
printf("input num:");
scanf("%d",&e->num);
list_add_after(p,&(e->node));
}
copy
附上头文件中用到的具体函数的源代码
/* *
* list_add_after - add a new entry
* @listelm: list head to add after
* @elm: new entry to be added
*
* Insert the new element @elm *after* the element @listelm which
* is already in the list.
* */
static inline void
list_add_after(list_entry_t *listelm, list_entry_t *elm) {
__list_add(elm, listelm, listelm->next);
}
copy
/* *
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
* */
static inline void
__list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) {
prev->next = next->prev = elm;
elm->next = next;
elm->prev = prev;
}
copy
删除函数,以及头文件中用到的具体函数的源代码
void list_delete(list_entry_t * head)
{
list_entry_t *p;
p=list_search(head);
list_del(p);
}
copy
/* *
* list_del - deletes entry from list
* @listelm: the element to delete from the list
*
* Note: list_empty() on @listelm does not return true after this, the entry is
* in an undefined state.
* */
static inline void
list_del(list_entry_t *listelm) {
__list_del(listelm->prev, listelm->next);
}
static inline void
__list_del(list_entry_t *prev, list_entry_t *next) {
prev->next = next;
next->prev = prev;
}
copy
学习时间 451分钟
操作时间 62分钟
按键次数 667次
实验次数 25次
报告字数 9236字
是否完成 完成