20 Jan, 2012, plamzi wrote in the 1st comment:
Votes: 0
Question:

Has anyone used the symbol table in order to find and execute a function by its name? Reason I'm asking is I'd really like to house as many of the tables as I can inside a MySQL database, including stuff like the command interpreter structure and the special procedures assignment instructions, which link to functions.

In my research, using the symbol table seems to come up as the easiest (maybe only) way to get a function pointer via name, but I haven't come across a specific example of the kind I'll need. Was just curious if someone has gone down this path already and can let me know if it's worth pursuing further. Even better if you have some prt–porter code to share :)

http://www.gnu.org/software/libc/manual/...
20 Jan, 2012, plamzi wrote in the 2nd comment:
Votes: 0
Came across this link, which seems promising. It's using dlopen/dlsym. Some forums warn about the overhead but I'm already compiling my production server with -g3 and -rdynamic. Plus, the year is 2012. Still curious if anyone else has done this, though.
20 Jan, 2012, David Haley wrote in the 3rd comment:
Votes: 0
Sure, tons of people have used dlsym. Several SMAUG variants use it for command tables. Looking up functions dynamically is quite common (dynamic library binding, anybody…?).

It's also very easy to write your own mini-symbol-table with a script that generates a name-to-function-pointer mapping, and then have your own lookup functions if you want to avoid dlsym.
20 Jan, 2012, plamzi wrote in the 4th comment:
Votes: 0
David Haley said:
Sure, tons of people have used dlsym. Several SMAUG variants use it for command tables. Looking up functions dynamically is quite common (dynamic library binding, anybody…?).

It's also very easy to write your own mini-symbol-table with a script that generates a name-to-function-pointer mapping, and then have your own lookup functions if you want to avoid dlsym.


Found some uses in merc/SMAUG/LOP. Cheers.
25 Jan, 2012, plamzi wrote in the 5th comment:
Votes: 0
plamzi said:
David Haley said:
Sure, tons of people have used dlsym. Several SMAUG variants use it for command tables. Looking up functions dynamically is quite common (dynamic library binding, anybody…?).

It's also very easy to write your own mini-symbol-table with a script that generates a name-to-function-pointer mapping, and then have your own lookup functions if you want to avoid dlsym.


Found some uses in merc/SMAUG/LOP. Cheers.


I looked deeper and it seemed that the LOP version I had before me didn't do much, if anything, with dynamic library binding. I looked at some modern LPMud derivatives but it looked like they use it to a different effect. So then I tried to infer a minimal implementation from various C forums and came up with something pretty cool.

The first "discovery" was that once you compile your objects with "-fPIC" and your binary with "-rdynamic -ldl", you can already do:

#include <dlfcn.h>

#define ACMD(name) \
void (name)(struct char_data *ch, char *argument, int cmd, int subcmd)

typedef void (*acmd)(struct char_data *ch, char *argument, int cmd, int subcmd);

acmd command = (acmd) dlsym(0, "do_look");
if(command)
(*command) (ch, line, cmd, cmd_info[cmd].subcmd);


Normally, dlsym takes as first argument the name of a loaded shared library, but a 0 seems to make it search the loaded dynamic functions. This not very obvious use already satisfies my original requirements. I can now have a command table stored in the database, which maps user input "look" to a string "do_look", which then gets resolved to a function dynamically.

This trick will also be used to map special procedures to any entity, by user-readable function name, with no translation tables needed. All we'll need is one or more string slots.

But why stop there? I've added a few line to the "Makefile" to generate a shared object clone of the MUD binary. This object is then attached (or re-attached) during runtime.

[Makefile]
$(BINDIR)/circle : $(OBJFILES)
$(CC) -o $(BINDIR)/circle $(PROFILE) $(OBJFILES) $(LIBS)
$(CC) -shared -o $(BINDIR)/circle.so $(PROFILE) $(OBJFILES) $(LIBS)

[Binding]
circle_lib = dlopen("circle.so", RTLD_LAZY);

[Lookup]
acmd command = (acmd) dlsym(circle_lib, "do_look");


What this lets me do is "override" any function that I care to check for in the library first. For instance, I've already intercepted all user commands, so I can tweak a command, compile, overwrite, and re-attach circle.so, and the latest version of that command will come into effect without boot.

Of course, the lowest-hanging fruit are the functions that are widely used and follow a common template (in circle, those would be user commands, special procedures, manual spells). Beyond that, overriding functions that have a unique format of return type and arguments will probably happen only if really needed.

While I'm probably the person who cares least about rebooting the game, the relative simplicity of implementing this was so surprising to me that I thought it would be worth sharing.
25 Jan, 2012, JohnnyStarr wrote in the 6th comment:
Votes: 0
This is really weird, but nifty.
26 Jan, 2012, David Haley wrote in the 7th comment:
Votes: 0
It's not weird at all. It's very standard to implement functions in your core distributable and allow people to hook in dynamic libraries to override those functions. And doing that at runtime is how very many plugin systems work. It's really a very useful tool.
05 Mar, 2012, plamzi wrote in the 8th comment:
Votes: 0
I did some more research on this as I was interested in eventually being able to override any function I want without jumping through hoops. I thought I'd share some even niftier variadic macros I put together to accomplish this.

Apparently, there's a way to call a function from a dynamic library without knowing the exact arguments it can take. However, if you are interested in what it returns, you do have to know the type. The first macro can be used to call any function returning void and the second is an example of a variation that returns an int. You can add macros analogously for other return types.

/* Definition: */

typedef void* (*voidy)();

#define CALL(name, …)\
{ voidy ff;\
*(void**)(&ff) = dlsym(circle_lib, name);\
if (!ff) *(void**)(&ff) = dlsym(0, name);\
if (ff) ff(__VA_ARGS__); \
else log_printf("ERROR: Function %s not found in either lib or code.", name); }\

/* Examples: */

if (d->connected != CON_PLAYING) {
/* nanny(d, comm) */
CALL("nanny", d, comm);
}



if (!(pulse % PULSE_MOBILE)) {
/* mobile_activity() */
CALL("mobile_activity");
}


/* Definition: */

typedef int (*inty)();

#define RCALL(ret, name, …)\
{ inty ff;\
*(void**)(&ff) = dlsym(circle_lib, name);\
if (!ff) *(void**)(&ff) = dlsym(0, name);\
if (ff) ret = ff(__VA_ARGS__); \
else log_printf("ERROR: Function %s not found in either lib or code.", name); }\

/* Example: */

int r = 0;

if (!IS_NPC(ch)) {
if (GET_MOB_SHOP(k))
RCALL(r, "shop_keeper", ch, k, cmd, arg, SPEC_COMMAND);
if (!r && GET_MOB_QUEST(k))
RCALL(r, "questmaster", ch, k, cmd, arg, SPEC_COMMAND);
}

if (r) return r;


As we work on various parts of the code, we can convert function calls pretty easily to use the above macros. We are already able to update vast tracts of CircleMUD code at runtime. That's not something you normally get with Diku-rivatives. I realize that there are a number of "mud driver"-based codebases out there that use the same principle, but for anyone who is not shopping around for a codebase or prefers Diku-rivatives, this is a very quick (20-30 min. for you to intercept about 50% of the code), low-maintenance (see the Makefile change earlier in the thread) way of adding runtime update capabilities without bloating the code.
0.0/8