Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Passing parameters to C++ functions manually by pushing them onto the stack

Credits go to CascoOscuro, Evert, gillius, Jonatan Hedborg, Kitty Cat, Korval, and Oscar Giner for helping out!
This thread is locked; no one can reply to it. rss feed Print
Passing parameters to C++ functions manually by pushing them onto the stack
Mr. Xboxor
Member #3,763
August 2003
avatar

Ok, so let's say that in my program I store the certain parameter types that a particular function takes. Using this information, and a pointer to data that I want to pass to a function, I want to push the data onto stacks/put it into registers manually through inline assembly code, and read the return value of the function.

An example:

1 
2int Blah(int p1,int p2)
3{
4 return p1+p2;
5}
6 
7void main()
8{
9 string function="Blah";
10 void (*blah_pointer)()=Blah;
11 int p1,p2;
12 if (function=="Blah")
13 {
14 //assembly code that pushes p1 and p2 onto stack correctly
15 blah_pointer();
16 //assembly code that reads the return value correctly
17 }
18}

I'm afraid this is a very difficult question, so I thankyou ahead of time for any time you spend on it. Basically I want to know what the parameter passing conventions are for the various basic data types, and how they apply to classes, pointers, and the like. Oh, and how to read the return value the function returns. I'm guessing it may be compiler specific, but that is ok. I'm using the mingw port of gcc. Any information really would be useful.

Thanks for any help.

Chris Mueller

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." -Mr. Bunny

Jonatan Hedborg
Member #4,886
July 2004
avatar

I have no idea, but im sure your disassembler of choice will answer that question ;)

Evert
Member #794
November 2000
avatar

Quote:

Basically I want to know what the parameter passing conventions are for the various basic data types, and how they apply to classes, pointers, and the like.

As I recall, parameters are pushed onto the stack right to left, but you can check that easily enough by looking at the assembler output.

Quote:

Oh, and how to read the return value the function returns.

I'm not sure if this is still true, but it used to be true that integers were returned in AL/AX/EAX for char/short/int. Pointers are returned in DS:EDI (I think - might be DS:ESI).

Quote:

I'm using the mingw port of gcc. Any information really would be useful.

Take note of the -s compiler switch: it will make the compiler spit out the generated assembler source code.

Korval
Member #1,538
September 2001
avatar

Quote:

Basically I want to know what the parameter passing conventions are for the various basic data types, and how they apply to classes, pointers, and the like.

No, you do not.

I feel like we've had this conversation before, but here goes.

This changes from processor to processor. Your code will, almost certainly not work running natively in 64-bit mode on x86-64 chips (since they have an additional 8 registers), and any code that does will almost certainly not do it on 32-bit chips. And, without question, you can forget about running it on Mac's or anything that isn't an x86 chip. There is no singular hard-and-fast rule for this. And, as you have pointed out, it can even vary from compiler to compiler.

One of C's primary (initial) functions was to abstract away this kind of thing so that we wouldn't need it. I'm pretty sure that the answer to the obvious question of "Why are you needing to do this?" is going to involve some attempt to call a function of runtime-defined arguments in a fashion that C was not designed to handle.

Think of it this way. Microsoft, knowing full well that COM was only going to be used on x86 machines, could have had macros pushing and popping arguments on and off the stack. However, they do not. They make you call IDispatch interfaces via passing an array of arguments and a "pointer" for a return value. This solution, while bothersome, works, and works guarenteeably and without question. What you're trying to do does not.

Quote:

Take note of the -s compiler switch: it will make the compiler spit out the generated assembler source code.

Will the assembly actually have the argument pushing/popping in it? I seem to recall that, on some GCC's I've used, the assembler that they output is somewhat... incomplete regarding these kinds of details. It expects the linker to fill in the blanks (which makes sense, as it allows libraries built by other compilers to be used by GCC, and vice-versa).

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

One of C's primary (initial) functions was to abstract away this kind of thing so that we wouldn't need it.

C also allows you to have an indeterminate arguments, whereas C++ doesn't. He could just define the function as int somefunc(myargs), declare it as int somefunc(), and just pass the arguments as if it's expecting it. It works with function pointers, too.

1extern "C" {
2 typedef struct Something {
3 int (*func)();
4 int val;
5 } Something;
6 Something *thing;
7}
8 
9void somefunc(Something *this, int a)
10{
11 this->val = a;
12}
13 
14int someotherfunc(Something *this)
15{
16 return this->val;
17}
18 
19// then
20thing->func = somefunc; // might need to cast
21thing->func(thing, 1000);
22thing->func = someotherfunc; // might need to cast, too
23if(thing->func(thing) == 0)
24 exit(-1);

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

CascoOscuro
Member #4,966
August 2004
avatar

Quote:

C also allows you to have an indeterminate arguments, whereas C++ doesn't.

It is not true. In C++ as C you can put an indeterminated number of arguments (for example, printf(const char* str, ...)) and use them with the cstdarg macros.
I think you are talking about the difference with function(void) and function(). In C function() is different than C++, where you are telling the compiler that the function has no parameters; in C to do this you must use void.

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

It is not true. In C++ as C you can put an indeterminated number of arguments (for example, printf(const char* str, ...)) and use them with the cstdarg macros.

If you look at my example though, that's not quite what it does (ie. no va_args.. just some prototype and casting trickery). Although it might still work with (...) in place of (), but the compiler may not be bound to put those extra arguments on the stack like normal arguments.. I don't know.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Oscar Giner
Member #2,207
April 2002
avatar

func(int a, ...) is variable number of arguments (but at least 1 argument is mandatory), which is different from func(), indeterminate number of variables (but the number of arguments taken by the real function is fixed, just unspecified in the declaration) :)

Quote:

