安装依赖:
yum -y group install “development tools”
yum -y install ncurses-devel
下载内核源码后解压,下的慢选国内源:
wget https://mirror.tuna.tsinghua.edu.cn/kernel/v2.6/
tar -zxf linux.2.6.32.22.tar.gz
移动到内核源码目录:
mov linux….. /usr/src/kernels/
进入目录编译
cd /usr/src/kernels/
make menuconfig
make -j4 bzImage
make -j4 modules
make -j4 modules_install
make -j4
make -j4 install
最后一步找不到一些模块可以忽略
复制虚拟机一份,一个debug是被调试的,一个server是调试debug的
两个机器关机状态下添加串行端口,使用通道.\pipe\s ,debug是服务端server是客户端,另一头都是虚拟机,主动放弃cpu勾选上
开机,一个执行cat /dev/ttyS1 另一个 echo 123 &>/dev/ttyS1看看有输出没,没有就换,从ttyS0开始往后试,假设是ttyS1
这时候重启debug,选择编译好的内核按e编辑,在quiet后面加 kgdboc=ttyS1,kgdbwait kgdbwait 然后启动,进入kgdbwait界面
server启动gdb:
gdb /usr/src/kernels/linux.2.6.32/vmlinuz
set remotebaud 115200
target remote /dev/ttyS1
这时如果正常就可以进行调试了,如果不正常,需要重新编译内核,修改 /kernel/kgdb.c下面switch …. case ‘f’:下面if里的error….和case ‘T’:下面相同的一行,重新编译后再次进行调试即可
gdb按c执行后,在debug上以root运行
echo g > /proc/sysrq-trigger
即可中断

;启动扇区加载用户程序

app_lba_start equ 100               ;一会会将程序放在硬盘的100扇区

SECTION mbr align=16 vstart=0x7c00      ;mbr段声明,16字节对齐,初始地址7c00

;设置堆栈段和栈指针
mov ax,0
mov ss,ax
mov sp,ax

mov ax,[cs:phy_base]                ;phy_base保存的是一个32位地址,所以需要用两个寄存器存放
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx
mov ds,ax                           ;十六进制左移1位,实际上是除以了16
mov es,ax                           ;将ds和es都设置成计算出的phy_base的段地址

xor di,di
mov si,app_lba_start                ;程序在硬盘上的第一个逻辑扇区号
xor bx,bx                           ;bx清零
call read_hard_disk_0               ;加载程序到DS:0x0000处

;以下判断整个程序有多大
mov dx,[2]                          ;ds:0x0002处保存的值,是用户程序声明的用户程序大小
mov ax,[0]                          ;ds:0x0000处保存的值,这时ds依然是定义的phy_base段地址
mov bx,512                          ;512每个扇区
div bx                              ;看看占了几个扇区
cmp dx,0                            ;除尽了没有
jnz @1                              ;没除尽,跳到@1
dec ax                              ;除尽了,余数ax减一,因为已经预读了一个扇区
@1:
cmp ax,0                            ;有可能长度本来就不够一个扇区,这时ax为0,所以用jz
jz direct                           ;跳到读完操作

;读取剩余扇区
push ds                             ;保存ds,因为程序传参要用ds

mov cx,ax                           ;循环次数(剩余扇区数)
@2:
mov ax,ds
add ax,0x20                         ;如果容纳不下,那就加个0x20的段间距,这样多了0x200=512
mov ds,ax

xor bx,bx
inc si                              ;下一个逻辑扇区
call read_hard_disk_0
loop @2

pop ds                              ;恢复数据段基址ds到用户程序头部段

;计算入口点代码段基址
direct:
mov dx,[0x08]                       ;此时为何是0x06和0x08?因为取的是user的section.code_1.start
mov ax,[0x06]                       ;这是一个双字地址,所以dx存高位ax存低位
call calc_segment_base
mov [0x06],ax                       ;将真正的用户代码入口的基地址写回到用户头中

