Assembly Can';即使已加载到内存中,也不能从kernel.s打印
我试图通过编写自己的引导加载程序将内核加载到内存中。我已经能够成功地将内核加载到内存中——我知道这是因为我使用了bochs调试器,将断点设置为0x7c00并逐步执行,系统确实跳转到内核中。问题是在跳入内核后,所有的print语句(在kernel.s中)都不能工作。在终端上指示内核已加载到内存中 下面是bootblock.s文件(大部分相关代码位于labelAssembly Can';即使已加载到内存中,也不能从kernel.s打印,assembly,x86,bootloader,att,osdev,Assembly,X86,Bootloader,Att,Osdev,我试图通过编写自己的引导加载程序将内核加载到内存中。我已经能够成功地将内核加载到内存中——我知道这是因为我使用了bochs调试器,将断点设置为0x7c00并逐步执行,系统确实跳转到内核中。问题是在跳入内核后,所有的print语句(在kernel.s中)都不能工作。在终端上指示内核已加载到内存中 下面是bootblock.s文件(大部分相关代码位于labelbooter: # bootblock.s # Empty boot block file # .equ symbol, expressio
booter
:
# bootblock.s
# Empty boot block file
# .equ symbol, expression
# These directive set the value of the symbol to the expression
.equ BOOT_SEGMENT,0x07c0
.equ DISPLAY_SEGMENT,0xb800
.equ KERNEL_LOCATION, 0x1000
.equ STACK_SP, 0xffff
.equ STACK_SS, 0x0
.text # Code segment
.globl _start # The entry point must be global
.code16 # Real mode
_start:
###MAKE BOOTABLE###
#. = _start + 510
#.byte = 0x55
#.byte = 0xaa
jmp booter
os_size:
#Place where createimage writes the OS size
.word 0
.word 0
print:
movw $BOOT_SEGMENT,%ax
movw %ax,%ds
print_loop:
lodsb
cmpb $0,%al
je print_done
movb $14,%ah
movl $0x0002,%ebx
int $0x10
jmp print_loop
print_done:
retw
booter:
###SET UP STACK###
#Allocating the stack
movw $STACK_SS, %ax
movw %ax, %ss
movw $STACK_SP, %sp
movl $allocating, %esi
call print
movl $done, %esi
call print
#Resetting the disk drive, setting %dl and calling int 0x13
#movb $0x0, %ah
#movb $0x0, %dl
#int $0x13
movl $bootblock_test, %esi
call print
movl $hellostring, %esi
call print
###LOAD KERNEL###
movl $loadingkernel, %esi
call print
#Number of sectors to read
#movb $0x24, %al
#movb $0x80, %al
movb $0x08, %al
movb $0x02, %ah
#track number
#movb $0x00, %ch
#which sector to read from (sector 2 where kernel should be)
movb $0x02, %cl
#set up head number
movb $0x0, %dh
#Set the drive number to 0x0 (floppy)
movb $0x0, %dl
#Time to set es:bx to read from the correct place (0:1000)
movw $0x0100, %bx
movw %bx, %es
movw $0x0, %bx
#movw $0x0, %ax
#Setting %ah = 2 and calling int 0x13 (read sector)
int $0x13
movl $done, %esi
call print
#Booting up at 0x07c0
#movw $BOOT_SEGMENT, %ax
#movw %ax, %ds
#movl $bootmessage, %esi
#call print
#%dh/%ch control head numbers, setting them to 0
#movb $0x0, %dh
#movb $0x0, %ch
#movw %ds,
###INVOKE KERNEL###
#Kernel jump
movl $readymessage, %esi
call print
#Setting %ds = 0x7c0
movw $0x0100, %ax
movw %ax, %ds
#Time to set es:bx to read from the correct place (0:1000)
movw $0x0100, %bx
movw %bx, %es
movw $0x0, %bx
movl $0x1000, %ax
jmp %ax
mov $0x0, %ax
#If any errors, message will be displayed here
movl $errormessage, %esi
call print
forever:
jmp forever
#Error handling
error:
movl $errormessage, %esi
call print
# messages
mystring:
.asciz "test.\n\r"
bootblock_test:
.asciz "\nBootblock Test\n\r"
hellostring:
.asciz "How are you today?\n\r"
myname:
.asciz "Welcome\n\r"
loadingkernel:
.asciz "Loading Kernel...\n\r"
done:
.asciz "Done!\n\r"
bootmessage:
.asciz "Booting up...\n\r"
readymessage:
.asciz "Sliding into yo Kernel like... \n\r"
errormessage:
.asciz "Something went terribly wrong...\n\r"
rebootmessage:
.asciz "Press any key to reboot the OS!\n\r"
allocating:
.asciz "Allocating Stack...\n\r"
以下是kernel.s文件:
.data # Data segment
# Some strings
kernel:
.asciz "[Kernel]-> "
testing:
.asciz "Running a trivial test... "
works:
.asciz "Seems Ok. Now go get some sleep :)."
not:
.asciz "*Failed*"
# 'Newline' string ('carriage return', 'linefeed', '\0')
newline:
.byte 10
.byte 13
.byte 0
# An integer
result:
.word 1000
.text # Code segment
.code16 # Real mode
.globl _start # The entry point must be global
#
# The first instruction to execute in a program is called the entry
# point. The linker expects to find the entry point in the "symbol" _start
# (with underscore).
#
_start:
pushw %bp # Setup stack frame
movw %sp,%bp
pushw $newline
call displayString # Print messages
pushw $kernel
call displayString
pushw $testing
call displayString
pushw $1000
call trivialTest # trivialTest(1000)
addw $8,%sp # Pop newline, kernel, testing, and '1000'
cmpw %ax,result
jne .L6 # If (trivialTest(1000) != 1000) goto L6
pushw $works
jmp .L12
.L6: # Test failed
pushw $not
.L12:
call displayString # Print ok/failed message
addw $2,%sp
pushw $newline
call displayString
addw $2,%sp
.L8: # Loop forever
jmp .L8
#
# int trivialTest(n)
# {
# if (n > 0) {
# trivialTest(n-1);
# }
# return n;
# }
trivialTest:
pushw %bp # Setup stack frame
movw %sp,%bp
movw 4(%bp),%ax # Move argument to ax
testw %ax,%ax # Logical compare (sets SF, ZF and PF)
jg .L2 # if (argument > 0) goto L2
xorw %ax,%ax # else return 0
popw %bp
retw
.L2:
decw %ax
pushw %ax
call trivialTest # trivialTest(argument - 1)
# (Recursive calls until argument == 0)
addw $2,%sp # Pop argument
incw %ax
popw %bp
retw # Return (argument in ax)
displayString:
pushw %bp # Setup stack frame
movw %sp,%bp
pushw %ax # Save ax, bx, cx, si, es
pushw %bx
pushw %cx
pushw %si
pushw %es
movw %ds, %ax # Make sure ES points to the right
movw %ax, %es # segment
movw 4(%bp),%cx # Move string adr to cx
movw %cx, %si
loop:
lodsb # Load character to write (c) into al,
# and increment si
cmpb $0, %al
jz done # if (c == '\0') exit loop
movb $14,%ah # else print c
movw $0x0002,%bx
# int 0x10 sends a character to the display
# ah = 0xe (14)
# al = character to write
# bh = active page number (we use 0x00)
# bl = foreground color (we use 0x02)
int $0x10
jmp loop
done:
popw %es # Restore saved registers
popw %si
popw %cx
popw %bx
popw %ax
popw %bp
retw # Return to caller
再一次,我在调试器中检查了内核是否正在加载到内存(0x1000)。我认为问题在于如何设置/使用bootblock.s中的某些寄存器(主要是:
ds
,ax
),但我不能确切地确定它是什么。这些观察结果可能会帮助您:
- 当尝试加载内核时,应该取消设置曲目编号的行的注释
- 在displayString例程中,不需要设置ES寄存器。只需要正确的DS即可
- 您正试图通过一个近跳转!(
)跳转到内核,但该跳转无效。您需要一个段间跳转。请使用jmp%ax
jmp$0x0000:$0x0100
- 您应该避免使用像%esi这样的32位寄存器来传递地址,因为这是16位代码
#Number of sectors to read
movb $0x08, %al
我发现原来的海报正在做一项作业,有人发布了一些关键信息来解决问题。我将其作为参考。一些关键信息是所使用的Makefile类型(略有不同)以及为分配提供的名为createimage
的程序
这一点非常接近:
# Makefile for the OS projects.
# Best viewed with tabs set to 4 spaces.
CC = gcc -Wall -Wextra -std=c99 -g
LD = ld
# Where to locate the kernel in memory
KERNEL_ADDR = 0x1000
# Compiler flags
#-fno-builtin: Don't recognize builtin functions that do not begin with
# '__builtin_' as prefix.
#
#-fomit-frame-pointer: Don't keep the frame pointer in a register for
# functions that don't need one.
#
#-make-program-do-what-i-want-it-to-do:
# Turn on all friendly compiler flags.
#
#-O2: Turn on all optional optimizations except for loop unrolling
# and function inlining.
#
#-c: Compile or assemble the source files, but do not link.
#
#-Wall: All of the `-W' options combined (all warnings on)
CCOPTS = -c -fomit-frame-pointer -O2 -fno-builtin
# Linker flags
#-nostartfiles: Do not use the standard system startup files when linking.
#
#-nostdlib: Don't use the standard system libraries and startup files when
# linking. Only the files you specify will be passed to the linker.
#
#-Ttext X: Use X as the starting address for the text segment of the output
# file.
LDOPTS = -nostartfiles -nostdlib -Ttext
# Makefile targets
all: bootblock createimage kernel image boch_image
kernel: kernel.o
$(LD) $(LDOPTS) $(KERNEL_ADDR) -o kernel $<
bootblock: bootblock.o
$(LD) $(LDOPTS) 0x0 -o bootblock $<
createimage: createimage.o
$(CC) -o createimage $<
# Create an image to put on the floppy
image: bootblock createimage kernel
./createimage ./bootblock ./kernel
# Put the image on the floppy (these two stages are independent, as both
# vmware and bochs can run using only the image file stored on the harddisk)
#boot: image
# cat ./image > /dev/sda
#write image to boch disk image
boch_image: image
dd if=image of=bochs.img conv=notrunc
# Clean up!
clean:
rm -f *.o
rm -f createimage image bootblock kernel
# No, really, clean up!
distclean: clean
rm -f *~
rm -f \#*
rm -f *.bak
rm -f bochsout.txt
# How to compile a C file
%.o:%.c
$(CC) $(CCOPTS) $<
# How to assemble
%.o:%.s
$(CC) $(CCOPTS) $<
# How to produce assembler input from a C file
%.s:%.c
$(CC) $(CCOPTS) -S $<
重要的是,在提取代码和数据段后(将放入带有dd
的磁盘映像中),它会在底部提供类似的输出:
processing ./kernel:
header size is: 52
program header size is: 32
Program header table offset in file is : 52
p_offset: 0x54 p_filesz: 0x10db p_memsz: 0x10db
write image:
9 sectors, 4608 bytes
dd if=image of=bochs.img conv=notrunc
此处的重要信息9个扇区,4608字节
。这表示扇区中的内核有多大。原始海报的代码必须确保在加载内核时读取了那么多扇区。因此,简单的修复方法是将代码更改为:
#Number of sectors to read should match output from createimage
movb $0x09, %al
应该考虑的另一点是,在内核和引导加载程序代码中,他使用的是
lodsb
,而在他的引导加载程序(或内核)中,他没有明确地使用CLD设置方向标志(CLD很可能是他对所编写代码的预期)。方向标志的状态不能被假定为已设置或未设置,因此通常最好明确设置。是的,好的观点。在Linux程序的用户模式下,ABI要求为新启动的程序清除DF(即,在执行后)。情况并非如此。您不能保证方向标志由BIOS设置。在您的代码中,您使用lodsb
,它依赖方向标志来确定是向前还是向后移动。您的lodsb
代码是在假定%di
增加1的情况下编写的,因此(可以在设置堆栈后立即执行)需要放置CLD指令(无操作数)。请阅读有关此指令的更多信息。与此指令相反的是STD,它使lodsb
,movsb
和相关指令在每次之后递减%di
/%si
use@DynamoBoosterDUH,愚蠢的错误对不起。对于GNU汇编程序,它应该是段和偏移量之间的逗号,而不是冒号。所以请尝试jmp$0x0000,$0x1000
@dynamoboster忽略我最后向您提出的建议。我在internet上的其他地方找到了似乎是作业的内容。您的项目中有一个名为createimage的程序,它执行类似的操作。我想您有createimage?除了answ中给出的内容之外er by@user3144770我只能假设在链接器脚本或链接器选项中,您已正确设置引导加载程序和内核的原点。基于代码,引导加载程序的原点将设置为0x0000
,因为您在引导加载程序段区域中显式使用0x07C0
段考虑到您打算跳转到内核的方式(您需要一个远呼叫,正如答案中所提到的)在没有进一步信息的情况下,可能会显示您打算将内核的原点设置为0x1000
。如果链接器命令或链接器脚本未正确设置,则如果使用不可重定位的代码(当前段内的绝对内存寻址),可能会导致潜在问题这是一个小小的挑剔,超出了使用32位寄存器(如%esi)的范围,如
#Number of sectors to read should match output from createimage
movb $0x09, %al