4

Is there any way to make this code shorter?

long call_f(int argc, long *argv) {
  switch (argc) {
    case 0:
      return f();
      break;
    case 1:
      return f(argv[0]);
      break;
    case 2:
      return f(argv[0], argv[1]);
      break;
    case 3:
      return f(argv[0], argv[1], argv[2]);
      break;
    case 4:
      return f(argv[0], argv[1], argv[2], argv[3]);
      break;
    // ...
  }
  return -1;
}
8
  • why can't you pass the arguments to 'f' ? Commented Jul 23, 2010 at 18:10
  • Because the function that calls call_f does not pass a constant value for argc. Commented Jul 23, 2010 at 18:13
  • i mean both argc and argv... do you need that wrapper function? Commented Jul 23, 2010 at 18:15
  • Is f a function you have written, and therefore can change, or is f a function someone else has written, so you must pass variadic arguments? I thought you wouldn't ask the question unless you couldn't change f. Commented Jul 23, 2010 at 18:24
  • 3
    Note that your code looks unsafe (how will f know what the last argument is). You probably should be calling f(NULL), f(argv[0], NULL), etc. Commented Jul 23, 2010 at 18:26

6 Answers 6

3

No, there isn't any good way to do this. See here: http://c-faq.com/varargs/handoff.html

You can write a macro with token pasting to hide this behavior but that macro will be no simpler than this code, thus it's only worth writing if you have multiple functions like f() where you would otherwise have to duplicate this case statement.

Sign up to request clarification or add additional context in comments.

Comments

3

I don't know how you can make your code shorter but I saw this line in your code:

 return f();

From the next calls to f function, it seems that f is a function that takes variable number of arguments.

You can read in wikipedia that:

Variadic functions must have at least one named parameter, so, for instance,

char *wrong(...);

is not allowed in C.

Based on that, maybe the return f(); statement is causing you trouble?

2 Comments

Good eye. +1 vote. Could OP be using C++ overloaded functions, and not variadics as we both seem to have guessed?
No, this is just example code. In the real code, there is no case 0:.
1

There's actually a method to call a function at run-time if you know its calling convention and which parameters it receives. This however lies out of the standard C/C++ language scope.

For x86 assembler:

Assuming the following:

  1. You know to prepare all the parameters for your function in a solid buffer, exactly in the manner they'd be packed on the stack.
  2. Your function doesn't take/return C++ objects by value.

You may use then the following function:

int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
{
    // Reserve the space on the stack
    // This is equivalent (in some sense) to 'push' all the parameters into the stack.
    // NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
    // care of ensuring all the consumed memory pages are accessible
    _alloca(nSizeParams);

    // Obtain the stack top pointer
    char* pStack;
    _asm {
        mov pStack, esp
    };

    // Copy all the parameters into the stack
    // NOTE: Don't use the memcpy function. Because the call to it
    // will overwrite the stack (which we're currently building)
    for (size_t i = 0; i < nSizeParams; i++)
        pStack[i] = ((char*) pParams)[i];

    // Call your function
    int retVal;
    _asm {
        call pfn
        // Most of the calling conventions return the value of the function (if anything is returned)
        // in EAX register
        mov retVal, eax
    };

    return retVal;
}

You may need to adjust this function, depending on the calling convention used

Comments

1

I'll post here the same answer as I posted at the duplicated question, but you should take a look at the discussion there:

What is libffi?

Some programs may not know at the time of compilation what arguments are to be passed to a function. For instance, an interpreter may be told at run-time about the number and types of arguments used to call a given function. ‘libffi’ can be used in such programs to provide a bridge from the interpreter program to compiled code.

The ‘libffi’ library provides a portable, high level programming interface to various calling conventions. This allows a programmer to call any function specified by a call interface description at run time.

FFI stands for Foreign Function Interface. A foreign function interface is the popular name for the interface that allows code written in one language to call code written in another language. The ‘libffi’ library really only provides the lowest, machine dependent layer of a fully featured foreign function interface. A layer must exist above ‘libffi’ that handles type conversions for values passed between the two languages.

‘libffi’ assumes that you have a pointer to the function you wish to call and that you know the number and types of arguments to pass it, as well as the return type of the function.


Historic background

libffi, originally developed by Anthony Green (SO user: anthony-green), was inspired by the Gencall library from Silicon Graphics. Gencall was developed by Gianni Mariani, then employed by SGI, for the purpose of allowing calls to functions by address and creating a call frame for the particular calling convention. Anthony Green refined the idea and extended it to other architectures and calling conventions and open sourcing libffi.


Calling pow with libffi

#include <stdio.h>
#include <math.h>
#include <ffi.h>

int main()
{
  ffi_cif     call_interface;
  ffi_type    *ret_type;
  ffi_type    *arg_types[2];

  /* pow signature */
  ret_type = &ffi_type_double;
  arg_types[0] = &ffi_type_double;
  arg_types[1] = &ffi_type_double;

  /* prepare pow function call interface */
  if (ffi_prep_cif(&call_interface, FFI_DEFAULT_ABI, 2, ret_type, arg_types) == FFI_OK)
  {
    void *arg_values[2];
    double x, y, z;

    /* z stores the return */
    z = 0;

    /* arg_values elements point to actual arguments */
    arg_values[0] = &x;
    arg_values[1] = &y;

    x = 2;
    y = 3;

    /* call pow */
    ffi_call(&call_interface, FFI_FN(pow), &z, arg_values);

    /* 2^3=8 */
    printf("%.0f^%.0f=%.0f\n", x, y, z);
  }

  return 0;
}

I think I can assert libffi is a portable way to do what I asked, contrary to Antti Haapala's assertion that there isn't such a way. If we can't call libffi a portable technology, given how far it's ported/implemented across compilers and architectures, and which interface complies with C standard, we too can't call C, or anything, portable.

Information and history extracted from:

https://github.com/atgreen/libffi/blob/master/doc/libffi.info

http://en.wikipedia.org/wiki/Libffi

Comments

0

You can check out my answer to:

Best Way to Store a va_list for Later Use in C/C++

Which seems to work, yet scare people. It's not guaranteed cross-platform or portable, but it seems to be workable on a couple of platforms, at least. ;)

1 Comment

You would have to adapt my method for syscall, but it could be done using a static array of strings which specify the syscall arguments. If you are pen testing, my method of constructing the argument buffer will work well for you. Just bitblt it from the heap to the stack and jump.
0

Does f have to accept a variable number of pointers to long? Can you rewrite it to accept an array and a count?

8 Comments

answering with a question? i would go with TMN
@fazo: What does TMN's answer mean, exactly?
No, f is syscall. I didn't include that in the original question because I thought it might make the question complicated.
there was an answer ny TMN.. use stdarg.h
@fazo, use it how, specifically? Call a variant of the syscall which consumes a va_list? Does that exist?
|

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.