can relative jmp but not absolute!?(solved)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

can relative jmp but not absolute!?(solved)

Post 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.
Last edited by earlz on Fri Apr 17, 2009 1:26 pm, edited 1 time in total.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: can relative jmp but not absolute!?

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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..
User avatar
nekros
Member
Member
Posts: 391
Joined: Wed Mar 05, 2008 9:10 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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.
Working On:Bootloader, RWFS Image Program
Leviathan: http://leviathanv.googlecode.com
Kernel:Working on Design Doc
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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
Last edited by earlz on Thu Apr 16, 2009 8:08 pm, edited 1 time in total.
User avatar
nekros
Member
Member
Posts: 391
Joined: Wed Mar 05, 2008 9:10 pm
Contact:

Re: can relative jmp but not absolute!?

Post by nekros »

Yeah but does it restore them?
Working On:Bootloader, RWFS Image Program
Leviathan: http://leviathanv.googlecode.com
Kernel:Working on Design Doc
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: can relative jmp but not absolute!?

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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..
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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)
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Re: can relative jmp but not absolute!?

Post 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.
Post Reply