3

I am trying to write a program that will run a function from a dynamic library with its name, arguments and their types inputted by the user. I can get a void* pointer to this function using dlsym(func_name), however I do not know the types of arguments and their amount beforehand, so I can't dereference this pointer the normal way.

If I copy the byte representations of all the arguments into a char * array, can I call a function with this array as its arguments? For example when calling printf in assembly I can push the arguments and the format string to the stack and call printf. Maybe I can do something similar in C, or use assembly insertions?

For example, lets say the function is void func(int, double); and I am given arguments int a and double b. Then I would create an array:

char buf[sizeof(int) + sizeof(double)]
memcpy(buf, &a, sizeof(int))
memcpy(buf + sizeof(int), &b, sizeof(double))

And use it as the arguments pushed onto a stack.

The user inputs the name of the function, a string describing its type, and the arguments themselves. For example:

func_name
viid
5
6
2.34

func_name is the name of the function which will be used in dlsym. viid means that the function has void func_name(int, int, double) type (first char is the return type, the rest are arguments. v = void, i = int, d = double). The rest are arguments.

Roberto Caboni
  • 7,252
  • 10
  • 25
  • 39
Binabik
  • 33
  • 4
  • I believe it can be done somehow with casting. Try to send the arguments as char arrays and read it with casting to the actual types. This thing needs some trials until it succeeds. But if after you get the arguments you still don't know the types this can be a problem since you don't know how to read the arguments. Maybe you should send some more arguments that explain the types of the other arguments. – RafaelJan Apr 22 '20 at 22:07
  • Not necessarily only the size, but the type. Different types like FP may use a different stack. – chux - Reinstate Monica Apr 22 '20 at 22:08
  • 2
    I don't think you can really do this. Some variables are passed in registers, some are passed on the stack, and it depends on the data type. – Barmar Apr 22 '20 at 22:08
  • You may want to read this: [x86 calling conventions](https://en.wikipedia.org/wiki/X86_calling_conventions) – Andreas Wenzel Apr 22 '20 at 22:09
  • @RefaelJan the user also inputs a string that describes the types of arguments inputted, so that is not a problem. Can you please hint how can it be done with casting? Since I don't know the amount of arguments beforehand, I can't send the right amount of char arrays to the function. Only a fixed number of them, for example one – Binabik Apr 22 '20 at 22:11
  • @Binabik can you please give example of the user inputs? – RafaelJan Apr 22 '20 at 22:19
  • @RefaelJan added an example – Binabik Apr 22 '20 at 22:25
  • @Binabik - if I understand you correctly, you always get 3 arguments void*-the function, char*-for the types, char*-array of the arguments. And when you want to use it you know how many arguments you have from the number of types, and cast each argument to its type – RafaelJan Apr 22 '20 at 22:36
  • @RefaelJan I cannot change the function that I need to call, it is located in the dynamic library – Binabik Apr 22 '20 at 22:40
  • 1
    @OP Try `libffi` https://sourceware.org/libffi/ – Lorinczy Zsigmond Apr 23 '20 at 02:51

1 Answers1

2

No, except possibly in ABIs that pass arguments purely on the stack. Any computing environment has a specification of how arguments are passed. (This specification is part of an Application Binary Interface [ABI] specification for the platform.) The argument types and sizes affect whether they are passed in general registers, floating-point registers, or other locations, as well as how they are aligned. Few, if any, such specifications pass arguments as a raw dump of bytes to the stack.

Code can be written to decode a description of the arguments and construct the necessary state to call the function. Such code would need to be written at least partially in assembly or various language extensions. (I would expect some people may have already written such code and made it available but do not have references for such.) This is likely an excessively costly solution to implement for most situations; you should seek alternative ways to achieve your underlying goal.

If an ABI does specify that all arguments are put on the stack, then it might be possible to pass arbitrary arguments in a structure containing an array of unsigned char:

  • A maximum size must be determined, such as struct { unsigned char bytes[128]; }, because standard C does not support structures of variable size.
  • The structure must be aligned as required for whatever argument has the strictest alignment.
  • The bytes of all arguments must be properly aligned within the structure.
  • The function would be called by passing the structure itself as an argument. Take care that the ABI must say that even large structures are passed directly on the stack. Some might say that a pointer to (a copy of) the structure is passed.

Note that ABIs might include other details affecting function calls, such as saying that a certain register must contain the number of arguments or be set a certain way, even if it does not itself contain an argument.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • What if I limit the question to i386 Linux? According to this https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-i386-and-x86-6 the function calling convention is that all arguments are passed on stack in the reverse order – Binabik Apr 22 '20 at 22:16
  • 1
    @Binabil: Maybe. If all arguments are passed on the stack, passing an array of `char` would be different—C converts the array to a pointer and passes that. If the arguments were correctly positioned in a structure (with correct alignments) containing an array of `unsigned char`, such a kludge might work. But standard C does not have variable length structures, so you would need to set a maximum size. – Eric Postpischil Apr 22 '20 at 22:43
  • Lets say there is a maximum number of bytes that all the arguments together can take up. How would I pass such a structure to the function? Just a pointer to the struct would do? As in if struct Mystr x is my structure, I should call the function using ((void(*)(struct Mystr*))sym)(&x) ? (assuming the function return type is void) ? – Binabik Apr 22 '20 at 22:52