PDA

View Full Version : Cross-language natives


imported_malex
10-14-2004, 16:03
Since sourcemod aims to support writing plugins in multiple languages, it would be very useful to be able to call functions defined in any plugin, written in any language, from any other plugin, written in any other language. This should be part of the core, so plugin writers wouldn't have to worry about it.

Currently, with amxmodx, a C++ module cannot call functions in other C++ modules, nor can it easily call Small functions. This means C++ modules can't access authentication information, or mysql, or any of the other nifty things provided by other modules and plugins.

Now, I didn't say this was easy, or possible, but it would be a very desirable feature.

\malex\

BAILOPAN
10-14-2004, 16:33
You can only call public functions, so that rules out many things.

The way MetaEng works is by keeping a table of function names to pointers, so anything that is registered to MetaEng is available to anything else. The way this is cool is that if the lookup and addition functions are propogated, then plugins can contribute back into the main pool.

However, it gets iffy. If say an AMX plugin wants to call a random function in a Perl script, it's not possible without an interface that connects the two. To help this interface, there is a very very simple class in MetaEng called CForward that basically pushes references into the scripting engines... the only problem is that not every language has a definitive way of pushing parameters.

For going from *->AMX is not difficult at all, writing forwards for AMX is trivial. But to C/C++ it's not since parameters can't nicely pushed randomly.

Example, say you have this (example):

$f = call_function($engId, $plugId);
call_push_int($f, $param1);
call_push_int($f, $param2);
call_push_float($f, $param3);
call_push_end($f);


The perl engine would build a generic block of data describing the forward, The MetaEng would get this block and pass it on where it's going. But the engine in question must have a way to decode that block generically. This is not possible in C. The block of data looks like this:
void *blkParams[PARAMS*2+1]
blkParams[0] = numParams
blkParams[i+1] = pointer to parameter
blkParams[i+2] = has the param type

So the AMX engine can easily say "oh, just build a new block of data based on this, then call amx_Exec()". In C, a specific function has to be rewritten since you can't variably push C arguments without assembly.

So I guess what I'm saying is this will be TRIVIAL to implement, but in practice unless you know what you're doing you will be able to easily crash C plugins, if you call a function in a C plugin and don't know what you're doing. Public functions in C will have to be wrapped, like this:

METAENG_API int call_forward(void *blk)
{
actual_call(*(int *)(blk[1]));
}

actual_call(int a)
{
//....
}


It's messy, yeah. I plan on making some nice macros for the block stuff but I don't see another way to do this ;\

Well, I could do it in assembly which would actually be way easier at this rate. Lemme think about it :D

BAILOPAN
10-14-2004, 17:15
Edit, actually, Ithink I can implement this in assembly, it won't be too hard.

I just have to walk the parameter list in reverse, push the argument references, make the call, then clean the stack. So *raises glass of wine* here's to interoperability.

imported_malex
10-14-2004, 17:16
Assembly is bad. AMD64?

\malex\

Vapok
10-14-2004, 17:20
This is interesting...

At some point though, there would have to be a limit at how many different languages you could support.. wouldn't there??

BAILOPAN
10-14-2004, 17:24
Yes, the limit would be once there are no more languages :)

Addnig language support is not difficult just tedious, as you have to write the interface wrappers.

AMD64 assembly is just as easy as x86. i.e. it's terribly messy but it would essentially be hte same thing, just using the new registers.

I wrote a bit of x86-64 trying to port the ASM implementation of AMX (not the JIT - I'll leave that to dJeyL, assuming he's still working on it).

Vapok
10-14-2004, 18:14
We've run into a problem with the JIT on some Glibc 2.3 Linux systems....

So.. we're having to deal with that. I'm sure he's still working on it.

BAILOPAN
10-14-2004, 20:41
There are a bunch of problems with the JIT, oddly enough. I think it has to do with the kernel more than the actual code, but the JIT is messy itself as it modifies and runs its own executable code memory (very bad in linux!).

Anyway other things factor in like grsecurity and pax, which disallow running code in the data section of ELF... although even chpax/paxctl don't always work.

The solution is simply to not use the JIT for everything, but if we could find exactly what was causing the segfaults, it would be possible to detect it ahead of time.

It's best to always link same code builds, mixing glibc2-2 and glibc2-3 shared modules is a very bad idea. Maybe that could be part of it.

Anyway HL2 will only work on gcc3 (because certain things changed, like vtable formats) so when HL2 comes out, we're done with 2.2 and gcc 2.95 :)

Vapok
10-14-2004, 23:45
Anyway HL2 will only work on gcc3 (because certain things changed, like vtable formats) so when HL2 comes out, we're done with 2.2 and gcc 2.95 :)

A long time coming.. that's for sure... I was certain that we were stuck in the past. :)

FullThrottle
10-15-2004, 01:07
Sorry to run off topic but:

BAILOPAN actually to help narrow down the problem I've found out that everyone I've talked to having the JIT seg fault problem with .20 are running kernel 2.6.8

Vapok
10-15-2004, 03:06
That was also one of our findings.. going back to 2.6.5 resolved part of the issue.

FullThrottle
10-15-2004, 12:12
I would but I'm still a little new when it comes down to compiling the kernel and since our box is outsourced I don't know how to recompile the kernel without reinstalling the entire system which I can't do since the box is 2000+ miles away :(