在组装/编译/链接时构建静态IDT和GDT所需的解决方案 [英] Solution needed for building a static IDT and GDT at assemble/compile/link time

查看:144
本文介绍了在组装/编译/链接时构建静态IDT和GDT所需的解决方案的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题的灵感源于多年来许多人遇到的问题,尤其是在x86操作系统开发中.最近,一个相关的NASM问题被一项修改所破坏.在那种情况下,该人正在使用NASM并遇到汇编时间错误:

This question is inspired by a problem many have encountered over the years, especially in x86 operating system development. Recently a related NASM question was bumped up by an edit. In that case the person was using NASM and was getting the assemble time error:

shift运算符只能应用于标量值

shift operator may only be applied to scalar values

另一个相关问题询问在编译时生成静态IDT时导致错误的GCC代码问题:

Another related question asks about a problem with GCC code when generating a static IDT at compile time that resulted in the error:

初始化器元素不是常量

initializer element is not constant

在两种情况下,此问题均与以下事实有关:IDT条目需要一个指向异常处理程序的地址,而GDT可能需要到另一个结构(如任务段结构(TSS))的基地址.通常这不是问题,因为链接过程可以通过重定位修复程序来解析这些地址.对于 IDT条目此答案中对此进行了很好的解释.

In both cases the issue is related to the fact that an IDT entry requires an address to an exception handler and a GDT may need a base address to another structure like a Task Segment Structure (TSS). Normally this isn't an issue because the linking process can resolve these addresses through relocation fixups. In the case of an IDT entry or GDT Entry, the fields split up the base/function addresses. There are no relocation types that can tell a linker to shift bits around and then place them in memory the way they are laid out in a GDT/IDT entry. Peter Cordes has written a good explanation of that in this answer.

我的问题不是在问问题是什么,而是对问题的功能实际解决方案的要求.尽管我对此做出了自我回答,但这只是许多可能的解决方案之一.我只要求提出的解决方案满足以下要求:

My question is not asking what the issue is, but a request for functional, and practical solutions to the problem. Although I am self-answering this, it is only one of many possible solutions. I only ask that solutions proposed meet these requirements:

  • GDT和IDT的地址不应固定为特定的物理或线性地址.
  • 该解决方案至少应能够使用ELF对象和ELF可执行文件.如果它适用于其他格式,那就更好了!
  • 解决方案是否是构建最终可执行文件/二进制文件的过程的一部分都没有关系.如果解决方案需要在生成可执行文件/二进制文件后进行构建时间处理,那么这也是可以接受的.
  • GDT(或IDT)在加载到内存中时需要显示为已完全解析.解决方案必须不需要运行时修复程序.

我以传统引导加载程序 1 的形式提供了一些示例代码,该示例试图在组装时创建静态IDT和GDT,但在与nasm -f elf32 -o boot.o boot.asm组装时会出现这些错误:

I'm providing some sample code in the form of a legacy bootloader1 that tries to create a static IDT and GDT at assembly time but fails with these errors when assembled with nasm -f elf32 -o boot.o boot.asm:

boot.asm:78: error: `&' operator may only be applied to scalar values
boot.asm:78: error: `&' operator may only be applied to scalar values
boot.asm:79: error: `&' operator may only be applied to scalar values
boot.asm:79: error: `&' operator may only be applied to scalar values
boot.asm:80: error: `&' operator may only be applied to scalar values
boot.asm:80: error: `&' operator may only be applied to scalar values
boot.asm:81: error: `&' operator may only be applied to scalar values
boot.asm:81: error: `&' operator may only be applied to scalar values

代码是:

macros.inc

; Macro to build a GDT descriptor entry
%define MAKE_GDT_DESC(base, limit, access, flags) \
    (((base & 0x00FFFFFF) << 16) | \
    ((base & 0xFF000000) << 32) | \
    (limit & 0x0000FFFF) | \
    ((limit & 0x000F0000) << 32) | \
    ((access & 0xFF) << 40) | \
    ((flags & 0x0F) << 52))

