Page 1 of 1

Calling a function to a memory location (API implementation)

Posted: Sun Jan 02, 2011 7:12 pm
by Fergo
Hey everyone,

It's been a long time since I last posted here, but I'm back messing with OS design and I have a question:
I'm writing my kernel in C, and now it's time to implement a simple "API" for third party binaries to use the system functions. My basic idea is that I'll know the address of each of my kernel functions (I'll keep them at the beginning of the code, never changing it). In asm this idea works very well, because I can easily call a a procedure by a memory address (or by a constant which points to that address, to make things easier), but I don't know how to do something similar in C.

In assembly, for example, I'll add some something like this at a specific location of my kernel:

Code: Select all

call os_function_one
ret
call os_function_two
ret
call os_function_three
ret
As this piece of code will always be at the same location, I know the the address of each function (os_function_one would be 0x400, os_function_two would be 0x404, etc), so I could simple create a .inc file like the one below and make it available to third party developers, in asm:

Code: Select all

os_function_one	equ	0x0400	
os_function_two	equ	0x0404	
os_function_three	equ	0x0408
This way, the developer would just need to add this include to his project and call the functions, which would be translated into fixed address that would lead to that "call table" at the beginning of my kernel that redirects to the correct place.
It's a simple concept, by I'm having a hard time thinking on how to implement this to use with C development, not asm. Any ideas?

Thanks in advance,
Fergo.

Re: Calling a function to a memory location (API implementat

Posted: Sun Jan 02, 2011 7:25 pm
by NickJohnson
What you need to do is replace the table (which I assume contains a bunch of "jmp foo" elements) with a table of pointers to the kernel functions. Instead of doing a "call os_function_one", you would need to do a "mov ecx, os_function_one ; mov ecx, [ecx] ; call ecx", which is a bit more cumbersome in assembly, but would be done in C simply by calling a function pointer. You could easily wrap that in a set of stubs in the user binary though. From the kernel side, you simply need to make an array of function pointers that is linked at the beginning of the binary that is initialized to the system calls.

You could of course keep doing what you're doing too, and just write part of the system calls completely in assembly.

Re: Calling a function to a memory location (API implementat

Posted: Sun Jan 02, 2011 11:36 pm
by CelestialMechanic
NickJohnson wrote:What you need to do is replace the table (which I assume contains a bunch of "jmp foo" elements) with a table of pointers to the kernel functions. Instead of doing a "call os_function_one", you would need to do a "mov ecx, os_function_one ; mov ecx, [ecx] ; call ecx", which is a bit more cumbersome in assembly, but would be done in C simply by calling a function pointer. {Snip!}
Why three instructions? You could do "mov ecx, os_function_one; call [ecx]".

Better yet, since eax is probably going to be used for the return value, why not "mov eax, os_function_one; call [eax]"?

Of course, the register chosen will depend on your choice of registers for function arguments.

Re: Calling a function to a memory location (API implementat

Posted: Mon Jan 03, 2011 12:21 am
by Tosi
It's always good to review function pointer syntax as well. Remember you can use typedefs to abstract away some of the ugliness.

Code: Select all

typedef void (*syscall_t)(struct syscall_parms);
...
syscall_t syscall_table[NUM_SYSCALLS];
...
void
install_syscall(int num, long addr)
{
      syscall_table[num] = (syscall_t)addr;
}
And then when you want to call a given syscall you have the number for, just do:

Code: Select all

syscall_table[num](parms);
You could also encapsulate the syscall information in a struct containing a string, so you could get the address of a syscall by a user-passed string, like a GetProcAddress on Windows.

Re: Calling a function to a memory location (API implementat

Posted: Mon Jan 03, 2011 2:12 pm
by Fergo
Tosi wrote:It's always good to review function pointer syntax as well. Remember you can use typedefs to abstract away some of the ugliness.

Code: Select all

typedef void (*syscall_t)(struct syscall_parms);
...
syscall_t syscall_table[NUM_SYSCALLS];
...
void
install_syscall(int num, long addr)
{
      syscall_table[num] = (syscall_t)addr;
}
And then when you want to call a given syscall you have the number for, just do:

Code: Select all

syscall_table[num](parms);
You could also encapsulate the syscall information in a struct containing a string, so you could get the address of a syscall by a user-passed string, like a GetProcAddress on Windows.
Thanks. I think this is more or less what I have in mind. One question though: would be possible to make the syscall look more like a regular function? Instead of using an index for the syscall and a scruct for the arguments, would be possible to keep the syscall just like a regular C function? (my_syscall(arg1, arg2, arg3)?

Thanks a lot everyone,
Fergo

Re: Calling a function to a memory location (API implementat

Posted: Mon Jan 03, 2011 2:49 pm
by bewing
To do what you are talking about in a direct way, yes, you can use an array of function pointers in C, or a big structure full of unique "method" (ie. function) pointers -- to let apps know the entrypoints of system functions.

However:
In reality, you are going to have to do "sanity checking" on each of the arguments into every system call. And that sanity checking is unique for each call. So you will eventually need an API library that does something like:

Code: Select all

int user_function_X (arg1, arg2, arg3)
{
	if (sanity_check_arg1() == false) return -1;
	if (sanity_check_arg2() == false) return -1;
	if (sanity_check_arg3() == false) return -1;
	return syscall_func_X( arg1, arg2, arg3 );
}
And this API library may well end up getting coded in ASM.

Which means that it may not be wise to try to create some cute mapping from C into the system calls, because such a thing will never end up being used directly anyway.

Re: Calling a function to a memory location (API implementat

Posted: Tue Jan 04, 2011 8:38 pm
by Owen
syscalls.S

Code: Select all

ASM_EXPORT(sc_one) .equ 0xABCDEF00
ASM_EXPORT(sc_two) .equ 0xABCDEF20
syscalls.h

Code: Select all

_BEGIN_EXTERN_C
void sc_one(void* a, bool b, int c);
void sc_two(int a, ...);
_END_EXTERN_C
and so on...