;开始处理段重定位表
mov cx,[0x0a]                       ;需要重定位的个数,这里用户头部的0x0a是段重定位表项数
mov bx,0x0c                         ;把基地址定在0x0c,这里是段重定位表格的开头地址

realloc:
mov dx,[bx+0x02]                    ;32位地址的高16位
mov ax,[bx]                         ;32位地址的低16位
call calc_segment_base              ;重定位段
mov [bx],ax                         ;写回重定位后的地址
add bx,4                            ;下一个重定位项(每项4字节)
loop realloc

jmp far [0x04]                      ;跳转到用户程序

;---------------------------------------------
read_hard_disk_0:                      ;从硬盘读取一个逻辑扇区

push ax                             ;输入:DI:SI=起始逻辑扇区号
push bx                             ;DS:BX=目标缓冲区地址
push cx
push dx

mov dx,0x1f2                        ;0x1f2是保存要读取的扇区数量的端口
mov al,1
out dx,al

inc dx                              ;0x1f3
mov ax,si                           ;si传参100,0x1f3存储0-7位
out dx,al                           ;表示要读写的扇区

inc dx
mov al,ah                           ;0x1f4
out dx,al                           ;LBA地址 15-8

inc dx                              ;0x1f5
mov ax,di                           ;LBA地址23-16
out dx,al                           ;di传参为0

inc dx                              ;0x1f6
mov al,0xe0                         ;LBA28模式,主盘
or al,ah                            ;LBA地址27-24
out dx,al                           ;这里用or是因为LBA28模式设定是前四位,后四位还是扇区

inc dx                              ;0x1f7
mov al,0x20                         ;读命令
out dx,al

.waits:
in al,dx                            ;0x1f7
and al,0x88                         ;第8位表示硬盘忙,第4位表示已做好准备传输10001000
cmp al,0x08                         ;00001000表示可以传输了
jnz .waits                          ;硬盘是否不繁忙且已经准备好传输数据,否则继续等待

mov cx,256                          ;读256次
mov dx,0x1f0                        ;0x1f0是硬盘接口的数据接口

.readw:
in ax,dx                            ;读数据
mov [bx],ax                         ;将数据写到ds:bx所在的位置
add bx,2                            ;偏移两个读写下一个数据
loop .readw

pop dx
pop cx
pop bx
pop ax

ret

;--------------------------------------------
calc_segment_base:                      ;计算16位段地址
;输入:DX:AX=32位物理地址,dx=0x08 ax=0x06
;返回:ax=16位段基地址
push dx

add ax,[cs:phy_base]                ;取用户程序基址+06,也就是用户程序定义的段地址
adc dx,[cs:phy_base+0x02]           ;adc带进位的加法,基址+08,也就是用户程序定义的,这里取高位
shr ax,4                            ;0x0000,右移四位,ax存储低16位,dx高4位,总共20位地址
ror dx,4                            ;0x0001,循环右移四位 0x1000
and dx,0xf000                       ;低12位我们不需要,and取高四位
or ax,dx                            ;or得到结果0x1000

pop dx                              ;恢复现场,ax为返回值

ret

;----------------------------------------------
phy_base dd 0x10000                 ;用户程序被加载的物理起始地址

times 510-($-$$) db 0
db 0x55,0xaa

用户代码:

;===============================================================================
SECTION header vstart=0                     ;定义用户程序头部段
program_length  dd program_end          ;程序总长度[0x00]

;用户程序入口点
code_entry      dw start                ;偏移地址[0x04]
dd section.code_1.start ;段地址[0x06]

realloc_tbl_len dw (header_end-code_1_segment)/4
;段重定位表项个数[0x0a]

;段重定位表
code_1_segment  dd section.code_1.start ;[0x0c]
code_2_segment  dd section.code_2.start ;[0x10]
data_1_segment  dd section.data_1.start ;[0x14]
data_2_segment  dd section.data_2.start ;[0x18]
stack_segment   dd section.stack.start  ;[0x1c]