; Macro to build a IDT descriptor entry
%define MAKE_IDT_DESC(offset, selector, access) \
    ((offset & 0x0000FFFF) | \
    ((offset & 0xFFFF0000) << 32) | \
    ((selector & 0x0000FFFF) << 16) | \
    ((access & 0xFF) << 40))

boot.asm :

%include "macros.inc"

PM_MODE_STACK EQU 0x10000

global _start

bits 16

_start:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, ax                  ; Stack grows down from physical address 0x00010000
                                ; SS:SP = 0x0000:0x0000 wraps to top of 64KiB segment
    cli
    cld
    lgdt [gdtr]                 ; Load our GDT
    mov eax, cr0
    or eax, 1
    mov cr0, eax                ; Set protected mode flag
    jmp CODE32_SEL:start32      ; FAR JMP to set CS

bits 32
start32:
    mov ax, DATA32_SEL          ; Setup the segment registers with data selector
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov esp, PM_MODE_STACK      ; Set protected mode stack pointer

    mov fs, ax                  ; Not currently using FS and GS
    mov gs, ax

    lidt [idtr]                 ; Load our IDT

    ; Test the first 4 exception handlers
    int 0
    int 1
    int 2
    int 3

.loop:
    hlt
    jmp .loop

exc0:
    iret
exc1:
    iret
exc2:
    iret
exc3:
    iret

align 4
gdt:
    dq MAKE_GDT_DESC(0, 0, 0, 0)   ; null descriptor
.code32:
    dq MAKE_GDT_DESC(0, 0x000fffff, 10011010b, 1100b)
                                ; 32-bit code, 4kb gran, limit 0xffffffff bytes, base=0
.data32:
    dq MAKE_GDT_DESC(0, 0x000fffff, 10010010b, 1100b)
                                ; 32-bit data, 4kb gran, limit 0xffffffff bytes, base=0
.end:

CODE32_SEL equ gdt.code32 - gdt
DATA32_SEL equ gdt.data32 - gdt

align 4
gdtr:
    dw gdt.end - gdt - 1        ; limit (Size of GDT - 1)
    dd gdt                      ; base of GDT

align 4
; Create an IDT which handles the first 4 exceptions
idt:
    dq MAKE_IDT_DESC(exc0, CODE32_SEL, 10001110b)
    dq MAKE_IDT_DESC(exc1, CODE32_SEL, 10001110b)
    dq MAKE_IDT_DESC(exc2, CODE32_SEL, 10001110b)
    dq MAKE_IDT_DESC(exc3, CODE32_SEL, 10001110b)
.end:

align 4
idtr:
    dw idt.end - idt - 1        ; limit (Size of IDT - 1)
    dd idt                      ; base of IDT


