Page 1 of 1
can relative jmp but not absolute!?(solved)
Posted: Thu Apr 16, 2009 4:23 pm
by earlz
Hi, I have been adding paging support recently..
My program code is loaded at 0xA0000000
I have confirmed that my code properly executes and is indeed loaded at 0xA0000000(I did a cli from usermode and got a general protection fault. The eip was A0000000)
(also, I have copied my test_paged_task code so that it is aligned within a page)
But now I have a very strange problem. I can relative jmp but not absolute.
(this is all in yasm)
Code: Select all
global test_paged_task
test_paged_task:
.t:
mov eax,0xA0000000
nop
nop
nop
int 80 ;this makes the bug show up in emulation(commented it works fine in emulation, but on real HW it breaks)
jmp dword eax
that will not work, it will say page fault, attempting to get page 0 from memory but it says the EIP that causes it is A0000041 which makes no sense at all, how could it even get there!?
but here is the strange thing:
Code: Select all
global test_paged_task
test_paged_task:
.t:
mov eax,0xA0000000
nop
nop
nop
int 80
jmp dword .t
Using a relative jump though, it will work fine. It calls the interrupt and does exactly as it should. With this, I can even make multiple paged tasks and they all work fine without corrupting each other. But it just doesn't make sense. Am I missing something here? is my assembler code incorrect on the first one?
Also, the first one breaks on emulation and real hardware; the second one works on emulation and real hardware.
Re: can relative jmp but not absolute!?
Posted: Thu Apr 16, 2009 6:02 pm
by Brendan
Hi,
Just a thought, but does "int 80" modify the contents of EAX?
Try this:
Code: Select all
int 80 ;this makes the bug show up in emulation(commented it works fine in emulation, but on real HW it breaks)
mov eax,0xA0000000
jmp dword eax
Cheers,
Brendan
Re: can relative jmp but not absolute!?
Posted: Thu Apr 16, 2009 6:07 pm
by earlz
Brendan wrote:Hi,
Just a thought, but does "int 80" modify the contents of EAX?
Try this:
Code: Select all
int 80 ;this makes the bug show up in emulation(commented it works fine in emulation, but on real HW it breaks)
mov eax,0xA0000000
jmp dword eax
Cheers,
Brendan
oh my god.. I'm so dumb..
that fixed it.. though now I have to go trace another bug cause my interrupt handlers are support to push and pop all registers..
Re: can relative jmp but not absolute!?
Posted: Thu Apr 16, 2009 7:11 pm
by nekros
Apparently, you haven't added process support yet have you? After every interrupt that occurs during a programs execution, you need to restore the programs state. That is, unless you want to pass some information through registers or the programs stack of course.
Re: can relative jmp but not absolute!?
Posted: Thu Apr 16, 2009 7:33 pm
by earlz
nekros wrote:Apparently, you haven't added process support yet have you? After every interrupt that occurs during a programs execution, you need to restore the programs state. That is, unless you want to pass some information through registers or the programs stack of course.
Well this is something I just realized.. registers should be saved.. so why aren't they? now I have to go trace down anohter bug cause my interrupt handler(at least I thought) saved all registers..
edit:
This makes no sense.. I have a pusha and popa at the start and end of each interrupt handler.. so this makes no sense.. when printing the hex value of the pushed eax from within the interrupt handler it for some reason is distorted to 0xA0000020
this is some weird crap
Re: can relative jmp but not absolute!?
Posted: Thu Apr 16, 2009 7:50 pm
by nekros
Yeah but does it restore them?
Re: can relative jmp but not absolute!?
Posted: Thu Apr 16, 2009 8:10 pm
by earlz
see my edit above..
also this is my interrupt handling code
Code: Select all
int_template: ;compiled once but edited and used as a template
; push byte 0 ;error code
; push byte 0 ;interrupt number
pusha
push ds
push es
push fs
push gs
mov ax, 0x08 ; Load the Kernel Data Segment descriptor!
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss,ax
mov ebx, esp ; move the stack to temporary location
mov eax,0
inc byte [int_level]
push ebx ;push the stack so it can be used as a C argument
mov eax, int_catcher ;this stays constant
call eax ; A special call, preserves the 'eip' register
;Get the return value
pop esp ;throw away parameter
mov esp,eax ;The int_catcher returns the stack!
dec byte [int_level]
pop gs
pop fs
pop es
pop ds
popa
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP!
end_template:
int_template_size: dd (end_template-int_template)
Re: can relative jmp but not absolute!?
Posted: Fri Apr 17, 2009 12:59 am
by Brendan
Hi,
earlz wrote:see my edit above..
also this is my interrupt handling code
Um.
The first 2 lines are commented out, so the "add esp,8" at the end would make ESP wrong. This should cause it to crash, so I'm guessing the "mov esp,eax" changes ESP to something different, causing the POPA to get the wrong data from the stack and the IRET to get the right data from the stack.
I'm also a bit worried by the "this is compied once but edited and used as a template" part. What edits the template (and what gets messed up when that happens)? More importantly is *why* this template is copied and modified in the first place.
Something more sane might look like this:
Code: Select all
first_interrupt:
push dword 0 ;Fake error code
push dword 1 ;Interrupt number
jmp common_int_handler
second_interrupt:
push dword 0 ;Fake error code
push dword 2 ;Interrupt number
jmp common_int_handler
third_interrupt:
push dword 0 ;Fake error code
push dword 3 ;Interrupt number
jmp common_int_handler
common_int_handler:
pusha
push ds
push es
push fs
push gs
No need to use self-modifying code at all...
However, the entire idea of using a common interrupt handler is broken - the first thing the common interrupt handler would do is a "switch{}" because different interrupts need to run different code; so you go from one piece of code for each interrupt (kindly provided by the CPU/IDT), to a common piece of code for all interrupts, to a different piece of code for each interrupt again (with a lot of useless overhead in the middle to keep the compiler happy). It might make sense for IRQ handlers (e.g. if the kernel delivers IRQs to processes via. IPC, but not in a monolithic kernel), but for all other interrupts (IPIs, software interrupts, exception handlers, spurious IRQs and the timer IRQ if that's used by the scheduler) it's like going to a tattoo shop and asking them to remove a tattoo, and then buying a tattoo when they realize you don't have one to remove yet.
Cheers,
Brendan
Re: can relative jmp but not absolute!?
Posted: Fri Apr 17, 2009 9:52 am
by earlz
Well, I thought the constant repetition of interrupts is dumb. Sure you could do a yasm macro.. but that isn't quite good enough for me..
So I made a thing that copies the interrupt handler into a buffer and pushes a dummy error code(if needed) and adds stuff to the end for IRQ handling and all that.. really it is quite manageable.. it is only confusing to read. Also, I don't consider it really self-modifying in the usual sense. It just has multiple copies of this code in memory with a piece added depending on what kind of interrupt, so no cache stuff that I could mess up..
But anyway, the add esp,8 is correct. The interrupt modifier will always "uncomment" those two lines if needed and what not..
and the int_catcher function is common among them all.
Code: Select all
struct InterruptInfo *int_catcher(struct InterruptInfo *cpu){
if(int_hooks[cpu->int_no]==NULL){return cpu;}
return int_hooks[cpu->int_no](cpu);
}
If there is no interrupt installed, then it ignores the interrupt..
The interrupt code I think is sound.. but the eax problem makes no sense.. because the pusha and popa are never touched..
Re: can relative jmp but not absolute!?
Posted: Fri Apr 17, 2009 11:31 am
by earlz
earlz wrote:Well, I thought the constant repetition of interrupts is dumb. Sure you could do a yasm macro.. but that isn't quite good enough for me..
So I made a thing that copies the interrupt handler into a buffer and pushes a dummy error code(if needed) and adds stuff to the end for IRQ handling and all that.. really it is quite manageable.. it is only confusing to read. Also, I don't consider it really self-modifying in the usual sense. It just has multiple copies of this code in memory with a piece added depending on what kind of interrupt, so no cache stuff that I could mess up..
But anyway, the add esp,8 is correct. The interrupt modifier will always "uncomment" those two lines if needed and what not..
and the int_catcher function is common among them all.
Code: Select all
struct InterruptInfo *int_catcher(struct InterruptInfo *cpu){
if(int_hooks[cpu->int_no]==NULL){return cpu;}
return int_hooks[cpu->int_no](cpu);
}
If there is no interrupt installed, then it ignores the interrupt..
The interrupt code I think is sound.. but the eax problem makes no sense.. because the pusha and popa are never touched..
EDIT:
Ok I have been playing around with this a little bit and discovered if I disable interrupts in the paging function then I never get an error. So that means that my problem is either caused by my scheduler, my IRQs, or a race condition between the two(or possibly the IRQ interrupting the interrupt I call)
so it's not my interrupt stub that is the problem.. .
EDIT2:
Ok, I have concluded it is a very hard to trace race-condition. Also, it is not a problem of an IRQ interrupting another interrupt(the 0020 bug appeared with disabling interruptins within interrupts and by another hard to explain method)
Re: can relative jmp but not absolute!?
Posted: Fri Apr 17, 2009 1:25 pm
by earlz
Holy **** I'm dumb...
Code: Select all
//from the template editor..
tmp[int_template_size+4-1]=0xB0; //mov al,0x20 --this actually overwrites the iret in the template
tmp[int_template_size+4+0]=0x20; //^
tmp[int_template_size+4+1]=0xE6; //out 0x20,al
tmp[int_template_size+4+2]=0x20; //^
tmp[int_template_size+4+3]=0xCF; //iret
Yea, I'm sure that has somethign to do with it. It was just now brought to my attention that out doesn't have an immediate type option, so everything secretly gets 0x20 on the bottom bit.