Re: Manipulate the return address of a C function
Posted: Wed Mar 04, 2009 7:02 pm
can you rephrase that last bit? I'm not entirely sure what your asking.
And yes you are right, IRET has different behaviour when it returns to kernel mode.
According to the intel docs, IRET pops off the eip, cs and eflags (in that order) and then if we are changing privalidge level, it also pops the useresp and ss.
This occured to me earlier, but i was assuming you where doing usermode stuff rather than not.
You can't just remove the useresp and ss values, since you do want to change stack, so just push the esp and ss to the stack and pop them as required.
note, when we pop esp, we will have the same stack as before, hence the same eip as when the interrupt was called, which is fine, however you have to be carefull when modifying useresp in the registers struct, since changing it will change what stack it goes back to, and since the eip and things are on the original stack, they will also be different.
This is an issue when you create a thread now, as you will go to the new stack, then pop off eflags, cs and eip, which of course arn't on the new stack already.
So you have to put them there.
So .. I think this will work, it'll prob have some bugs in, but i think the principle is sound, sorry its so complex, there may be a simpler solution, but i can't see it yet.
Also sorry about the lengthiness and wordiness and probable incoherancy of this post, I came up with the solution as I went, I hope you understand it.
And yes you are right, IRET has different behaviour when it returns to kernel mode.
According to the intel docs, IRET pops off the eip, cs and eflags (in that order) and then if we are changing privalidge level, it also pops the useresp and ss.
This occured to me earlier, but i was assuming you where doing usermode stuff rather than not.
You can't just remove the useresp and ss values, since you do want to change stack, so just push the esp and ss to the stack and pop them as required.
Code: Select all
_isr0:
cli
push esp
push ss
push byte 0 ; Push a dummy error code.
push byte 0 ; Push the interrupt number.
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
call _isr_c_timer
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
pop ss
pop esp
sti
IRET
This is an issue when you create a thread now, as you will go to the new stack, then pop off eflags, cs and eip, which of course arn't on the new stack already.
So you have to put them there.
Code: Select all
void create_thread(void (*entry)(), u32int stack) {
registers_t t = registers[current_thread];
t.eip = entry;
//add the end_task method to the stack
stack -=4
*((u32int*)t.useresp) = end_task;
//add the eip, cs and eflags to the stack
stack-=4;
*((u32int*)stack = entry;
stack-=4;
*((u32int*)stack = t.cs;
stack-=4;
*((u32int*)stack = t.eflags;
//now set the user stack to stack
//so when it pops off into esp there is the right values there
t.useresp = stack;
//now the new stack has eflags, cs, eip, end_task on it, in that order.
//the _isr0 code pops ss, esp off the current stack, giving the new stack
//iret pops eflags, cs, eip off the new stack, leaving just end_task
//when the thread returns, end_task should be called.
registers[thread_count++] = t;
}
Also sorry about the lengthiness and wordiness and probable incoherancy of this post, I came up with the solution as I went, I hope you understand it.