Manipulate the return address of a C function

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.
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Manipulate the return address of a C function

Post by mangaluve »

When I create a thread in my OS, I do the following for now

Code: Select all

void create_thread(void (*entry)(), u32int stack) {
	registers_t t = registers[current_thread];
	t.eip = entry;
	t.esp = stack;

	registers[thread_count++] = t;
}
The first line is just to fill most of the registers with valid values (temporary for now). Then I set the stack pointer and the instruction pointer, where entry is the function that will be invoked by the thread. Everything works fine, context switches and such.

However, when a thread is done (i.e. the function "entry" is done) I would like to "automatically" go to a function called dispose_thread. So I tried something like this

Code: Select all

void dispose_thread() {
	printf("This thread is now dead");
	// do some clean up
}

void create_thread(void (*entry)(), u32int stack) {
	registers_t t = registers[current_thread];
	t.eip = entry;
	t.esp = stack;
	*((u32int*)stack--) = dipose_thread;
	registers[thread_count++] = t;
}
because I assumed that when my entry-function is finished, it pops the return-address from the stack (which would now be dispose_thread). So what I'm trying to do here is that I start with a "clean" stack. Then I push the return address. However, dispose_thread is never invoked. Does a C-function work like this? (pops the return address when it's done). Is the code for pushing correct? What I want to do is to "simulate" a assembler-CALL, so when the entry-function starts, the stack looks the same as if it has been called with CALL _entry from asm.
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

c uses the cdecl calling standards by default: http://en.wikipedia.org/wiki/X86_callin ... ions#cdecl

One way around this, is do the same thing for the way proccesses are started, define an entry point __thread_start, which calls entry, and then calls dispose, that way you don't even have to manipulate the stack.

As for why this code isn't working, it looks ok to me, so the best thing to do would be to step through the code using the bochs debugger monitoring esp to see where its pushing and popping.

Wiggles
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

Thanks! I thought about that too, but I also wanted to know why my code does not work. Anyway, suppose I make such a function in asm, how would it look? I want to pass the thread-function as an argument, so it would be something like
[global _thread_start]
[extern _dispose_thread]

Code: Select all

_thread_start:
   ; pop the entry-function from the stack and make a call to it	
   call _dispose_thread
Can I use to "call"-operation on a registry?
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

yep

Code: Select all

_thread_start:
   pop  eax     ;note we don't need to save eax first as this is a new thread
                   ;so it can get scrapped

   call   eax    ;do it

   call _dispose_thread

   jmp $           ;don't want to go past here if for some reason _dispose_thread fails
Also if you want to send arguments to the thread then just push them on the stack before you jmp (not call) _thread_start
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

This is the first time I've tried to do own threads..but here is the essenssial code. isr.asm is the ISR that will jump to some C-code for the actual switching.

isr.asm:

Code: Select all

[extern _isr_c_timer] ; 	C function in isr.c

[global _isr0]
_isr0:
    cli                         
    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
    sti
    iret           		; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP

isr.c:

Code: Select all

typedef struct registers
{
    u32int ds;
    u32int edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.
    u32int int_no, err_code;    // Interrupt number and error code (if applicable)
    u32int eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.
} __attribute__((packed)) registers_t;


void isr_c_timer(registers_t t) {
	registers[current_thread] = t;
	current_thread = (current_thread + 1) % thread_count;

	t = registers[current_thread];	

	PIC_sendEOI(0);			
}

void end_task() {
   printf("Killing the thread...");
   // do cleanup
}

void create_thread(void (*entry)(), u32int stack) {
	registers_t t = registers[current_thread];
	t.eip = entry;
	t.useresp = stack;

	*((u32int*)t.useresp) = end_task;
	t.useresp -= 4;

	registers[thread_count++] = t;
}
the _isr0 function is executed on a timer interrupt. The state of the CPU is stored in a structure that fits the registers_t struct. The C-function isr_c_timer is called. The current CPU-state is stored in the registers-array, to be used later when the thread is to execute again. The CPU-state for the next thread is loaded and put on the stack, Except for the ESP (we don't want to use the ESP of the "new" thread until we do iret, which will load t.useresp. if we didnt do this, the "new stackpointer" will be used after popa in the asm-function after we've returned to it).

If I just have a lot of threads that are looping forever, it works fine. However, if I have a thread that terminates after some time, everything crashes and the end_task-function is Not executed as supposed. The create_thread function is a little uggly. The first line is just to get valid values when we create the thread (it works). After setting the proper esp/stack pointer, I try to put the address of the end_task as the first thing on the new thread's stack. Do you see anything wrong here? Am I thinking the wrong way about not replacing ESP? Also it seems like the useresp above, pushed automatically when an interrupt is triggered, doesn't store the ESP.
Last edited by mangaluve on Wed Mar 04, 2009 9:26 am, edited 1 time in total.
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

Ok, couple of things:

1) you call cli twice in your isr0 routine.
2) you don't have to save the current esp, as you are doing, since if you look at the popa instruction, it just pops the esp off into nothing, it doesn't restore it.
3) The end_task part isn't working since that function exists in the kernel mode CS and probably exists in memory marked as supervisor. Whereas your thread is in a different CS, and can't access supervisor memory.

IE. you try and jump to it, firstly you are in the wrong CS, secondly the memory is not accessable to you.

This is a guess, there may well be other issues, note this also only makes sense if your threads are running in usermode.

What you want to do is have a end_task method in usermode (in your libc) that calls a interrupt, which means end task. This then makes it harder to set it up as a return address on the stack, since you don't know exactly where in the user mode prog it will be. This is why the method I described in my last post makes sense, as you don't have to bother with that.

Hope that helps.

Wiggles
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

Thank you! I know I can do it in different way (and I eventually will), but I'd like to understand why it doesn't work :)

I didn't know that popa didn't restore the values! Can I restore the values pushed by "pusha" in some simple way? (without having to do like 5 push:s). Or is it just hte stack pointer that isn't restored?

I didnt really understand what you mean by supervisor and so on, this is only for the kernel. I've been in the kernel all the time.. I only have one CS/DS/SS segment, used everywhere.. I modified the code in my previous post to remove some unneccessary stuff. But it still does not work.
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

ok, pusha pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
popa pops all of them except esp, which it skips over.
check the intel docs for more details volume 2b.

Ok, so if this is only kernel based then what i said was crap, also you can get rid of some stuff from your isr0 routine.

Code: Select all

_isr0:
    cli                         
    push byte 0                 ; Push a dummy error code.
    push byte 0                   ; Push the interrupt number.

    cli
    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
    sti
    iret                 ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
You don't need to save the data descriptors as they stay constant. Also you need to modify your registers struct, to remove them.

You don't have to do this, but meh.


Anyway, onto the main problem, I'm going to need some more details, how is it crashing? page fault / ...

What are you running this on?

If its bochs, check the bochout.txt and paste it to www.slexy.org for me and link it.
if its Quemu, then do whatever is equivelent.
if its real hardware, try and get it running in bochs so we can get some debugging going on.

One last thing to try, if you look at the push command, it decrements the stack pointer by 4, then stores the word there. What you are doing is storing then decrementing.

Try getting rid of the:
t.useresp -= 4;





Wiggles.
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

So in my code, only registers_t.useresp is used for the return right? (the next threads CS will equal that value?)
I removed the redundant code and I also tried to decrement the stack pointer before using it. The bochs error I get is

Code: Select all

00175742548p[CPU  ] >>PANIC<< prefetch: running in bogus memory
That's really the only interesting part in the log (the stuff before that is just BIOS / the boot loader)

one thing is a little weird..if I print the useresp when I switch thread, I get really weird values (except for the first time when I set the value myself). It could be an error in my printf-function.. but t.useresp Should contain the value of the esp right?
Last edited by mangaluve on Wed Mar 04, 2009 9:59 am, edited 1 time in total.
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

ok, so its not jumping to the correct place, when bochs does a dump, what does it say eip is?

Also your going to have to set up the debugger in bochs (theres a gui that makes it a bit nicer) but i can't remember what its called. You need to look at the stack as it ends and see what happens.

If you can upload your code, and compiler scripts, and details on how to run it in bochs, I can have a look through if you want.

Wiggles
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

Thanks it would be great if you could give the code a look, I'll upload it when I get home in perhaps two hours! Thanks!
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

Cool, also if you use irc then join irc.wyldryde.org and go to #osdev

Its pretty empty, but i'm in there, will make it easier to talk than through the forums.

Wiggles
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

Just a short one, when I create a thread, what should I set the eflags-register to? (which will be pop:ed when the thread should start). I have no idea how to initialize my register_t-struct for my new threads
captainwiggles
Posts: 23
Joined: Tue Nov 11, 2008 3:03 pm

Re: Manipulate the return address of a C function

Post by captainwiggles »

http://en.wikipedia.org/wiki/FLAGS_register_(computing)

I use 0x0202
which is the reserved bit, and interrupts enable bit set.
mangaluve
Member
Member
Posts: 110
Joined: Mon Feb 23, 2009 6:53 am

Re: Manipulate the return address of a C function

Post by mangaluve »

Thanks work better now (in general). Still can't get the call to end_task to work though.. I'll look into it a little more before I bother you with my code ;) One thing is weird though.. I print the useresp-value (which is supposed to be pushed when an interrupt is triggered) but it's allways zero. Do I have the fields in register_t in the right order?

Edit: something is weird with the stack. Does IRET allways pop the esp from the stack, even if it hasn't changed? I looked a little at http://forum.osdev.org/viewtopic.php?f=1&t=19092. It doesn't seem like it here. If im just before my IRET, in asm, how can I move the esp that's on the stack (which I've changed) to esp?
Post Reply