Exception handling error
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Exception handling error
I was trying to get interrupts working on my OS, I set up the IDT and PIC, and I even successfully implemented 8254 IRQ 0 and tested it. Before going on to keyboard driver, I decided to implement handler for all exceptions, as they are currently handled by a dummy handler which does nothing.
Firstly, I implemented Division by zero, by calling a C function from assembly to print a message and wait for 5 seconds and then simply return. When I manually called the interrupt, it works fine. However, if I actually made a divide by zero error, it calls the interrupt, but that's the problem; it keeps calling it. I even tried to "return" to a different function, but as soon as it returned, it immediately gets re-triggered.
What is happening? I couldn't find anything in osdev like this.
Firstly, I implemented Division by zero, by calling a C function from assembly to print a message and wait for 5 seconds and then simply return. When I manually called the interrupt, it works fine. However, if I actually made a divide by zero error, it calls the interrupt, but that's the problem; it keeps calling it. I even tried to "return" to a different function, but as soon as it returned, it immediately gets re-triggered.
What is happening? I couldn't find anything in osdev like this.
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
Re: Exception handling error
Most exceptions are exact. This means that the CS:EIP in the IRET frame points to the faulting instruction. In your case, you have a faulting "div" instruction (or similar), so the IRET frame contains the address of that instruction, and when you IRET, you return exactly back to the faulting instruction. Which immediately faults again.
Now, sometimes this is what you want. Consider the case of a page fault, where some access fails, but only because a page has been removed temporarily. In that case, you suspend the faulting process, fix the problem, and return to the process when you're done. That way, the process can repeat the instruction that failed, and this time it won't fail.
But in most cases, you cannot handle an exception by simply returning, as the fault will just re-occur. Most faults in kernel space should probably be handled by a kernel panic. That means: Print as much information about the fault that occurred as you can (exception number, EIP and ESP of the failing context, maybe the other registers, and a stack trace is always helpful), and then stop the system. If you support multiple CPUs, send IPIs to them to stop them, then execute HLT in an endless loop. The reason is that in that case the kernel is faulty and must be stopped.
If a userspace program failed, then it is really up to you to decide what should happen. UNIX sends a signal to the program, Windows causes an exception to happen (which, if not caught, leads to the wonderful "foobar.exe does not work anymore" screen we all know and love). Whatever you do, don't just return, as that won't fix things.
Now, sometimes this is what you want. Consider the case of a page fault, where some access fails, but only because a page has been removed temporarily. In that case, you suspend the faulting process, fix the problem, and return to the process when you're done. That way, the process can repeat the instruction that failed, and this time it won't fail.
But in most cases, you cannot handle an exception by simply returning, as the fault will just re-occur. Most faults in kernel space should probably be handled by a kernel panic. That means: Print as much information about the fault that occurred as you can (exception number, EIP and ESP of the failing context, maybe the other registers, and a stack trace is always helpful), and then stop the system. If you support multiple CPUs, send IPIs to them to stop them, then execute HLT in an endless loop. The reason is that in that case the kernel is faulty and must be stopped.
If a userspace program failed, then it is really up to you to decide what should happen. UNIX sends a signal to the program, Windows causes an exception to happen (which, if not caught, leads to the wonderful "foobar.exe does not work anymore" screen we all know and love). Whatever you do, don't just return, as that won't fix things.
Carpe diem!
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Re: Exception handling error
But I tried changing the return address by poping the flags and then poping the return address to ebx, changing it to another function, pushing ebx and then flags and then issuing iret instruction. The same thing happens again. But this time, it flickers for a split second showing the output of the function I had givennullplan wrote:Most exceptions are exact. This means that the CS:EIP in the IRET frame points to the faulting instruction. In your case, you have a faulting "div" instruction (or similar), so the IRET frame contains the address of that instruction, and when you IRET, you return exactly back to the faulting instruction. Which immediately faults again.
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Exception handling error
Does that function try to return using the garbage left on the stack by the previous function?
Re: Exception handling error
You know, you can just modify things in memory, without having to pop them off the stack. Besides, EIP is on top of stack before IRET. If you did what you described, then you changed CS, not EIP. So, you want:
It would seem to me you managed to divert your interrupt return somewhere else, but that somewhere else just ended up faulting on its own.
Code: Select all
mov [esp], other_func
iretd
Carpe diem!
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Re: Exception handling error
Can I set it to ebp to simultaneously clear the garbage in stack before jumping to new location?nullplan wrote:You know, you can just modify things in memory, without having to pop them off the stack. Besides, EIP is on top of stack before IRET. If you did what you described, then you changed CS, not EIP.
Also, doesn't interrupts causes flags to be pushed on to the stack? So how should I set up the stack? flags, return or vice versa?
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Exception handling error
What will it return to if you do that? Can you guarantee that EBP holds a reasonable value?pranavappu007 wrote:Can I set it to ebp to simultaneously clear the garbage in stack before jumping to new location?
Re: Exception handling error
IRET removes the frame from stack. That is, if no change of privilege takes place, it increases ESP by 12. If a change of privilege takes place, the point is kind of moot, since stacks are switched.pranavappu007 wrote:Can I set it to ebp to simultaneously clear the garbage in stack before jumping to new location?
It does, but it pushes flags first. You would know that if your read the processor manual. Search for Intel Software Developer's Manual, or for AMD Architecture Programmer's Manual, depending on taste. They are both describing the same architecture, so the differences are minuscule. You want volume 2 in either case, since that is system programming for both vendors. And they will tell you that in 32-bit mode, an interrupt frame contains EIP, CS, and flags, in that order on stack (looking up from ESP). If the CS indicates that a change in privilege took place, then ESP and SS follow.pranavappu007 wrote:Also, doesn't interrupts causes flags to be pushed on to the stack? So how should I set up the stack? flags, return or vice versa?
Carpe diem!
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Re: Exception handling error
Does something like this will work?
Does this trick work at all? Does it need any modification?
Code: Select all
mov esp, ebp ;clearing the stack
pushf ;pushing the flags
mov ebx, other_function ;getting the pointer to non-error function
push ebx
iretd
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Exception handling error
Can you guarantee that EBP holds a reasonable value?
What will happen when other_function() returns? What will it return to? Will it be able to restore the callee-saved registers properly?
Also, if you come up with a solution to the above problems, you still need to put a CS selector on the stack between the flags and the return address.
What will happen when other_function() returns? What will it return to? Will it be able to restore the callee-saved registers properly?
Also, if you come up with a solution to the above problems, you still need to put a CS selector on the stack between the flags and the return address.
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Re: Exception handling error
If my understanding is correct, ebp holds the start of the frame created by a C function. I can make sure no division by zero exception occurs st start of my program. So if I set esp as ebp, then it should reset the stack frame by the function which caused div by zero.Octocontrabass wrote:Can you guarantee that EBP holds a reasonable value?
What will happen when other_function() returns? What will it return to? Will it be able to restore the callee-saved registers properly?
Also, if you come up with a solution to the above problems, you still need to put a CS selector on the stack between the flags and the return address.
For function return, it confess I didn't think about it. But now, I think I should create a managing function that never returns but manage this sort of thing. But inorder not to create unnecessary stack frames, I might do it in assembly.
So I should push the CS to the stack, before the address?
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Exception handling error
Only if the compiler has decided a frame pointer is necessary for the function. It may use EBP for something else, and then you have no idea where the stack will be.pranavappu007 wrote:If my understanding is correct, ebp holds the start of the frame created by a C function.
In that case, why return at all? Use an ordinary jump to go to your managing function.pranavappu007 wrote:For function return, it confess I didn't think about it. But now, I think I should create a managing function that never returns but manage this sort of thing. But inorder not to create unnecessary stack frames, I might do it in assembly.
Yep.pranavappu007 wrote:So I should push the CS to the stack, before the address?
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Re: Exception handling error
If I didn't do 'iret', doesn't the entire program code then be treated as part of ISR? I still have flat overlapping memory segments, but still can you do it?How does the CPU know the exception handling is done?Octocontrabass wrote: In that case, why return at all? Use an ordinary jump to go to your managing function.
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
-
- Member
- Posts: 91
- Joined: Mon Apr 20, 2020 11:02 am
Re: Exception handling error
Then how the compiler manages the stack? If ebp is used for something else, then isn't the entire stack is lost? OR does it store the stack pointer somewhere else?Octocontrabass wrote: Only if the compiler has decided a frame pointer is necessary for the function. It may use EBP for something else, and then you have no idea where the stack will be.
A beginner developer/student. Likes to know stuff. Don't have an OS to put here.
Re: Exception handling error
I’m new to x86 asm, but as I understand it, ESP is the stack pointer, EBP is just the stack frame pointer. If the function doesn’t require its own stack frame, then the compiler can use the register for other things like passing in the address of the return variable/structure (I’ve seen gcc do this).pranavappu007 wrote:Then how the compiler manages the stack? If ebp is used for something else, then isn't the entire stack is lost? OR does it store the stack pointer somewhere else?Octocontrabass wrote: Only if the compiler has decided a frame pointer is necessary for the function. It may use EBP for something else, and then you have no idea where the stack will be.
If you’ve entered the function via an interrupt, you know what the stack above the ESP stack pointer will contain (the interrupt stack frame and whatever you might have pushed during your interrupt handling).
-edit- if you want a clean return from a CPU exception, you need to build a recovery stack somewhere, you’ll need to manually build a stack frame to match what the CPU expects to find on the stack when it executes an iret... set the ip position in the clean stack frame to point to a recovery function. Then set the ESP register to this clean stack and execute iret.
This stuff is much easier with multitasking as you can just suspend the faulting task from the ready list, and swap in a good task.
Last edited by bloodline on Thu Oct 22, 2020 3:00 am, edited 5 times in total.
CuriOS: A single address space GUI based operating system built upon a fairly pure Microkernel/Nanokernel. Download latest bootable x86 Disk Image: https://github.com/h5n1xp/CuriOS/blob/main/disk.img.zip
Discord:https://discord.gg/zn2vV2Su
Discord:https://discord.gg/zn2vV2Su