脚注

  • 1 我选择一个引导加载程序作为示例,因为最小完整可验证示例更易于生成.尽管代码位于引导加载程序中,但是类似的代码通常是作为内核或其他非引导加载程序代码的一部分编写的.该代码通常可以用汇编语言以外的其他语言编写,例如C/C ++等.


    Footnotes

    • 1I chose a bootloader as an example since a Minimal Complete Verifiable Example was easier to produce. Although the code is in a bootloader, similar code is usually written as part of a kernel or other non-bootloader code. The code may often be written in languages other than assembly, like C/C++ etc.

      由于BIOS始终将旧版引导加载程序加载到物理地址0x7c00,因此对于这种情况,还有其他一些特定的解决方案可以在组装时完成.这种特定的解决方案打破了OS开发中更普遍的用例,在这种情况下,开发人员通常不希望将IDT或GDT地址硬编码为特定的线性/物理地址,因为最好让链接器为它们做. p>

    • Because a legacy bootloader is always loaded by the BIOS at physical address 0x7c00, there are other specific solutions for this case that can be done at assembly time. Such specific solutions break the more general use cases in OS development where a developer usually doesn't want to hard code the IDT or GDT addresses to specific linear/physical addresses, as it is preferable to let the linker do that for them.

      推荐答案

      我最常用的一种解决方案是实际使用GNU链接器(ld)为我构建IDT和GDT.这个答案不是编写GNU链接程序脚本的入门,但是它确实使用了BYTESHORTLONG链接程序脚本指令来构建IDT,GDT,IDT记录和GDT.记录.链接器可以使用包含<<>>&|等的表达式,并在最终解析的符号的虚拟内存地址(VMA)上执行这些操作.

      One solution that I most commonly use is to actually use the GNU linker (ld) to build the IDT and GDT for me. This answer isn't a primer on writing GNU linker scripts, but it does make use of the BYTE, SHORT, and LONG linker script directives to build the IDT, the GDT, the IDT record, and the GDT record. The linker can use expressions involving <<, >>, &, | etc, and do these on the virtual memory addresses (VMA) of symbols it ultimately resolves.

      问题在于链接描述文件很笨.它们没有宏语言,因此您最终不得不编写如下的IDT和GDT条目:

      The problem is that the linker scripts are rather dumb. They don't have a macro language so you'd end up having to write the IDT and GDT entries like this:

      . = ALIGN(4);
      gdt = .;
      NULL_SEL = ABSOLUTE(. - gdt);
      SHORT(0);
      SHORT(0);
      BYTE(0 >> 16);
      BYTE(0);
      BYTE((0 >> 16 & 0x0f) | (0 << 4)); BYTE(0 >> 24);
      
      CODE32_SEL = ABSOLUTE(. - gdt);
      SHORT(0x000fffff);
      SHORT(0);
      BYTE(0 >> 16);
      BYTE(10011010b);
      BYTE((0x000fffff >> 16 & 0x0f) | (1100b << 4));
      BYTE(0 >> 24);
      
      DATA32_SEL = ABSOLUTE(. - gdt);
      SHORT(0x000fffff);
      SHORT(0);
      BYTE(0 >> 16);
      BYTE(10010010b);
      BYTE((0x000fffff >> 16 & 0x0f) | (1100b << 4));
      BYTE(0 >> 24);
      gdt_size = ABSOLUTE(. - gdt);
      
      . = ALIGN(4);
      idt = .;
      SHORT(exc0 & 0x0000ffff);
      SHORT(CODE32_SEL);
      BYTE(0x00);
      BYTE(10001110b);
      SHORT(exc0 >> 16);
      SHORT(exc1 & 0x0000ffff);
      SHORT(CODE32_SEL);
      BYTE(0x00);
      BYTE(10001110b);
      SHORT(exc1 >> 16);
      SHORT(exc2 & 0x0000ffff);
      SHORT(CODE32_SEL);
      BYTE(0x00);
      BYTE(10001110b);
      SHORT(exc2 >> 16);
      SHORT(exc3 & 0x0000ffff);
      SHORT(CODE32_SEL);
      BYTE(0x00);
      BYTE(10001110b);
      SHORT(exc3 >> 16);
      idt_size = ABSOLUTE(. - idt);
      

      exc0exc1exc2exc3是从对象文件定义和导出的异常函数.您可以看到IDT条目在代码段中使用CODE32_SEL.建立GDT时,告诉链接器计算选择器编号.显然,这非常混乱,并且随着GDT(尤其是IDT)的增长变得更加笨拙.

      exc0, exc1, exc2, and exc3 are the exception functions defined and exported from an object file. You can see the IDT entries are using CODE32_SEL for the code segment. The linker is told to compute the selector numbers when building the GDT. Obviously this is very messy and becomes more unwieldy as the GDT and most especially the IDT grow.

      您可以使用诸如 C预处理器(cpp),因为它为更多开发人员所熟悉.尽管 C 预处理器通常用于预处理C/C ++文件,但它不仅限于这些文件.您可以在任何类型的文本文件(包括链接器脚本)上使用它.

      You could use a macro processor like m4 to simplify things, but I prefer to use the C preprocessor (cpp) as it is familiar to a lot more developers. Although the C pre-processor is usually used to pre-process C/C++ files, it isn't limited to those files. You can use it on any kind of text file including linker scripts.

      您可以创建一个宏文件,并定义几个宏,例如MAKE_IDT_DESCMAKE_GDT_DESC来创建GDT和IDT描述符条目.我使用扩展名命名约定,其中ldh代表(Linker Header),但是您可以根据需要命名这些文件:

      You can create a macro file and define a couple macros like MAKE_IDT_DESC and MAKE_GDT_DESC to create GDT and IDT descriptor entries. I use an extension naming convention where ldh stands for (Linker Header), but you can name these files whatever you wish:

      macros.ldh :

      #ifndef MACROS_LDH
      #define MACROS_LDH
      
      /* Linker script C pre-processor macros */
      
      /* Macro to build a IDT descriptor entry */
      #define MAKE_IDT_DESC(offset, selector, access) \
          SHORT(offset & 0x0000ffff); \
          SHORT(selector); \
          BYTE(0x00); \
          BYTE(access); \
          SHORT(offset >> 16);
      
      /* Macro to build a GDT descriptor entry */
      #define MAKE_GDT_DESC(base, limit, access, flags) \
          SHORT(limit); \
          SHORT(base); \
          BYTE(base >> 16); \
          BYTE(access); \
          BYTE((limit >> 16 & 0x0f) | (flags << 4));\
          BYTE(base >> 24);
      #endif
      

      要减少主链接程序脚本中的混乱情况,您可以创建另一个头文件来构建GDT和IDT(及相关记录):

      To cut down on the clutter in the main linker script you can create another header file that builds the GDT and IDT (and associated records):

      gdtidt.ldh

      #ifndef GDTIDT_LDH
      #define GDTIDT_LDH
      
      #include "macros.ldh"
      
      /* GDT table */
      . = ALIGN(4);
      gdt = .;
          NULL_SEL   = ABSOLUTE(. - gdt); MAKE_GDT_DESC(0, 0, 0, 0);
          CODE32_SEL = ABSOLUTE(. - gdt); MAKE_GDT_DESC(0, 0x000fffff, 10011010b, 1100b);
          DATA32_SEL = ABSOLUTE(. - gdt); MAKE_GDT_DESC(0, 0x000fffff, 10010010b, 1100b);
          /* TSS structure tss_entry and TSS_SIZE are exported from an object file */
          TSS32_SEL  = ABSOLUTE(. - gdt); MAKE_GDT_DESC(tss_entry, TSS_SIZE - 1, \
                                                        10001001b, 0000b);
      gdt_size = ABSOLUTE(. - gdt);
      
      /* GDT record */
      . = ALIGN(4);
      SHORT(0);                      /* These 2 bytes align LONG(gdt) on 4 byte boundary */
      gdtr = .;
          SHORT(gdt_size - 1);
          LONG(gdt);
      
      /* IDT table */
      . = ALIGN(4);
      idt = .;
          MAKE_IDT_DESC(exc0, CODE32_SEL, 10001110b);
          MAKE_IDT_DESC(exc1, CODE32_SEL, 10001110b);
          MAKE_IDT_DESC(exc2, CODE32_SEL, 10001110b);
          MAKE_IDT_DESC(exc3, CODE32_SEL, 10001110b);
      idt_size = ABSOLUTE(. - idt);
      
      /* IDT record */
      . = ALIGN(4);
      SHORT(0);                      /* These 2 bytes align LONG(idt) on 4 byte boundary */
      idtr = .;
          SHORT(idt_size - 1);
          LONG(idt);
      
      #endif
      

      现在,您只需要在链接脚本中要放置结构的点(在节内)中包含gdtidt.ldh:

      Now you just have to include gdtidt.ldhin the linker script at a point (inside a section) that you'd like to place the structures:

      link.ld.pp :

      OUTPUT_FORMAT("elf32-i386");
      ENTRY(_start);
      
      REAL_BASE = 0x00007c00;
      
      SECTIONS
      {
          . = REAL_BASE;
      
          .text : SUBALIGN(4) {
              *(.text*);
          }
      
          .rodata : SUBALIGN(4) {
              *(.rodata*);
          }
      
          .data : SUBALIGN(4) {
              *(.data);
      /* Place the IDT and GDT structures here */
      #include "gdtidt.ldh"
          }
      
          /* Disk boot signature */
          .bootsig : AT(0x7dfe) {
              SHORT (0xaa55);
          }
      
          .bss : SUBALIGN(4) {
              *(COMMON);
              *(.bss)
          }
      
          /DISCARD/ : {
              *(.note.gnu.property)
              *(.comment);
          }
      }
      

      此链接描述文件是我用于引导扇区的典型脚本,但是我所做的只是包括gdtidt.ldh文件,以允许链接描述文件生成结构.剩下要做的就是对link.ld.pp文件进行预处理.我将.pp扩展名用于预处理程序文件,但您可以使用任何扩展名.要从link.ld.pp创建link.ld,可以使用以下命令:

      This linker script is a typical one I use for boot sectors, but all I've done is include the gdtidt.ldh file to allow the linker to generate the structures. The only thing left to do is pre-process the link.ld.pp file. I use the .pp extension for pre-processor files but you could use any extension. To create link.ld from link.ld.pp you can use the command:

      cpp -P link.ld.pp >link.ld
      

      生成的结果link.ld文件如下所示:

      The resulting link.ld file that gets generated will look like:

      OUTPUT_FORMAT("elf32-i386");
      ENTRY(_start);
      REAL_BASE = 0x00007c00;
      SECTIONS
      {
          . = REAL_BASE;
          .text : SUBALIGN(4) {
              *(.text*);
          }
          .rodata : SUBALIGN(4) {
              *(.rodata*);
          }
          .data : SUBALIGN(4) {
              *(.data);
      . = ALIGN(4);
      gdt = .;
          NULL_SEL = ABSOLUTE(. - gdt); SHORT(0); SHORT(0); BYTE(0 >> 16); BYTE(0); BYTE((0 >> 16 & 0x0f) | (0 << 4)); BYTE(0 >> 24);;
          CODE32_SEL = ABSOLUTE(. - gdt); SHORT(0x000fffff); SHORT(0); BYTE(0 >> 16); BYTE(10011010b); BYTE((0x000fffff >> 16 & 0x0f) | (1100b << 4)); BYTE(0 >> 24);;
          DATA32_SEL = ABSOLUTE(. - gdt); SHORT(0x000fffff); SHORT(0); BYTE(0 >> 16); BYTE(10010010b); BYTE((0x000fffff >> 16 & 0x0f) | (1100b << 4)); BYTE(0 >> 24);;
          TSS32_SEL = ABSOLUTE(. - gdt); SHORT(TSS_SIZE - 1); SHORT(tss_entry); BYTE(tss_entry >> 16); BYTE(10001001b); BYTE((TSS_SIZE - 1 >> 16 & 0x0f) | (0000b << 4)); BYTE(tss_entry >> 24);;
      gdt_size = ABSOLUTE(. - gdt);
      . = ALIGN(4);
      SHORT(0);
      gdtr = .;
          SHORT(gdt_size - 1);
          LONG(gdt);
      . = ALIGN(4);
      idt = .;
          SHORT(exc0 & 0x0000ffff); SHORT(CODE32_SEL); BYTE(0x00); BYTE(10001110b); SHORT(exc0 >> 16);;
          SHORT(exc1 & 0x0000ffff); SHORT(CODE32_SEL); BYTE(0x00); BYTE(10001110b); SHORT(exc1 >> 16);;
          SHORT(exc2 & 0x0000ffff); SHORT(CODE32_SEL); BYTE(0x00); BYTE(10001110b); SHORT(exc2 >> 16);;
          SHORT(exc3 & 0x0000ffff); SHORT(CODE32_SEL); BYTE(0x00); BYTE(10001110b); SHORT(exc3 >> 16);;
      idt_size = ABSOLUTE(. - idt);
      . = ALIGN(4);
      SHORT(0);
      idtr = .;
          SHORT(idt_size - 1);
          LONG(idt);
          }
          .bootsig : AT(0x7dfe) {
              SHORT (0xaa55);
          }
          .bss : SUBALIGN(4) {
              *(COMMON);
              *(.bss)
          }
          /DISCARD/ : {
              *(.note.gnu.property)
              *(.comment);
          }
      }
      

      在问题中对示例boot.asm文件进行了一些修改后,我们最终得到:

      With slight modifications to the sample boot.asm file in the question we end up with:

      boot.asm :

      PM_MODE_STACK      EQU 0x10000 ; Protected mode stack address
      RING0_STACK        EQU 0x11000 ; Stack address for transitions to ring0
      TSS_IO_BITMAP_SIZE EQU 0       ; Size 0 disables IO port bitmap (no permission)
      
      global _start
      ; Export the exception handler addresses so the linker can access them
      global exc0
      global exc1
      global exc2
      global exc3
      
      ; Export the TSS size and address of the TSS so the linker can access them
      global TSS_SIZE
      global tss_entry
      
      ; Import the IDT/GDT and selector values generated by the linker
      extern idtr
      extern gdtr
      extern CODE32_SEL
      extern DATA32_SEL
      extern TSS32_SEL
      
      bits 16
      
      section .text
      _start:
          xor ax, ax
          mov ds, ax
          mov es, ax
          mov ss, ax
          mov sp, ax                  ; Stack grows down from physical address 0x00010000
                                      ; SS:SP = 0x0000:0x0000 wraps to top of 64KiB segment
      
          cli
          cld
          lgdt [gdtr]                 ; Load our GDT
          mov eax, cr0
          or eax, 1
          mov cr0, eax                ; Set protected mode flag
          jmp CODE32_SEL:start32      ; FAR JMP to set CS
      
      bits 32
      start32:
          mov ax, DATA32_SEL          ; Setup the segment registers with data selector
          mov ds, ax
          mov es, ax
          mov ss, ax
          mov esp, PM_MODE_STACK      ; Set protected mode stack pointer
      
          mov fs, ax                  ; Not currently using FS and GS
          mov gs, ax
      
          lidt [idtr]                 ; Load our IDT
      
          ; This TSS isn't used in this code since everything is running at ring 0.
          ; Loading a TSS is for demonstration purposes in this case.
          mov eax, TSS32_SEL
          ltr ax                      ; Load default TSS (used for exceptions, interrupts, etc)
      
          xchg bx, bx                 ; Bochs magic breakpoint
      
          ; Test the first 4 exception handlers
          int 0
          int 1
          int 2
          int 3
      
      .loop:
          hlt
          jmp .loop
      
      exc0:
          mov word [0xb8000], 0x5f << 8 | '0'   ; Print '0'
          iretd
      exc1:
          mov word [0xb8002], 0x5f << 8 | '1'   ; Print '1'
          iretd
      exc2:
          mov word [0xb8004], 0x5f << 8 | '2'   ; Print '2'
          iretd
      exc3:
          mov word [0xb8006], 0x5f << 8 | '3'   ; Print '3'
          iretd
      
      section .data
      ; Generate a functional TSS structure
      ALIGN 4
      tss_entry:
      .back_link: dd 0
      .esp0:      dd RING0_STACK     ; Kernel stack pointer used on ring0 transitions
      .ss0:       dd DATA32_SEL      ; Kernel stack selector used on ring0 transitions
      .esp1:      dd 0
      .ss1:       dd 0
      .esp2:      dd 0
      .ss2:       dd 0
      .cr3:       dd 0
      .eip:       dd 0
      .eflags:    dd 0
      .eax:       dd 0
      .ecx:       dd 0
      .edx:       dd 0
      .ebx:       dd 0
      .esp:       dd 0
      .ebp:       dd 0
      .esi:       dd 0
      .edi:       dd 0
      .es:        dd 0
      .cs:        dd 0
      .ss:        dd 0
      .ds:        dd 0
      .fs:        dd 0
      .gs:        dd 0
      .ldt:       dd 0
      .trap:      dw 0
      .iomap_base:dw .iomap          ; IOPB offset
      .iomap: TIMES TSS_IO_BITMAP_SIZE db 0x00
                                     ; IO bitmap (IOPB) size 8192 (8*8192=65536) representing
                                     ; all ports. An IO bitmap size of 0 would fault all IO
                                     ; port access if IOPL < CPL (CPL=3 with v8086)
      %if TSS_IO_BITMAP_SIZE > 0
      .iomap_pad: db 0xff            ; Padding byte that has to be filled with 0xff
                                     ; To deal with issues on some CPUs when using an IOPB
      %endif
      TSS_SIZE EQU $-tss_entry
      

      新的boot.asm还将创建一个TSS表(tss_entry),该链接表脚本中将使用该表来构建与该TSS相关联的GDT条目.

      The new boot.asm also creates a TSS table (tss_entry) which is used in the linker script to build the GDT entry associated with that TSS.

      预处理链接脚本;集合;关联;并生成一个用作引导扇区的二进制文件,可以使用以下命令:

      To pre-process the linker script; assemble; link; and generate a binary file that works as a boot sector, the following commands can be used:

      cpp -P link.ld.pp >link.ld
      nasm -f elf32 -gdwarf -o boot.o boot.asm
      ld -melf_i386 -Tlink.ld -o boot.elf boot.o
      objcopy -O binary boot.elf boot.bin
      

      要在QEMU中运行boot.bin软盘映像,可以使用以下命令:

      To run the boot.bin floppy disk image in QEMU you can use the command:

      qemu-system-i386 -drive format=raw,index=0,if=floppy,file=boot.bin
      

      要与BOCHS一起运行,可以使用以下命令:

      To run it with BOCHS you can use the command:

      bochs -qf /dev/null \
              'floppya: type=1_44, 1_44="boot.bin", status=inserted, write_protected=0' \
              'boot: floppy' \
              'magic_break: enabled=0'
      

      代码执行以下操作:

      • 使用lgdt指令加载GDT记录.
      • 处理器处于禁用A20的32位保护状态.演示中的所有代码都位于物理地址0x100000(1MiB)下,因此不需要启用A20.
      • 使用lidt加载IDT记录.
      • 使用ltr将TSS选择器加载到任务寄存器中.
      • 调用每个异常处理程序(exc0exc1exc2exc3).
      • 每个异常处理程序在显示屏的左上角打印一个数字(0、1、2、3).
      • Load the GDT record with the lgdt instruction.
      • Processor is put into 32-bit protected with A20 disabled. All the code in the demonstration resides beneath physical address 0x100000 (1MiB) so enabling A20 isn't required.
      • Loads the IDT record with lidt.
      • Loads the TSS selector into the task register with ltr.
      • Calls each of the exception handlers (exc0, exc1, exc2, and exc3).
      • Each exception handler prints a number (0, 1, 2, 3) to the upper left corner of the display.

      如果它在BOCHS中正确运行,则输出应类似于:

      If it runs correctly in BOCHS the output should look like:

      这篇关于在组装/编译/链接时构建静态IDT和GDT所需的解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