Implementing C varargs in assembler

Programming, for all ages and all languages.
Post Reply
kernel_journeyman

Implementing C varargs in assembler

Post by kernel_journeyman »

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.
Tim

Re:Implementing C varargs in assembler

Post by Tim »

First off: why not write your kprintf in C?
kernel_journeyman wrote:How does a C function know how many arguments were passed to 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.
(Is there something on the stack frame that can act like a sentinel so it can count the arguments?
No -- you want .NET for that. :)
Just looking at the number of %-format conversions isn't a very robust way of counting arguments passed!)
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.
What do the va_start and va_end macros expand to?
Depends on the implementation.

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 let's call va_list a pointer to the next parameter on the list. va_start needs to retrieve the first parameter, given the first non-variable parameter (such as the printf format string). Parameter lists consisting only of (...) aren't allowed in C.

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.
Post Reply