Memory allignment of parameters in Lite-C

Posted By: Excessus

Memory allignment of parameters in Lite-C - 06/12/07 10:12

I am working on a networking plugin for gamestudio. To implement remote procedure calls with an arbitrary number and types of parameters, I have to know some things about lite-c internals.

I've done a small test which confirms that the parameters of a function are stored in a single, contiguous piece of memory, and that each parameter takes 4 bytes.

Here is my test code:
Code:

void testfunc(char x, int y)
{
char* chrPtr = &x;
int* intPtr = chrPtr + 4;

STRING* tmp = str_create("");
error(str_for_num(tmp, *intPtr)); // prints y
}


I'd like to know if this is guaranteed to be that way, or if you plan to make changes. Are there any exceptions where such code would not behave as expected?

On the receiving end, I have to call a function from a lite-c function pointer in C++, with the received parameters (the parameters are stored in an array of bytes). Calling Lite-C functions from C++ with a Lite-C function pointer works, but I don't know how to pass the parameters if they are stored in a char array and I don't know the exact function signature.

So, given an array of bytes that contains the parameter data (in C++), how could I call a Lite-C function from a function pointer and set the parameters to the data in array? Basicly I need some way to copy over the data in the array to the piece of memory where the parameters are stored. Where is that piece of memory?

Do you think this kind of coding is dangerous? Should I just let the users serialize their own parameters into a char array and always use that as the parameter for a remote procedure call? (Less convenient for the users)
Posted By: jcl

Re: Memory allignment of parameters in Lite-C - 06/12/07 10:35

That piece of memory is the stack.

In a programming language, parameters to a function are pushed on the stack before calling the function. All C functions are normally guaranteed to work this way, which is why you can call any C function from any library with an arbitrary number of parameters.

The number of bytes per parameter can vary due to compiler implementations. For speed reasons, C compilers (including lite-C) normally use 32 bit parameters even for char and short.

If you have a lite-C function

int foo(int a, int b, int c) { ... }

and pass the pointer of this function to a C++ program, you can then just call the pointer from C++

*fooptr(a,b,c)

and the parameters are passed correctly over to lite-C. It does not matter how many parameters the function has.
Posted By: Excessus

Re: Memory allignment of parameters in Lite-C - 06/12/07 10:51

Thank you jcl,

That was helpful but I need some more information, let me clarify:
I want the user of my plugin to be able to pass any function pointer (to a function with ANY signature) to C++, so I use a void* for this.

In C++, I want to call this function with parameter data that I received over the internet. This parameter data is stored as a char array. Now I want to call the function pointer and pass the parameter data *without knowing the function signature* (I only know the length in bytes of the parameter list). The parameter data has the correct length in bytes, so it just has to be copied to the stack at the location where the Lite-C function expects the first parameter.

I've tested this C++ function:
Code:

struct LargeStruct
{
int a;
int b;
int c;
int d;
};
typedef void (*LiteCFunc)(LargeStruct x);
DLLFUNC void tst(int x, void* funcPtr)
{
LiteCFunc myFunc = static_cast<LiteCFunc>(funcPtr);
LargeStruct x;
x.a = 0;
x.b = x;
x.c = 0;
x.d = 0;
(*myFunc)(x);
}


Called it from Lite-C:
Code:

void testfunc(char x, int y); // function pointer/prototype
void tst(int x, void* funcPtr); // DLL function prototype
tst(100, testfunc);


This results in the Lite-C function testfunc being called with the second parameter set to 100. However, there are 2 problems with this aproach:
-There is a limit to how large (in bytes) a parameter list can be.
-I always have to copy the maximum amount of bytes, and if the function has a shorter parameter list (like above) I will be writing to unknown memory.

Is there a cleaner way to copy the (arbitrary length) parameter data to the stack?
Posted By: Tobias

Re: Memory allignment of parameters in Lite-C - 06/13/07 22:31

Why dont you use a pointer

(*myFunc)(&x);
Posted By: jcl

Re: Memory allignment of parameters in Lite-C - 06/14/07 06:12

Usually, calling a function with data of different size is done by giving the size as a function argument:

void foo(int size,char *data)
{
...
}

...
foo(sizeof(LargeStruct),&MyStruct);
Posted By: Excessus

Re: Memory allignment of parameters in Lite-C - 06/14/07 11:25

Yes that is possible, but I'm not the one writing the called functions, and I'd like to make this as easy as possible for the users of my plugin. I rather not require the user to do a lot of deserialization and casting (actually that's the whole point of implementing RPCs; convenience for the user). The user should be able to freely choose a parameter list, and my plugin should take care that the right values are passed.

The method with casting the parameters to a LargeStruct seems best. What I do is cast the RPC function pointer (passed to C++ as a void*) to a "void (*func) (LargeStruct)", regardless of what parameter list it has (parameter list size must be <= sizeof(LargeStruct)). I then cast the serialized parameter data to a LargeStruct and call the function pointer with this LargeStruct as parameter. Lite-C interprets the parameter data as the original parameter list (not as a LargeStruct), but since the data in the LargeStruct "fits" the parameter list, it will result in correct values for the parameters. I'm not so worried about the performance issues of copying a whole LargeStruct (could even use several structs of increasing size) nor about the fact that parameter list size will be limited to sizeof(LargeStruct).. I am however worried that since the LargeStruct is often larger than the parameter list, I am copying to memory I do not own. Could you tell me if this is a risk, or is the memory past the end of the parameter list safe to write to?

The best solution would be to be able to pass a char array by value, because then I can use a fitting size parameter list for every registered RPC function. I read here that this can be done by making the parameter a "const char []", but it doesn't seem to work.. It's hard to find information about this on the web, but maybe you have an idea as to why this doesn't work/how to make it work.

Thanks
Posted By: Joey

Re: Memory allignment of parameters in Lite-C - 06/14/07 14:28

why don't you copy the data onto the stack with assembler and then call the function without arguments?
Posted By: Excessus

Re: Memory allignment of parameters in Lite-C - 06/14/07 14:36

Hi Joey,

That's a really good idea! Unfortunately, I've never used assembler before. Where do you suggest I start learning assembler, and what topic should I study in particular to do this? Or is this trivial enough for you to be willing to write it for me/provide an example?
Posted By: jcl

Re: Memory allignment of parameters in Lite-C - 06/14/07 15:36

I'm not 100% sure that I fully understand what you intend to do, but when you call a function, the parameters are already on the stack. The assembler code for this is produced by the compiler before calling the function.

When you call something like

foo(MyStruct)

MyStruct is copied onto the stack and the function foo can access all members of it.
Posted By: Joey

Re: Memory allignment of parameters in Lite-C - 06/14/07 16:06

yes, but if he has no idea about the arguments and the argument structure he can't call the function afaik. like that, he could get the arguments delivered by some kind of reinterpret_cast<char[]> (i'd suggest that) and then he could copy that char* to the stack and then furthermore call the function without any arguments (get the address of the function, call it in assembler). that's some kind of detour but i think most compilers would complain about calling an unknown function with a struct as arguments - or does it automatically get transformed into the single arguments then afterwards in the function? i don't think so.

assembler is hard to learn. instruction sets differ and you need loads of time. you could start writing assembler snippets for stuff you normally do directly in c(++), p.ex. additions, copying, searching strings. there's a good tutorial with the very basics in a wikibook. personally i've bought a reference for all instructions (x86, mmx, sse(1-3)) since when you've got the idea behind assembler it's still hard to remember the instructions.

joey.
Posted By: Excessus

Re: Memory allignment of parameters in Lite-C - 06/14/07 18:30

Quote:

but if he has no idea about the arguments and the argument structure



Yes this is pretty much how it is. Although "I" and "my code" knows what kind of arguments are passed, the C++ compiler doesn't. The parameter data is stored in a piece of memory on the heap pointed to by a char*. The function pointer is stored as a void*.

Example of how my solution works:
- The user function to be called has the signature "void functionName (int, int)".
- The user function is passed to C++ as a void*. At this point the original type information is lost for the C++ compiler so I cast it to a "void (*) (LargeStruct)".
- I have a char* to a char array of 8 bytes containing the data for the two int parameters. They are in a char array because they where serialized and sent over the internet.
- I copy the parameter data pointed to by the char* to an instance of LargeStruct.
- I call the function pointed to by the "void (*) (LargeStruct)" with the instance of LargeStruct as parameter.
- Lite-C knows the original function signature and interprets the passed data as two ints. The int can be used like normal in the function.
The problem: sizeof(LargeStruct) > 2 * sizeof(int), so when I called the function from C++ (which thinks the function has a LargeStruct as parameter), more than 8 bytes where allocated. I'm afraid that the Lite-C compiler only frees 2 * sizeof(int) when the function returns. Is this the case?

The parameter data is in a char array, but I cannot just pass this char* because the Lite-C function (user function) expects some arbitrary parameter list and not a char*. I don't want to require the user to read his parameters from a char array. What the trick with LargeStruct does is create a type that is large enough to contain the parameter data while also allowing pass-by-value (unlike an array for example). The thing here is that C++ interprets the function as a function taking a LargeStruct as parameter, and Lite-C interprets it as a function taking whatever parameters the user has specified.

As I sayd the trick (or should I say hack?) with the LargeStruct seems to work, but I'm afraid that because Lite-C expects less parameter data than sizeof LargeStruct, it will not free all memory. I don't know enough about Lite-C internals and general compiler architecture to tell if this is a valid concern or if the memory will be freed anyway.

I hope it is understandable now. And I hope it is possible with the LargeStruct trick. Otherwise I'll have to start learning assembler.

One assembler related question: do the instruction sets among all computers that run gamestudio differ, or do all intel and amd cpus understand the same assembler?
Posted By: jcl

Re: Memory allignment of parameters in Lite-C - 06/15/07 06:28

Don't worry, there is no memory allocated. The stack is just a continuous memory area that is reserved for passing parameters. Setting the stack pointer back to its correct position is the task of the calling function when using a __cdecl function declaration (which is the default).
Posted By: Excessus

Re: Memory allignment of parameters in Lite-C - 06/15/07 07:32

Quote:

Setting the stack pointer back to its correct position is the task of the calling function when using a __cdecl function declaration



Thanks! That's exactly the information I needed.
© 2024 lite-C Forums