Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function addresses #1

Open
X-Ryl669 opened this issue Jul 6, 2022 · 1 comment
Open

Function addresses #1

X-Ryl669 opened this issue Jul 6, 2022 · 1 comment

Comments

@X-Ryl669
Copy link

X-Ryl669 commented Jul 6, 2022

I've seen you've listed function addresses as a todo item in your list.
What do you mean by this ?

I'd like to make a an automated REPL system for esp-idf (large) api in order to be able to call the functions declared in the API, so I'm trying to make use of crefl for this.

Ideally, I need to link the function's name to their addresses in the final binary, so I can create a function pointer and jump to them.
I've though of doing this this way:

  1. Make a db of all the API I'm interested in via enumerating all header files and clang calling crefl on each header.
  2. Have a tool that open the DB and write pseudo dynamic linker code, in pseudo code like this:
for(type in db.types) {
   if (type == function) {
      set #functionname# = type.name
      print_in_validation_code("bool validate_call_#functionname#(user_input) { todo }")
      print_in_mapping_code(header_file_for_function, "call_table_entry{ "#functionname#", &#functionname#, type.args.count, type.signature },")
   }
}
  1. Once the tool is run, it's building multiple c source file (one for validating the function call, that is checking the number of argument and the argument types, the other is just a map from function name to the actual function's pointer
  2. Compile the binding code
  3. Use the declared entries in my REPL's code to be able to link what's found in header to what's callable

Right now, this idea should work without crepl listing function addresses since taking the function address is done by the compiler, but it has numerous drawbacks:

  1. To call the function, I need to implement a typedef for the function signature, and a wrapper that's unpacking argument's array to the function signature itself (that can probably be automatically written, but it's a pain).
  2. This requires an extra step in the build process (can't be runtime based), and needs to store all the reflection information in tables in the binary
  3. Can't take address of a static function (it won't be the same as the include in the binding code will generate its own version of the function). Might probably fail linking too, I don't know.

If crefl had the function address in the database (needs to be added at a later step, since these address are only accessible/valid once the object is linked, and compilation and linking will likely not use clang), then we won't need to store all the reflection in the binary, and it could be loaded at runtime. Also a mode to create a struct's memory at runtime would be very useful.

@michaeljclark
Copy link
Owner

yes, the intention is to add function addresses but I didn't get up to it yet. that's why it doesn't have a check-mark.

the idea is that you can query the address of a function and then pack arguments to it so that you can invoke it dynamically. it needs a bridge between crefl to something like libffi. the resulting code will be much simpler than libffi is currently because the type signature does not need to be built up by hand. I have some code for this but it's not yet cleaned up enough to commit.

instead of calling ffi_prep_cif with a manually built-up interface composed with struct ffi_cif, one might have:

struct crefl_ffi_cif; // crefl equivalent to ffi_cif in libffi
int crefl_ffi_prep(crefl_ffi_cif *cif, decl_ref r);
void crefl_ffi_call(crefl_ffi_cif *cif, void (*fn)(void), void *arg_result, void **arg_values);

where one passes the decl_ref to the function, to have the call information structure automatically populated.

we have a metadata merge step with crefltool, which is our version of a link, and from that we generate an array containing the runtime metadata that is linked into the final binary. at that stage, before the final link, we could also (optionally) create an array that takes the addresses of functions and global variables. I say optionally because taking the address of objects may change the code that is generated.

the resulting ffi code might look like something like this code below. considerably simpler than libffi. note there is a challenge to lookup up a function declaration because we essentially need to parse an unambiguous function signature. what I have below is sufficient for C but does not handle type overloading. i'd actually like to be able to expose a C API to to some basic C++ metadata but that would require a more sophisticated way to get a reference to a function or member function than simply using its mangled name. ideally it might look a bit like a Rust function decl. e.g. function f(int) -> int;

the crefl_type function here is actually a macro that stringifies its argument and that is then used at runtime.

#include <stdio.h>

#include "ffi.h"

struct point { float x; float y; };
struct index { unsigned a, b, c; };
struct moon { struct point p; struct index s; };

int f(struct moon m, int x)
{
    int y =  x + m.s.a + m.s.b + m.s.c;
    printf("f( { { %f, %f }, { %u, %u, %u } }, %d) -> %d\n",
        m.p.x, m.p.y, m.s.a, m.s.b, m.s.c, x, y);
    return y;
}

void call_f_moon_ffi()
{
    decl_db *db = crefl_db_internal();

    int ret;
    crefl_ffi_cif cif;
    if ((ret = crefl_ffi_prep(&cif, crefl_type(db, function f))) != FFI_OK) {
        panic("error: crefl_ffi_prep: ret=%d\n", ret);        
    }

    struct moon m = { { 1.0f, 2.0f }, { 3, 4, 5 } };
    int x = 6;

    void *arg_values[2] = { &m, &x };
    crefl_ffi_arg arg_result;
    crefl_ffi_call(&cif, (void (*)())&f, &arg_result, arg_values);
}

int main(int argc, const char **argv)
{
    call_f_moon_ffi();
}

let me know and I can share you some of this stuff out-of-band. as it's not quite at the level of committing to the repo yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants