Page 1 of 2

use of .... (variable arguments to a function)

Posted: Tue Jun 24, 2008 3:13 pm
by suthers
ok I feel like a real newbie asking this question, but I have a reason... :oops:
I've already written complex programs that have had functions with varying amounts of input, but I've always had vectors to help me....
So for that reason I don't know how to use the ... operator in defining and passing args to a function....
So my questions are:
1) how do I use ... in the defining of a function?
2) How do I know how many args are passed to the function within the ....
3) How do I access the individual variables passed to the function through the ....
4) ASM wise how do I tell how many arguments are passed to the function (how and where are they pushed onto the stack?)
Yah in case you didn't guess I am re-writing the printf function for my OS so it can print variables too...
Thanks in avance,

Jules

P.S: Yes, The first few lines were an attempt to justify myself..., but all true I assure you..., please don't yell at me...

edit: Yah, I forgot to say this is in C by the way... :wink:

Re: use of ....

Posted: Tue Jun 24, 2008 4:10 pm
by iammisc
suthers wrote: 1) how do I use ... in the defining of a function?
Simple, you just put ... as the last argument of the arg list like for a simple printf:

Code: Select all

int printf(const char* fmt, ...);
Simple.
suthers wrote: 2) How do I know how many args are passed to the function within the ....
You don't that's why you either have a terminating argument such as a NULL or you have the caller tell you how many arguments are being passed. For example, printf knows how many arguments it is going to be receiving by parsing its format string.
suthers wrote: 3) How do I access the individual variables passed to the function through the ....
Use the va_* macros. For example(This code might not work or even be syntactically correct. Its only purpose is for you to get the basic gist of things):

Code: Select all

void using_var_args(int num_args, ...)
{
   va_list args;
   int i = 0;
   va_start(args, num); /* the first argument is a va_list. The second is the last non ... argument to a function */
   printf("the numbers passed are : ");
   for(;i < num_args;i++) {
       int cur = va_arg(args, int); /* the first argument is a va_list. The second is the type to return */
       printf("%d,", cur);
   }
   /* finally remember to call va_end */
  va_end(args);
}
suthers wrote: 4) ASM wise how do I tell how many arguments are passed to the function (how and where are they pushed onto the stack?)
I'm assuming this is for the i386 processor. Other processors are different, even x86_64 which you might assume to be like the i386 is different. Basically, you first need to know the stack offset at which your last argument is located. Then you'd have to use that info as the base for other arguments. For example, if all your arguments are integers and your last argument was the first argument, it would be at ebp offset 12. So, the next argument would be at ebp offset 16 and the one after that would be at 20 and the one after that at 24 and so on.

Hope that helps.

Look Here for more info:
http://wiki.osdev.org/Stack

and

http://www.cplusplus.com/reference/clib ... start.html
http://www.cplusplus.com/reference/clib ... a_arg.html
http://www.cplusplus.com/reference/clib ... a_end.html

Re: use of ....

Posted: Tue Jun 24, 2008 4:50 pm
by Combuster
I'm assuming this is for the i386 processor. Other processors are different, even x86_64 which you might assume to be like the i386 is different.
and varargs + assembly + x86_64 = Bad Idea (tm)

Re: use of ....

Posted: Tue Jun 24, 2008 4:59 pm
by suthers
What would be the best way to 'emulate' a va_list though...
would I use a void pointer and increment it by 4 to get the next arg and take (&the_last_arg_passed_to_printf_that's_non_...) + 4 as the address of the beginning o the list?
Thanks in advance,

Jules

P.S. I'll try to implement this way, but It might not work, so if anybody has any better suggestions please tell me... (or reassure me that this is a good idea/it will work would be nice too...)

Re: use of ....

Posted: Wed Jun 25, 2008 2:33 am
by JamesM
suthers wrote:What would be the best way to 'emulate' a va_list though...
would I use a void pointer and increment it by 4 to get the next arg and take (&the_last_arg_passed_to_printf_that's_non_...) + 4 as the address of the beginning o the list?
Thanks in advance,

Jules

P.S. I'll try to implement this way, but It might not work, so if anybody has any better suggestions please tell me... (or reassure me that this is a good idea/it will work would be nice too...)
You don't need to 'emulate' a va_list, just use it! It's a GCC builtin, not in the standard library. An example from my code:

Code: Select all

#include <stdarg.h>

...

void X86StackFrame::construct(ProcessorState &state,
                              uintptr_t returnAddress,
                              unsigned int nParams,
                              ...)
{
  // Obtain the stack pointer.
  uintptr_t *pStack = reinterpret_cast<uintptr_t*> (state.getStackPointer());
  
  // How many parameters do we need to push?
  // We push in reverse order but must iterate through the va_list in forward order,
  // so we decrement the stack pointer here.
  pStack -= nParams+1; // +1 for return address.
  uintptr_t *pStackLowWaterMark = pStack;
  
  *pStack++ = returnAddress;
  
  va_list list;
  va_start(list, nParams);
  
  for(int i = nParams-1; i >= 0; i--)
  {
    *pStack++ = va_arg(list, uintptr_t);
  }
  
  va_end(list);
  
  // Write the new stack pointer back.
  state.setStackPointer(reinterpret_cast<uintptr_t> (pStackLowWaterMark));
}

Re: use of ....

Posted: Wed Jun 25, 2008 5:40 am
by suthers
Doesn't work for me....
But then again I;m using an antiquated version of gcc (3.4.2 and the mingw port at that...).
Could that have something to do with it?
Thanks in advance,

Jules

Re: use of ....

Posted: Wed Jun 25, 2008 6:27 am
by JamesM
What doesn't work? Are you sure you included <stdarg.h>?

Re: use of ....

Posted: Wed Jun 25, 2008 8:20 am
by suthers
including a stdlib include in an OS?
I didn't think that would work, so I didn't try...
Now to figure out how to tell gcc what include path to use in the command line :lol:
Jules

Re: use of ....

Posted: Wed Jun 25, 2008 8:58 am
by JamesM
JamesM wrote:It's a GCC builtin, not in the standard library.
Did you even read what I wrote?

Re: use of ....

Posted: Wed Jun 25, 2008 11:08 am
by suthers
Sorry I didn't read tha part... :oops:
Jules

Re: use of ....

Posted: Wed Jun 25, 2008 3:18 pm
by iammisc
I also believe in gcc that you could simply use __builtin_va_list, __builtin_va_arg, etc. However, I read about this a long time ago and I might be wrong.

Re: use of ....

Posted: Wed Jun 25, 2008 3:36 pm
by Combuster
True. From stdarg.h coming with gcc:

Code: Select all

#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)

Re: use of ....

Posted: Wed Jun 25, 2008 3:40 pm
by suthers
Combuster wrote:True. From stdarg.h coming with gcc:

Code: Select all

#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)
Weird all my standard args contains is:

Code: Select all

#ifndef RC_INVOKED
#include_next<stdarg.h>
#endif
and also there is no other stdargs file in my include directory, so I don't understand how the #include_next could work...
Jules

Re: use of ....

Posted: Wed Jun 25, 2008 4:51 pm
by suthers
Thanks, fixed, all I did was remove '-nostdinc' from the command line, I though: "Ok, so I'm was trying to pass an include path to gcc, which is its default path... :idea: :shock: , how about I just allow it to use its standard command path...", Yah I know I'm pretty stupid sometimes.... :oops:
Thanks a mill for the help.
Thanks,

Jules

Re: use of ....

Posted: Thu Jun 26, 2008 2:33 am
by Combuster
and also there is no other stdargs file in my include directory, so I don't understand how the #include_next could work...
its not in /usr/include, its in the GCC directory (one of the other places it will look for its headers):

Code: Select all

/usr>find -iname 'stdarg.h'
./lib/gcc/i686-pc-mingw32/3.4.4/include/stdarg.h