shift operator may only be applied to scalar values
另一个相关问题询问了GCC代码生成静态IDT时的问题,导致出现以下错误:
在这两种情况下,问题与IDT条目需要指向异常处理程序的地址以及GDT可能需要指向另一个结构(如任务段结构(TSS))的基址有关。通常这不是问题,因为链接过程可以通过重定位修复解决这些地址。在IDT entry或GDT Entry的情况下,字段分离了基地址/函数地址。没有重定位类型可以告诉链接器移动位并将它们按照GDT / IDT条目中的布局放置在内存中。Peter Cordes在this answer中对此进行了很好的解释。 我的问题不是在询问问题是什么,而是请求对问题的功能性和实际的解决方案。虽然我自己回答了这个问题,但这只是众多可能解决方案之一。我只要求提出的解决方案符合以下要求:initializer element is not constant
- GDT和IDT的地址不应固定在特定的物理或线性地址上。
- 至少,该解决方案应能够与ELF对象和ELF可执行文件一起工作。如果它适用于其他格式,那就更好了!
- 无论解决方案是否是构建最终可执行文件/二进制文件的过程的一部分都没有关系。如果一个解决方案需要在生成可执行文件/二进制文件后进行构建时处理,那也是可以接受的。
- 当加载到内存中时,GDT(或IDT)需要显示为完全已解析。解决方案不得需要运行时修复。
不起作用的示例代码
我提供了一些样本代码,以遗留引导程序1的形式,试图在汇编时创建静态IDT和GDT,但在使用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++等。
由于遗留的引导加载程序始终由BIOS加载到物理地址0x7c00处,因此对于这种情况可以在汇编时进行其他特定的解决方案。这些特定的解决方案会破坏操作系统开发中更一般的用例,其中开发人员通常不希望将IDT或GDT地址硬编码到特定的线性/物理地址,而是更倾向于让链接器为他们完成这项工作。