Functions with variable-length argument lists

Programming, for all ages and all languages.
Post Reply
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Functions with variable-length argument lists

Post by devel »

Hi there,

I am trying to implement a simple os with multi-segment memory model (real address mode).
Everything seems to be ok but once I set stack segment different from data segment, functions
with variable-length argument lists do not work properly. I am using gcc's __builtin_va_list for now
and I think that might be trouble. So my question is whether somebody can post a code that
handles this feature ( i.e. va_list, va_arg, va_start ...) .but everything built upon 'standard'
data types like int, char ...

thnx in advance & regards,
devel.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

gcc was built for 32-bit development rather than 16-bit, and blatantly assumes that DS=ES=SS

If you're serious about real mode development, consider getting a 16-bit compiler for the job.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

Yes, that's true but it is possible to apply address/codesize overrides (gas directive
'.code16' or '.code16gcc' ) so it is possible to emit code that runs in 16-bit
environment (with some additional setings). Regarding my problem, I hope that once I get
code for va_list handling and compile it for 16-bit environment my problem might be solved.

regards,
devel
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

GCC was build under the assumption of a flat memory model, so even if you get your varargs handling to work, you're still deep in "undefined" territory (i.e., next operation might format your hard drive).
Every good solution is obvious once you've found it.
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

to Solar:
Discovering undefined territories is what I am looking for so l would definitely risk that :D

Please forget about 16-bit environment and segmented memory model. If somebody could post some sample code then it would be really helpful.

regards,
devel
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Please forget about 16-bit environment and segmented memory model. If somebody could post some sample code then it would be really helpful.
Sample code for what? Ignoring the 16-bit multi segmented nature of your question is ignoring the entire point of your post. What do you want?

And in case a third opinion is needed, get a 16 bit compiler. Or at least, use a flat memory model. 'Undefined territory' means that GCC can do just about anything, as you're violating its assumptions in a big way. The worst that can happen? You can munge your CMOS, munge your HDD...
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

ok, I have just forgot about multi segment memory model even about 16-bit environment.
What I need is code that can 'fake' gcc's va_arg, va_start, ... macros in case if somebody
wants (for whatever cause) its own implementation.

regards,
devel
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

The reason that's difficult to answer is that it depends on the calling convention you are using. Generally, if you look in stdarg.h, you will see that va_arg et. al. are defined as pointing to a GCC builtin function. The compiler then substitutes the appropriate code.

If you are using 32 bit GCC, args are pushed on a stack. It's therefore fairly easy to make up the macros by looking at the stack frame and incrementing a pointer by sizeof(theargumentyouwant).

If you are using 64 bit GCC, the frist 6 args are passed via registers (rdi, rsi, rdx, rcx, r8, r9 in that order) and the rest are passed on the stack. This means that your macros suddenly become prohibitively complex and is why you should use the GCC builtin.

What are you trying to achieve at a higher level?

Cheers,
Adam
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

Hi,
used calling convention is cdec. Anyway you did mention that is easy to look into stack
frame could you please explain how. Is that possible from c?

This is just a pure curiosity by myself I am planning to use gcc's builtins.

regards,
devel
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

It's completely implementation dependent and subject to change. Any one implementation can't be relied upon, so use the varargs macros.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

As you have been warned, it is implementation-specific (future versions of GCC are absolutely welcome to change this without notice!), but here's theb start of an old, horrible printf()-like function I copied form somewhere before I saw the light.

Code: Select all

                 int Printf(const char *fmt, ...)
	{
	    /*  a higher level output function for the kernel   */
	    if(DebugPrefix)
	    {
	    	Puts(DebugPrefix);
	    	PutChar(' ');
	    }
	    
	    char **arg = (char **) &fmt;
	    char *p;
		char buffer[STR_BUFFER_MAX_OCT];
	    u16 padlen;
		int c;
	
		arg++;
		while((c=*fmt++)!=0)
		{
		    if(c!='%')
	            PutChar(c);
	        else
	        {
	            c = *fmt++;
	            switch(c)
				{
	                case 'd':                   /*  decimal integer     */
	                case 'i':
	                    ltostr(*((long *)arg++), buffer);
	                    Puts(buffer);
	                    break;
	                case 'x':                   /*  hexadecimal integer */
	                case 'X':
	                    htostr(*((long *)arg++), buffer);
	                    Puts(buffer);
	                    break;
	                case 'o':                   /*  octal   */
	                case 'O':
	                    otostr(*((long *)arg++), buffer);
	                    Puts(buffer);
	                    break;
....
..and so on - you get the idea.

Cheers,
Adam
Post Reply