Hi,
I'm writing a kprintf function for my kernel in assembly language, and I've been studying the sources of RedHat's newlib for clues. But I can't really see how variable argument lists are implemented behind the scenes.
How does a C function know how many arguments were passed to it? (Is there something on the stack frame that can act like a sentinel so it can count the arguments? Just looking at the number of %-format conversions isn't a very robust way of counting arguments passed!) What do the va_start and va_end macros expand to?
Thanks for your help.
Implementing C varargs in assembler
Re:Implementing C varargs in assembler
First off: why not write your kprintf in C?
Warning: x86 only! Oh, you're programming in assembler, so you don't care about portability. My bad.
So va_start needs to set the va_list to the next location after the format string. We're assuming that the stack expands downwards, so that will be equal to the address of the format string parameter, minus the size of a stack location.
va_end doesn't need to do anything on x86.
va_arg is the interesting one, but you didn't ask about that. Maybe you could look into it.
There isn't. printf is supposed to parse the format string to find out. Bear in mind that integer types smaller than int are promoted to int, and floats are promoted to float. However, integer types larger than int are passed as-is. Recall also that strings (and arrays in general) are passed as pointers.kernel_journeyman wrote:How does a C function know how many arguments were passed to it?
No -- you want .NET for that.(Is there something on the stack frame that can act like a sentinel so it can count the arguments?
You're right. If you think that's bad, looks at scanf -- at least printf doesn't try to write anywhere given what it's been passed in the format string.Just looking at the number of %-format conversions isn't a very robust way of counting arguments passed!)
Depends on the implementation.What do the va_start and va_end macros expand to?
Warning: x86 only! Oh, you're programming in assembler, so you don't care about portability. My bad.
- Assume that parameters are passed on the stack, which is a single contiguous block of memory, and that the stack grows downwards, i.e. towards address zero. On most other architectures, and on AMD64, and on x86 with calling conventions where some parameters are passed in registers (e.g. __fastcall) this does not hold, and variable parameters get more difficult to handle.
- Assume that SS and DS refer to the same memory (SS=DS if you like).
- Assume C's rules of promotion in variable parameter lists (see above) -- this is actually enforced here by the x86 architecture, where every stack location is the same size (32 bits in 32-bit protected mode).
So va_start needs to set the va_list to the next location after the format string. We're assuming that the stack expands downwards, so that will be equal to the address of the format string parameter, minus the size of a stack location.
va_end doesn't need to do anything on x86.
va_arg is the interesting one, but you didn't ask about that. Maybe you could look into it.