1

I am trying to call function pointer stored as a uint64_t value and pass arguments whose address are stored in a structure of uint64_t. Here is the code.

double sum(double *a, long len){
    double tmp = 0;
    for(size_t i=0;i<len;i++){
        tmp += a[i];
    }
    return tmp;
}

int main(){
    long size = 1000;
    double *a = new double[size];

    for(int i=0;i<size;i++){
        a[i] = i*1.0;
    }

    struct __attribute__ ((aligned(16))) args_t {
        double *a_func;
        long len_func;
    } args;

    args.a_func = a;
    args.len_func = size;

    double ret;

    uint64_t arg_ptr = reinterpret_cast<uint64_t>(&args);
    uint64_t arg_size = sizeof(args);
    uint64_t func_ptr = reinterpret_cast<uint64_t>(&sum);
    uint64_t func_ret = reinterpret_cast<uint64_t>(&ret);
// How should I call the function with arguments passed to it and get a return value?

}

Moto: I am trying to build a library which takes any function pointer, its arguments and return address where it executes the function and returns the value through return address. Thank you! :)

15
  • Why are you trying to store a function pointer in a uint64_t? Why not just declare an actual pointer to the function? Commented Nov 12, 2015 at 19:43
  • @CareyGregory: I am trying to build a library which takes any function pointer and hooks the data to its arguments. Is there anyother way to do this? Commented Nov 12, 2015 at 19:44
  • Then use a void * as Jonathan Howard suggested. At least that will guarantee the element has the correct size and alignment to store pointers. A uint64_t offers no such guarantee. So your code would become void *func_ptr = reinterpret_cast<void *>(&sum); Commented Nov 12, 2015 at 19:51
  • @CareyGregory: How to call from func_ptr? Lets say sum is a void return and void arguments function. Commented Nov 12, 2015 at 20:02
  • 1
    If you use C++, learn at least C++11 and use lambdas and std::function Commented Nov 12, 2015 at 20:28

3 Answers 3

1

Try this. You're missing a function pointer declaration in your struct.

Also, your func_ptr and func_ret calls are not typed correctly. I wouldn't hold pointer types in a uint64_t, I suggest using a void * as it will be the correct length for your architecture. You definitely shouldn't hold a function pointer in a uint64_t as there is no guaranteed by the standard that a sizeof(funcptr) == sizeof(void *).

double sum( double * a, long len )
{
    //...
    return 0.0;
}

int main(int argc, char const *argv[])
{
    long size = 1000;
    double * a = new double[size];

    struct args_t
    {
        /** you need a ptr declaration */
        double (*func_hsa)(double *, long );
        double * a_hsa;
        long len_hsa;
    } args;

    args.func_hsa = sum;
    args.a_hsa = a;
    args.len_hsa = size;

    double ret;

    ret = args.func_hsa(args.a_hsa, args.len_hsa);

    return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

The structure should contain only args not function pointer with its signature. Can you edit your code according to the question?
@Fr34K, Where are you saving your pointer then? The original example is invalid, because you're trying to cast the address of a struct to a function pointer, which will result in a hardware trap or undefined behavior when called.
Yes. But the location of the function is present in a variable from which you can access it.
Which variable? It doesn't seem to exist in the example you posted. Please post a minimal, complete, and verifiable example.
The variable is func_ptr
1

Moto: I am trying to build a library which takes any function pointer, its arguments and return address where it executes the function and returns the value through return address.

You need to know, at least dynamically at runtime, the signature of the called function. Since various application binary interface (ABI) conventions dictate different and incompatible calling conventions (e.g. a double argument is passed in floating point register, but a int value is passed in some integer register, and this is processor & ABI specific and followed by compilers and linkers, etc...), you should use a specific library for that (which contains some processor & ABI specific assembler code). The libffi is such a library and you should use it. If you can't use it, study the ABI of your implementation, e.g. this for Linux/x86-64 ABI.

I am trying to call function pointer stored as a uint64_t value and pass arguments whose address are stored in a structure of uint64_t.

You cannot do that (without using something like the libffi ...) in a portable way (i.e. you need some processor&ABI specific code, perhaps in assembler). You are implicitly supposing that arguments are passed on some stack, or at least thru addressable memory, and this is generally wrong, and is wrong for most x86-64 systems: most arguments are often passed in processor registers (and details are processor and ABI specific, so are different on Windows and on Linux).

The issue is not the function pointer, it is the calling convention and the arguments passing (and result passing).

BTW, in C and C++ the integral type castable to a pointer is intptr_t (from <stdint.h> in C, from <cstdint> in C++); using (and casting function pointers back and forth from/to) an int64_t on 32 bits architectures like ARM & x86 is not appropriate.

As I commented, if you can afford using C++11 at least, closures (i.e. lambda functions) and std::function are very helpful. Read also about callbacks.

See also GCC builtins for constructing function calls.

1 Comment

I would give this a second best answer.
0

So, Finally I got it working.

double sum(double *a, long len){
    double tmp = 0;
    for(size_t i=0;i<len;i++){
        tmp += a[i];
    }
    return tmp;
}

int main(){
    long size = 1000;
    double *a = new double[size];

    for(int i=0;i<size;i++){
        a[i] = i*1.0;
    }

    struct __attribute__ ((aligned(16))) args_t {
        double *a_func;
        long len_func;
    } args;

    args.a_func = a;
    args.len_func = size;

    double ret;

    uint64_t arg_ptr = reinterpret_cast<uint64_t>(&args);
    uint64_t arg_size = sizeof(args);
    uint64_t func_ptr = reinterpret_cast<uint64_t>(&sum);
    uint64_t func_ret = reinterpret_cast<uint64_t>(&ret);

    // We are trying to make a generic function caller
    size_t num_args = arg_size/8; // as elements of struct are 64bit long
    uint64_t *varg = reinterpret_cast<uint64_t*>(arg_ptr);
    switch(num_args){
        case 2:
            {
                void* (*fun)(void*, void*) = reinterpret_cast<void* (*)(void*, void*)>(func_ptr);
                uint64_t *ret_add = reinterpret_cast<uint64_t*>(func_ret);
                *ret_add = reinterpret_cast<uint64_t>(fun((void*)varg[0], (void*)varg[1]));
                break;
            }
        default:
            break;
    }
}

Using switch case is not a proper way but there are not better ways to structure unrolling without using any packages or going to a latest standard. Although C++14 has a feature for passing tuple as argument list, it is not fully implemented. The switch cases can be extended to multiple arguments.

2 Comments

libffi is doing likewise, but much more generally. Actually, you miss the distinction between double and long or void* arguments, which are transmitted differently (most processors have floating point registers which are different of general purpose integer/pointer registers). How would you extend your scheme to handle a long(*fun)(long,double) function pointer? You'll probably end up with a very huge switch (it is not only the number of arguments which matters, but also their type)
I am casting everything to uint64_t. If you look at the code, everything is operated on uint64_t. I'll look into libffi. Seemed like a good library (hence I upvoted your answer).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.