gpt4 book ai didi

gcc - 无法修改数据段寄存器。尝试时抛出一般保护错误

转载 作者:行者123 更新时间:2023-12-01 01:41:17 26 4
gpt4 key购买 nike

我一直在尝试在此之后创建一个 ISR 处理程序
tutorial詹姆斯莫洛伊,但我卡住了。每当我抛出软件中断时,通用寄存器和数据段寄存器都会被压入堆栈,变量由 CPU 自动压入。然后将数据段更改为 0x10(内核数据段描述符)的值,从而更改特权级别。然后在处理程序返回这些值之后是 pop编。但每当 ds 中的值更改了 GPE,错误代码为 0x2544,几秒钟后 VM 重新启动。 (链接器和编译器 i386-elf-gcc ,汇编器 nasm)

我尝试放置 hlt指令之间的指令来定位哪个指令正在抛出 GPE。在那之后,我发现了 `mov ds,ax' 指令。我尝试了各种方法,例如删除由引导代码初始化的堆栈,以删除代码的权限更改部分。我可以从公共(public) stub 返回的唯一方法是删除我的代码中更改特权级别的部分,但是当我想转向用户模式时,我仍然希望它们留下来。

这是我的常见 stub :

isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
xor eax,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor

mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

call isr_handler

xor eax,eax
pop eax
mov ds, ax ; This is the instruction everything fails;
mov es, ax
mov fs, ax
mov gs, ax
popa
iret

我的 ISR 处理程序宏:
extern isr_handler

%macro ISR_NOERRCODE 1
global isr%1 ; %1 accesses the first parameter.
isr%1:
cli
push byte 0
push %1
jmp isr_common_stub
%endmacro

%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli
push byte %1
jmp isr_common_stub
%endmacro
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
...

我的 C 处理程序导致“收到中断:0xD err.code 0x2544”
#include <stdio.h>
#include <isr.h>
#include <tty.h>

void isr_handler(registers_t regs) {
printf("ds: %x \n" ,regs.ds);
printf("Received interrupt: %x with err. code: %x \n", regs.int_no, regs.err_code);
}


我的主要功能:
void kmain(struct multiboot *mboot_ptr) {
descinit(); // Sets up IDT and GDT
ttyinit(TTY0); // Sets up the VGA Framebuffer
asm volatile ("int $0x1"); // Triggers a software interrupt
printf("Wow"); // After that its supposed to print this
}

如您所见,代码应该输出,
ds: 0x10
Received interrupt: 0x1 with err. code: 0

但结果是,
...
ds: 0x10
Received interrupt: 0xD with err. code: 0x2544

ds: 0x10
Received interrupt: 0xD with err. code: 0x2544
...

一直持续到 VM 自行重新启动。

我究竟做错了什么?

最佳答案

代码不完整,但我猜你看到的是 James Molloy 的 OSDev 教程中一个众所周知的错误的结果。 OSDev 社区在 errata list 中编译了一个已知错误列表。 .我建议查看并修复其中提到的所有错误。特别是在这种情况下,我认为导致问题的错误是这个:

Problem: Interrupt handlers corrupt interrupted state

This article previously told you to know the ABI. If you do you will see a huge problem in the interrupt.s suggested by the tutorial: It breaks the ABI for structure passing! It creates an instance of the struct registers on the stack and then passes it by value to the isr_handler function and then assumes the structure is intact afterwards. However, the function parameters on the stack belongs to the function and it is allowed to trash these values as it sees fit (if you need to know whether the compiler actually does this, you are thinking the wrong way, but it actually does). There are two ways around this. The most practical method is to pass the structure as a pointer instead, which allows you to explicitly edit the register state when needed - very useful for system calls, without having the compiler randomly doing it for you. The compiler can still edit the pointer on the stack when it's not specifically needed. The second option is to make another copy the structure and pass that



问题是 32 位 System V ABI 不保证按值传递的数据在堆栈上不会被修改!编译器可以自由地将该内存用于它选择的任何目的。编译器可能生成的代码破坏了堆栈上存储 DS 的区域。当 DS 设置为虚假值时,它崩溃了。你应该做的是通过引用而不是值传递。我建议在汇编代码中进行这些代码更改:
irq_common_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10 ;0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ; At this point ESP is a pointer to where GS (and the rest
; of the interrupt handler state resides)
; Push ESP as 1st parameter as it's a
; pointer to a registers_t
call irq_handler
pop ebx ; Remove the saved ESP on the stack. Efficient to just pop it
; into any register. You could have done: add esp, 4 as well
pop ebx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 8
sti
iret

然后修改 irq_handler使用 registers_t *regs而不是 registers_t regs :
void irq_handler(registers_t *regs) {
if (regs->int_no >= 40) port_byte_out(0xA0, 0x20);
port_byte_out(0x20, 0x20);

if (interrupt_handlers[regs->int_no] != 0) {
interrupt_handlers[regs->int_no](*regs);
}
else
{
klog("ISR: Unhandled IRQ%u!\n", regs->int_no);
}
}

我实际上建议每个中断处理程序都使用指向 registers_t 的指针。以避免不必要的复制。如果您的中断处理程序和 interrupt_handlers数组使用了 registers_t * 的函数作为参数(而不是 registers_t ),那么您将修改代码:
interrupt_handlers[r->int_no](*regs); 

成为:
interrupt_handlers[r->int_no](regs);

重要 :您必须为您的 进行这些相同类型的更改。 ISR 处理程序 也是。 IRQ 和 ISR 处理程序以及相关代码都有同样的问题。

关于gcc - 无法修改数据段寄存器。尝试时抛出一般保护错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56481584/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com