Take note of the -s compiler switch: it will make the compiler spit out the generated assembler source code.

It's -S (uppercase)

CascoOscuro
Member #4,966
August 2004
avatar

Quote:

is different from func(), indeterminate number of variables

In pure C. In C++ is like func(void), so you must use in C++ func(...).

About your idea of use manually the stack, use the asm{} instruction.
First of all you must set the ebp register to be the "pointer" to the stack parameters and the local and non-static variables:

//I'm using gcc assembler notation
asm{
  mov %esp,%ebp
  subl %esp, BYTES_FOR_YOUR_LOCAL_VARIABLE
  
  #Operations which you want...
  
  addl %esp,   BYTES_FOR_YOUR_LOCAL_VARIABLE
  mov %ebp,%esp
}

Altough you can simulate the stack with a STL stack modifier.

Oscar Giner
Member #2,207
April 2002
avatar

func(...) is not equivalent to C's func(). Actually, func(...) is quite useless since there's no standard way to access the parameters (va* functions require at least one parameter).

See this:

1int f();
2 
3int main()
4{
5 int r = f(23, 45);
6 printf("%d\n", r));
7 
8/*
9 this also compiles (and works, doesn't crash or does anything weird,
10 just the last 4 parameters are useless).
11*/
12 r = f(2, 5, 67, 3, 9, 12);
13 printf("%d\n", r));
14 
15/*
16 this also compiles, but will give you problems ar runtime.
17*/
18 r = f(2);
19 printf("%d\n", r));
20 
21 return 0;
22}
23 
24// in some place, probably in another file:
25int f(int a, int b)
26{
27 return a + b;
28}

This is valid C code. There's no way to do this using C++. Declaring f as int f(...) (and keeping its definition as int f(int a, int b)) will give you a link error.

C++ had to remove this feature to allow function overloading.

Kitty Cat
Member #2,815
October 2002
avatar

Would you be able to do this?

struct someStruct { // or class
   extern "C" int (*funcptr)();
}

You should be able to attach a normal C++ function to a C function pointer.. the only difference between the two is name mangling which should be fine as long as you play by the compiler's rules. You might need to make the whole struct a C-style struct (as in my previous example), or maybe just that one member (as here) if the compiler allows that.

But as long as the function pointer has () and uses the C mangling scheme, you should be able to attach any C or C++ function to it (class member functions can be tricky and may not be worth it.. just stic to normal C++ or C functions), and be able to pass any number of arbitrary arguments to the function pointer.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Mr. Xboxor
Member #3,763
August 2003
avatar

Yes, I have brought this up before, so sorry for the repitition.

First off, I'm not worried about compiler or hardware specific issues. I wasn't planning on making the program multi-platform or anything like that.

Secondly, inside the program, when I wish to call a function, all I will have is a pointer to the data inside of the program. I won't know at compile time how the parameters should be passed, but I will at runtime. Basically, at runtime, inside of the program, I will have a pointer to the data I want to pass to the function, I will know how many bytes each parameter consists of, and I will even know the type of the parameter. But I will not know this at compile time, so it rules out the use of C function pointers or whatever. I will take a look at the assembly code output though, thanks for that suggestion.

Chris Mueller

[edit]
Ok, I've taken a look at the assembly output, and it seems that there is no general form that it follows, so it would be extremely difficult to push parameters by hand. Thankyou for your guys' help, but I consider this topic finished now. Thanks for your efforts.
[/edit]

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." -Mr. Bunny

gillius
Member #119
April 2000

Use functors to do what you are wanting to do, and store the parameters in the functor object.

Gillius
Gillius's Programming -- https://gillius.org/

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

Secondly, inside the program, when I wish to call a function, all I will have is a pointer to the data inside of the program.

Why not make the functions take a void* argument to that data, and have the beginning of the function extract it however it needs?

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Mr. Xboxor
Member #3,763
August 2003
avatar

Ah, when I posted this question last time I got the same responses. What I'm trying to do is extremely difficult, and platform dependent, so I'm giving up.

I didn't want to have to program a function any differently. Meaning, I wanted to take a function I had already written, and just call it by passing parameters to it manually, but that's too hard.

Sorry for any inconvenience.

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." -Mr. Bunny

Billybob
Member #3,136
January 2003

This is FAR from compiler dependant. If it was, how would programs call DLL functions?
And the convention is always the same as long there is no optimisation. When you optimise the compiler might very well get rid of some arguments or use registers, but only when that applies (i.e. not for exported DLL functions).

On Windows, at least, the convention is to push arguments right to left, and then pop them left to right in the function.
Now, there is a slight difference when dealing with complex things like structs and classes. When dealing with those I'm assuming a pointer is passed and then the copy constructor is used inside the function.

I really don't see any reason pushing arguments yourself is seen as bad. The compiler does it anyway! You may not end up cross-platform, but then again maybe you will! I see no reason for Linux to push and pop arguments differently, though it's certainly possible.

Correct me if I'm horribly wrong!

EDIT: And yes, the ASM produced by GCC contains pushing and poping.

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

If it was, how would programs call DLL functions?

Function pointers with known paramters/return types. You can get around unknown paramters by using a C function pointer with indeterminate paramters (ie. (*funcptr)()), but he only has the stack data in memory and doesn't know what it is (only the function that's being called does). He should probably rethink his idea since this is completely looking like hack-ville.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Bob
Free Market Evangelist
September 2000
avatar

Quote:

If it was, how would programs call DLL functions?

DLL interface to C functions is well defined. It's independent of language or compiler. Try using C++ classes across DLLs and compilers.

Quote:

The compiler does it anyway!

The compiler is not required to. One can implement a fully-compliant C compiler without a stack.

--
- Bob
[ -- All my signature links are 404 -- ]

Go to: