日期:2019/3/31
内容:UCore-Lab0
一、UCore实验
实验 |
说明 |
关键词 |
Lab1 |
bootloader的实现 |
中断 |
Lab2 |
物理内存管理 |
x86分段/分页模式 |
Lab3 |
虚拟内存管理 |
缺页故障、页表替换 |
Lab4 |
内核线程管理 |
内核态线程 |
Lab5 |
用户进程管理 |
用户态进程创建、执行、切换和结束 |
Lab6 |
进度调度管理 |
CPU管理 |
Lab7 |
同步互斥 |
进程通信、死锁 |
Lab8 |
文件系统 |
IO访问、VFS |
二、AT&T语法
AT&T: asmop src, dst
Intel: asmop dst, src
AT&T |
Intel |
|
寄存器 |
%eax |
eax |
操作数顺序 |
movl %ebx, %eax |
mov eax, ebx |
立即数 |
movl $_val, %eax |
mov eax, _val |
地址 |
movl $0xd00d, %eax |
mov eax, 0xd00d |
操作数长度标识 |
movw %ax, %bx |
mov bx,ax |
寻址方式 |
imm32(base, index, scale) |
[base + index * scale + imm32] |
寻址方式对比。
AT&T |
Intel |
|
直接寻址 |
foo |
[foo] |
寄存器间接寻址 |
(%eax) |
[eax] |
变址寻址 |
_var(%eax) |
[eax + _var] |
_var(, %eax, 4) |
[eax * 4 + _var] |
|
_var(%ebx, %eax, 8) |
[ebx + eax * 4 + _var] |
三、gcc内联汇编
分两类:基本内联汇编和扩展内联汇编。
- 基本内联
单行格式:asm("inc %eax");
多行格式:
- 扩展内联
- 基本格式
__asm__ [volatile] ("asm statements" : outputs : inputs : registers-modified); |
注释:
·volatile可选,加上表示对asm编译不做处理。
·样板操作数:如下例子的%0, %1等。指令部中使用了几个样板操作数,就表明有几个变量需要与寄存器相结合,这样GCC和GAS在编译和汇编时会根据后面给定的约束条件进行恰当的处理。
·寄存器表示:%%eax。
·asm statements:写汇编指令段。
·outputs(对应dst操作数):规定输出变量如何与样板操作数进行结合的条件,每个条件称为一个"约束",必要时可以包含多个约束,相互之间用逗号分隔开。每个输出约束都以''=''号开始,然后紧跟一个对操作数类型进行说明的字后,最后是如何与变量相结合的约束。"=r"表示相应的目标操作数(指令部分的%0)可以使用任何一个通用寄存器,并且变量 __dummy存放在这个寄存器中。
·inputs(对应src操作数):输入约束的格式和输出约束相似,但不带''=''号。如果一个输入约束要求使用寄存器,则GCC在预处理时就会为之分配一个寄存器,并插入必要的指令将操作数装入该寄存器。与输入部中说明的操作数结合的寄存器或操作数本身,在执行完嵌入的汇编代码后也不保留执行之前的内容。
·registers-modified:这部分常常以"memory"为约束条件,以表示操作完成后内 存中的内容已有改变,如果原来某个寄存器的内容来自内存,那么现在内存中这个单元的内 容已经改变。乱码列表通知编译器,有些寄存器或内存因内联汇编块造成乱码,可隐式地破坏了条件寄存器的某些位(字段)。
- 例子1
int main() { int a = 10, b = 0; __asm__ __volatile__ ( "movl %1, %%eax;\n\r" "movl %%eax, %0;" :"=r"(b) :"r"(a) :"%eax" ); printf("Result: %d, %d\n", a, b); } |
·函数功能:把a赋值给b ·变量b是输出操作数,通过%0来引用,而变量a是输入操作数,通过%1来引用。 ·最后一行"%eax"告诉GCC它将改变寄存器eax中的值,GCC在处理时不应使用该寄存器来存储任何其它的值。 ·由于变量b被指定成输出操作数,当内联汇编语句执行完毕后,它所保存的值将被更新。 |
- 例子2
#include <stdio.h> int count = 10; int val = 1; int buf[10]; void main() { asm ( "cld " "rep " "stosl" : :"c"(count), "a"(val), "D"(buf) ); int i = 0; for(i = 0; i < 10; i++) printf("%d ", buf[i]); } |
·函数功能:向buf写入count个val ·stosl:将EAX中的值保存到ES:EDI指向的地址 ·cld:edi+=4 ·inputs:count放入ecx,val放入eax,buf放入edi |
- 约束符
符号 |
含义 |
符号 |
含义 |
m v o |
内存单元 |
i h |
直接操作数 |
r |
任意reg |
E F |
浮点数 |
q |
eax/ebx/ecx/edx之一 |
g |
ea/b/c/dx或内存 |
a b c d |
分别表示eax,ebx,ecx,edx |
S D |
esi, edi |
I |
常数0-31 |
四、gdb
在可以使用gdb调试程序之前,必须使用 -g或-ggdb编译选项编译源文件。运行gdb调试 程序时通常使用如下的命令:
gdb progname |
-
命令分类
aliases:命令别名
breakpoints:断点定义;
data:数据查看;
files:指定并查看文件;
internals:维护命令;
running:程序执行;
stack:调用栈查看;
status:状态查看;
tracepoints:跟踪程序执行。
- 使用详细列表
break |
FILENAME:NUM 在特定源文件特定行上设置断点 |
clear |
FILENAME:NUM 删除设置在特定源文件特定行上的断点 |
run |
运行调试程序 |
step |
单步执行调试程序,不会直接执行函数 |
next |
单步执行调试程序,会直接执行函数 |
backtrace |
显示所有的调用栈帧。该命令可用来显示函数的调用顺序 |
where continue |
继续执行正在调试的程序 |
display EXPR |
每次程序停止后显示表达式的值,表达式由程序定义的变量组成 |
file FILENAME |
装载指定的可执行文件进行调试 |
help CMDNAME |
显示指定调试命令的帮助信息 |
info break |
显示当前断点列表,包括到达断点处的次数等 |
info files |
显示被调试文件的详细信息 |
info func |
显示被调试程序的所有函数名称 |
info prog |
显示被调试程序的执行状态 |
info local |
显示被调试程序当前函数中的局部变量信息 |
info var |
显示被调试程序的所有全局和静态变量名称 |
kill |
终止正在被调试的程序 |
list |
显示被调试程序的源代码 |
quit |
退出 gdb |
- 使用实例
static char buff [256]; static char* string; int main () { printf ("Please input a string: "); gets (string); printf (" Your string is: %s ", string); } |
命令行 gcc -o bug -g bug.c gdb bug |
-
tui模式
进入gdb后,<Ctrl+X>,A,可显示代码窗口。
info win |
显示窗口的大小 |
layout next |
切换到下一个布局模式 |
layout prev |
切换到上一个布局模式 |
layout src |
只显示源代码 |
layout asm |
只显示汇编代码 |
layout split |
显示源代码和汇编代码 |
layout regs |
增加寄存器内容显示 |
focus cmd/src/asm/regs/next/prev |
切换当前窗口 |
refresh |
刷新所有窗口 |
tui reg next |
显示下一组寄存器 t |
ui reg system |
显示系统寄存器 |
update |
更新源代码窗口和当前执行点 |
winheight name +/- line |
调整name窗口的高度 |
tabset nchar |
设置tab为nchar个字符 |
五、qemu
参考文献
[1] https://www.cnblogs.com/feng-qi/articles/3055761.html
[2] http://blog.sina.com.cn/s/blog_51e9c0ab010099ow.html