Calling a function to a memory location (API implementation)

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
User avatar
Fergo
Member
Member
Posts: 27
Joined: Fri Oct 19, 2007 3:11 pm
Contact:

Calling a function to a memory location (API implementation)

Post 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.
Image
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

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

Post 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.
CelestialMechanic
Member
Member
Posts: 52
Joined: Mon Oct 11, 2010 11:37 pm
Location: Milwaukee, Wisconsin

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

Post 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.
Microsoft is over if you want it.
Tosi
Member
Member
Posts: 255
Joined: Tue Jun 15, 2010 9:27 am
Location: Flyover State, United States
Contact:

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

Post 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.
User avatar
Fergo
Member
Member
Posts: 27
Joined: Fri Oct 19, 2007 3:11 pm
Contact:

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

Post 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
Image
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

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

Post 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.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

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

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