写入 0xB8000 / 损坏的指针时出现奇怪的行为
Weird behavior when writing to 0xB8000 / corrupted pointer
我目前正在从头开发 OS 内核。
我想使用一个函数在屏幕上写入字符,使用 0xB8000 内存位置。
问题如下:
我使用:
void video_write(const unsigned char *string , char color ){
unsigned char *p = (unsigned char*) string ;
char *c = (char*) (VIDEO_MEMORY ); //VIDEO_MEMORY is 0XB8000
while(*p != '[=10=]')
{
*c = p[0] ;
c++ ;
*c = color ;
c++ ;
p++ ;
}
}
void clear_screen(){
char *c = (char*) VIDEO_MEMORY ;
int i = 0 ;
for(i ; i < 4000 ; i++){
*c='[=10=]' ;
c++ ;
}
}
打印到屏幕。
函数调用者:
void main(){
clear_screen() ;
video_write("Message[=11=]" , 0x0E);
}
OS 正确启动,但在输入 32 位 PM 并打印消息后,我得到以下信息:
在字符串的开头还有一个我没有输入的字符。
当我将内存转储到 0xB8000 时,我得到了这个:
00000000: c30e 4d0e 650e 730e 730e 610e 670e 650e ..M.e.s.s.a.g.e.
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
4D 在十六进制中是 "M" 。它应该是字符串中的第一个字符,但它不是,天知道是什么原因。
相反,第一个字符是 C3 ,这是字符串开头的垃圾。
尽管如此,直接从 main() 函数打印这些字符可以完美地工作,所以我猜测字符串的指针已损坏。
你们知道那里发生了什么吗?
编辑:
这是引导加载程序代码:
ifndef BOOT_ASM
%define BOOT_ASM
[org 0x7C00]
KERNEL_OFFSET equ 0x1000
[bits 16]
mov [BOOT_DRIVE] , dl
mov BP , 0x9000
mov SP , BP
call load_kernel
call switch_pm
%include "print.asm"
%include "hexprint.asm"
%include "disk_io.asm"
%include "GDT.asm"
load_kernel :
mov SI , KERNEL_MSG
call print_string
mov BX , KERNEL_OFFSET
mov DH , 15
mov DL , [BOOT_DRIVE]
call disk_load
ret
switch_pm:
cli
lgdt [gdt_descriptor]
mov EAX , CR0
or EAX , 1
mov CR0 , EAX
jmp CODE_SEG:PM_init
[bits 32]
PM_init:
mov AX , DATA_SEG
mov DS , AX
mov SS , AX
mov ES , AX
mov FS , AX
mov GS , AX
mov EBP , 0x90000
mov ESP , EBP
call BEGIN_PM
jmp $
BEGIN_PM :
call KERNEL_OFFSET
ret
BOOT_DRIVE: db 0
RM_MSG db "SAHARA OS , Real mode" , 0x0
PM_MSG db "SAHARA OS , Protected mode" , 0x0
KERNEL_MSG db "SaharaOS : Oasis kernel " , 0x0A , 0x0D , 0x0
times 510 - ($-$$) db 0
dw 0xaa55
%endif
(DATA_SEG 是 GDT 中的,gdt_data - gdt_start)
在 0x1000 我执行了这段代码,这是内核入口
[bits 32]
[extern main]
call main
jmp $
在 clear_screen() 之后转储内存时,我只得到 0 ,所以这是按预期工作的。
我认为它确实是包含此字符的字符串变量,因为当我这样做时:
void video_write(const unsigned char *string , char color ){
unsigned char *p = (unsigned char*) string ;
p++ ;
char *c = (char*) (VIDEO_MEMORY ); //VIDEO_MEMORY is 0XB8000
while(*p != '[=15=]')
{
*c = *p ;
c++ ;
*c = color ;
c++ ;
p++ ;
}
添加 "p++;" ,消息打印时没有伪影。
所以我不认为这是专门放在 0xB8000 中的一些垃圾。
例如,偏移视频内存,使消息打印在屏幕中间,结果相同:
当我将 VIDEO_MEMORY 偏移任何值时:
void video_write(const unsigned char *string , char color ){
unsigned char *p = (unsigned char*) string ;
char *c = (char*) (VIDEO_MEMORY + 1980 ); //VIDEO_MEMORY is 0XB8000
while(*p != '[=16=]')
{
*c = p[0] ;
c++ ;
*c = color ;
c++ ;
p++ ;
}
我明白了:
编辑:好吧,所以我只是想出了问题出在哪里,但不知道为什么。
问题出在编译和链接......
我之前为 64 位 elf 编译,我只是在所有地方都更改为 32 位 compiling/linking,文本打印正确,没有伪影。
我目前正在从头开发 OS 内核。
我想使用一个函数在屏幕上写入字符,使用 0xB8000 内存位置。
问题如下:
我使用:
void video_write(const unsigned char *string , char color ){
unsigned char *p = (unsigned char*) string ;
char *c = (char*) (VIDEO_MEMORY ); //VIDEO_MEMORY is 0XB8000
while(*p != '[=10=]')
{
*c = p[0] ;
c++ ;
*c = color ;
c++ ;
p++ ;
}
}
void clear_screen(){
char *c = (char*) VIDEO_MEMORY ;
int i = 0 ;
for(i ; i < 4000 ; i++){
*c='[=10=]' ;
c++ ;
}
}
打印到屏幕。
函数调用者:
void main(){
clear_screen() ;
video_write("Message[=11=]" , 0x0E);
}
OS 正确启动,但在输入 32 位 PM 并打印消息后,我得到以下信息:
在字符串的开头还有一个我没有输入的字符。
当我将内存转储到 0xB8000 时,我得到了这个:
00000000: c30e 4d0e 650e 730e 730e 610e 670e 650e ..M.e.s.s.a.g.e.
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
4D 在十六进制中是 "M" 。它应该是字符串中的第一个字符,但它不是,天知道是什么原因。 相反,第一个字符是 C3 ,这是字符串开头的垃圾。
尽管如此,直接从 main() 函数打印这些字符可以完美地工作,所以我猜测字符串的指针已损坏。
你们知道那里发生了什么吗?
编辑: 这是引导加载程序代码:
ifndef BOOT_ASM
%define BOOT_ASM
[org 0x7C00]
KERNEL_OFFSET equ 0x1000
[bits 16]
mov [BOOT_DRIVE] , dl
mov BP , 0x9000
mov SP , BP
call load_kernel
call switch_pm
%include "print.asm"
%include "hexprint.asm"
%include "disk_io.asm"
%include "GDT.asm"
load_kernel :
mov SI , KERNEL_MSG
call print_string
mov BX , KERNEL_OFFSET
mov DH , 15
mov DL , [BOOT_DRIVE]
call disk_load
ret
switch_pm:
cli
lgdt [gdt_descriptor]
mov EAX , CR0
or EAX , 1
mov CR0 , EAX
jmp CODE_SEG:PM_init
[bits 32]
PM_init:
mov AX , DATA_SEG
mov DS , AX
mov SS , AX
mov ES , AX
mov FS , AX
mov GS , AX
mov EBP , 0x90000
mov ESP , EBP
call BEGIN_PM
jmp $
BEGIN_PM :
call KERNEL_OFFSET
ret
BOOT_DRIVE: db 0
RM_MSG db "SAHARA OS , Real mode" , 0x0
PM_MSG db "SAHARA OS , Protected mode" , 0x0
KERNEL_MSG db "SaharaOS : Oasis kernel " , 0x0A , 0x0D , 0x0
times 510 - ($-$$) db 0
dw 0xaa55
%endif
(DATA_SEG 是 GDT 中的,gdt_data - gdt_start)
在 0x1000 我执行了这段代码,这是内核入口
[bits 32]
[extern main]
call main
jmp $
在 clear_screen() 之后转储内存时,我只得到 0 ,所以这是按预期工作的。
我认为它确实是包含此字符的字符串变量,因为当我这样做时:
void video_write(const unsigned char *string , char color ){
unsigned char *p = (unsigned char*) string ;
p++ ;
char *c = (char*) (VIDEO_MEMORY ); //VIDEO_MEMORY is 0XB8000
while(*p != '[=15=]')
{
*c = *p ;
c++ ;
*c = color ;
c++ ;
p++ ;
}
添加 "p++;" ,消息打印时没有伪影。 所以我不认为这是专门放在 0xB8000 中的一些垃圾。 例如,偏移视频内存,使消息打印在屏幕中间,结果相同: 当我将 VIDEO_MEMORY 偏移任何值时:
void video_write(const unsigned char *string , char color ){
unsigned char *p = (unsigned char*) string ;
char *c = (char*) (VIDEO_MEMORY + 1980 ); //VIDEO_MEMORY is 0XB8000
while(*p != '[=16=]')
{
*c = p[0] ;
c++ ;
*c = color ;
c++ ;
p++ ;
}
我明白了:
编辑:好吧,所以我只是想出了问题出在哪里,但不知道为什么。 问题出在编译和链接...... 我之前为 64 位 elf 编译,我只是在所有地方都更改为 32 位 compiling/linking,文本打印正确,没有伪影。