我正在使用汇编学习x86实模式下的中断处理。我下面的例子取自here:
.include "common.h"
BEGIN
CLEAR
/* Set address of the handler for interrupt 0. */
movw $handler, 0x00
/* Set code segment of the handler for interrupt 0. */
mov %cs, 0x02
int $0
PUTC $'b
hlt
handler:
PUTC $'a
iret
但是当我编译并运行以上代码时,
$ as --32 -o main.o main.S -g
$ ld -T linker.ld -o main.img --oformat binary -m elf_i386 -nostdlib main.o
$ qemu-system-i386 -hda main.img
我收到以下错误:
qemu-system-i386: Trying to execute code outside RAM or ROM at 0xf00fff53
This usually means one of the following happened:
(1) You told QEMU to execute a kernel for the wrong machine type, and it crashed on startup (eg trying to run a raspberry pi kernel on a versatilepb QEMU machine)
(2) You didn't give QEMU a kernel or BIOS filename at all, and QEMU executed a ROM full of no-op instructions until it fell off the end
(3) Your guest kernel has a bug and crashed by jumping off into nowhere
This is almost always one of the first two, so check your command line and that you are using the right type of kernel for this machine.
If you think option (3) is likely then you can try debugging your guest with the -d debug options; in particular -d guest_errors will cause the log to include a dump of the guest register state at this point.
Execution cannot continue; stopping here.
我在这里想念什么?为什么需要mov %cs, 0x02
或它实际上在做什么?
[我尝试在gdb下调试此命令,当我逐行执行时,我没有在gdb下遇到此错误,该错误很奇怪并且仍在检查中。
编辑
这是BEGIN
的定义方式:
.macro BEGIN
.local after_locals
.code16
cli
/* Set %cs to 0. */
ljmp $0, $1f
1:
xor %ax, %ax
/* We must zero %ds for any data access. */
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %bp
/* Automatically disables interrupts until the end of the next instruction. */
mov %ax, %ss
/* We should set SP because BIOS calls may depend on that. TODO confirm. */
mov %bp, %sp
/* Store the initial dl to load stage 2 later on. */
mov %dl, initial_dl
jmp after_locals
initial_dl: .byte 0
after_locals:
.endm
我只能假定您已将错误引入了本教程最初提供的代码中。例如,您说自己组装:
as --32 -o main.o main.S -g
如果您包含本教程中出现的common.h
,此命令将失败,并显示类似以下内容:
main.S: Assembler messages: main.S:4: Error: no such instruction: `begin' main.S:5: Error: no such instruction: `clear' main.S:11: Error: no such instruction: `putc $98' main.S:14: Error: no such instruction: `putc $97'
这些错误的发生是因为编写教程代码的方式要求C预处理程序在汇编代码上运行。最简单的方法是使用GCC通过将代码传递给后端AS汇编器来汇编代码:
gcc -c -g -m32 -o main.o main.S
GCC将使用带有.S
扩展名的任何文件扩展名,并在通过AS汇编器之前在.S
上运行C预处理程序。您也可以直接使用cpp
运行CPP预处理器,然后分别运行as
。
要使用GCC构建main.img
,请使用以下命令:
gcc -c -g -m32 -o main.o main.S
ld -T linker.ld -o main.img --oformat binary -m elf_i386 -nostdlib main.o
要使用C预处理器进行构建,请执行以下操作:
cpp main.S > main.s
as -g --32 -o main.o main.s
ld -T linker.ld -o main.img --oformat binary -m elf_i386 -nostdlib main.o
代码与QEMU一起运行时使用预期的代码:
qemu-system-i386 -hda main.img
输出应该类似于:
您的问题缺少一个最小的,完整的可验证示例。我修改了common.h
,使其仅包含您编写的代码所需的宏,并使其他所有内容相同:
linker.ld:
SECTIONS
{
/* We could also pass the -Ttext 0x7C00 to as instead of doing this.
* If your program does not have any memory accesses, you can omit this.
*/
. = 0x7c00;
.text :
{
__start = .;
/* We are going to stuff everything
* into a text segment for now, including data.
* Who cares? Other segments only exist to appease C compilers.
*/
*(.text)
/* Magic bytes. 0x1FE == 510.
*
* We could add this on each Gas file separately with `.word`,
* but this is the perfect place to DRY that out.
*/
. = 0x1FE;
SHORT(0xAA55)
/* This is only needed if we are going to use a 2 stage boot process,
* e.g. by reading more disk than the default 512 bytes with BIOS `int 0x13`.
*/
*(.stage2)
/* Number of sectors in stage 2. Used by the `int 13` to load it from disk.
*
* The value gets put into memory as the very last thing
* in the `.stage` section if it exists.
*
* We must put it *before* the final `. = ALIGN(512)`,
* or else it would fall out of the loaded memory.
*
* This must be absolute, or else it would get converted
* to the actual address relative to this section (7c00 + ...)
* and linking would fail with "Relocation truncated to fit"
* because we are trying to put that into al for the int 13.
*/
__stage2_nsectors = ABSOLUTE((. - __start) / 512);
/* Ensure that the generated image is a multiple of 512 bytes long. */
. = ALIGN(512);
__end = .;
__end_align_4k = ALIGN(4k);
}
}
common.h:
/* I really want this for the local labels.
*
* The major downside is that every register passed as argument requires `<>`:
* http://stackoverflow.com/questions/19776992/gas-altmacro-macro-with-a-percent-sign-in-a-default-parameter-fails-with-oper/
*/
.altmacro
/* Helpers */
/* Push registers ax, bx, cx and dx. Lightweight `pusha`. */
.macro PUSH_ADX
push %ax
push %bx
push %cx
push %dx
.endm
/* Pop registers dx, cx, bx, ax. Inverse order from PUSH_ADX,
* so this cancels that one.
*/
.macro POP_DAX
pop %dx
pop %cx
pop %bx
pop %ax
.endm
/* Structural. */
/* Setup a sane initial state.
*
* Should be the first thing in every file.
*
* Discussion of what is needed exactly: http://stackoverflow.com/a/32509555/895245
*/
.macro BEGIN
LOCAL after_locals
.code16
cli
/* Set %cs to 0. TODO Is that really needed? */
ljmp $0, $1f
1:
xor %ax, %ax
/* We must zero %ds for any data access. */
mov %ax, %ds
/* TODO is it really need to clear all those segment registers, e.g. for BIOS calls? */
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
/* TODO What to move into BP and SP?
* http://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process
*/
mov %ax, %bp
/* Automatically disables interrupts until the end of the next instruction. */
mov %ax, %ss
/* We should set SP because BIOS calls may depend on that. TODO confirm. */
mov %bp, %sp
/* Store the initial dl to load stage 2 later on. */
mov %dl, initial_dl
jmp after_locals
initial_dl: .byte 0
after_locals:
.endm
/* BIOS */
.macro CURSOR_POSITION x=$0, y=$0
PUSH_ADX
mov $0x02, %ah
mov $0x00, %bh
mov \x, %dh
mov \y, %dl
int $0x10
POP_DAX
.endm
/* Clear the screen, move to position 0, 0. */
.macro CLEAR
PUSH_ADX
mov $0x0600, %ax
mov $0x7, %bh
mov $0x0, %cx
mov $0x184f, %dx
int $0x10
CURSOR_POSITION
POP_DAX
.endm
/* Print a 8 bit ASCII value at current cursor position.
*
* * `c`: r/m/imm8 ASCII value to be printed.
*
* Usage:
*
* ....
* PUTC $'a
* ....
*
* prints `a` to the screen.
*/
.macro PUTC c=$0x20
push %ax
mov \c, %al
mov $0x0E, %ah
int $0x10
pop %ax
.endm
main.S:
#include "common.h"
BEGIN
CLEAR
/* Set address of the handler for interrupt 0. */
movw $handler, 0x00
/* Set code segment of the handler for interrupt 0. */
mov %cs, 0x02
int $0
PUTC $'b
hlt
handler:
PUTC $'a
iret