header_end:

;===============================================================================
SECTION code_1 align=16 vstart=0         ;定义代码段1(16字节对齐)
put_string:                              ;显示串(0结尾)。
;输入:DS:BX=串地址
mov cl,[bx]
or cl,cl                        ;cl=0 ?
jz .exit                        ;是的,返回主程序
call put_char
inc bx                          ;下一个字符
jmp put_string

.exit:
ret

;-------------------------------------------------------------------------------
put_char:                                ;显示一个字符
;输入:cl=字符ascii
push ax
push bx
push cx
push dx
push ds
push es

;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
in al,dx                        ;高8位
mov ah,al

mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx                        ;低8位
mov bx,ax                       ;BX=代表光标位置的16位数

cmp cl,0x0d                     ;回车符?
jnz .put_0a                     ;不是。看看是不是换行等字符
mov ax,bx                       ;此句略显多余,但去掉后还得改书,麻烦
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor

.put_0a:
cmp cl,0x0a                     ;换行符?
jnz .put_other                  ;不是,那就正常显示字符
add bx,80
jmp .roll_screen

.put_other:                             ;正常显示字符
mov ax,0xb800
mov es,ax
shl bx,1
mov [es:bx],cl

;以下将光标位置推进一个字符
shr bx,1
add bx,1

.roll_screen:
cmp bx,2000                     ;光标超出屏幕?滚屏
jl .set_cursor

mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0
mov di,0x00
mov cx,1920
rep movsw
mov bx,3840                     ;清除屏幕最底一行
mov cx,80
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls

mov bx,1920

.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al

pop es
pop ds
pop dx
pop cx
pop bx
pop ax

ret

;-------------------------------------------------------------------------------
start:
;初始执行时,DS和ES指向用户程序头部段
mov ax,[stack_segment]           ;设置到用户程序自己的堆栈
mov ss,ax
mov sp,stack_end

mov ax,[data_1_segment]          ;设置到用户程序自己的数据段
mov ds,ax

mov bx,msg0
call put_string                  ;显示第一段信息

push word [es:code_2_segment]
mov ax,begin
push ax                          ;可以直接push begin,80386+

retf                             ;转移到代码段2执行

continue:
mov ax,[es:data_2_segment]       ;段寄存器DS切换到数据段2
mov ds,ax

mov bx,msg1
call put_string                  ;显示第二段信息

jmp $

;===============================================================================
SECTION code_2 align=16 vstart=0          ;定义代码段2(16字节对齐)

begin:
push word [es:code_1_segment]
mov ax,continue
push ax                          ;可以直接push continue,80386+

retf                             ;转移到代码段1接着执行

;===============================================================================
SECTION data_1 align=16 vstart=0

msg0 db '  This is NASM - the famous Netwide Assembler. '
db 'Back at SourceForge and in intensive development! '
db 'Get the current versions from http://www.nasm.us/.'
db 0x0d,0x0a,0x0d,0x0a
db '  Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
db '     xor dx,dx',0x0d,0x0a
db '     xor ax,ax',0x0d,0x0a
db '     xor cx,cx',0x0d,0x0a
db '  @@:',0x0d,0x0a
db '     inc cx',0x0d,0x0a
db '     add ax,cx',0x0d,0x0a
db '     adc dx,0',0x0d,0x0a
db '     inc cx',0x0d,0x0a
db '     cmp cx,1000',0x0d,0x0a
db '     jle @@',0x0d,0x0a
db '     ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
db 0

;===============================================================================
SECTION data_2 align=16 vstart=0

msg1 db '  The above contents is written by LeeChung. '
db '2011-05-06'
db 0

;===============================================================================
SECTION stack align=16 vstart=0

resb 256

stack_end:

;===============================================================================
SECTION trail align=16
program_end: