Page 1 of 2

help with printf code

Posted: Tue Jan 03, 2012 3:19 pm
by yemista
I wrote a simple printf, and all that it will print are strings, not referenced, %s, strings, and hex numbers. All the subfunctions it calls works. Im thinking it has to do with the variable arg list. any suggestions?

Code: Select all

void kprintf(char* format, ...) {
  char* c;
  int n = 1;

  for(c = format; *c != '\0'; ++c) {
    if(*c == '%') {
      switch(*(++c)) {
        case 'x':
        case 'p':
          monitor_write_hex(get_arg(n++));
          break;
        case 'd':
          monitor_write_dec(get_arg(n++));
          break;
        case 'c':
          monitor_put((char)get_arg(n++));
          break;
        case 's':
          monitor_write((char*)get_arg(n++));
          break;
      }
    }

    else
      monitor_put(*c);
  }
}

Re: help with printf code

Posted: Tue Jan 03, 2012 3:24 pm
by NickJohnson
What is this "get_arg" function you're using? How could it possibly know where the arguments are if you don't even give it the address of the argument "format"?

Re: help with printf code

Posted: Tue Jan 03, 2012 3:26 pm
by yemista
get_arg gets the args of the function. thats why i start at 1, format is what i read through

Re: help with printf code

Posted: Tue Jan 03, 2012 4:04 pm
by AJ
Hi,

Could we see the code for get_arg?

If you're using IA32, the ABI states that parameters are passed on the stack. Does get_arg assume things based on the value of EBP? If not, it needs seeding with the value of format, as NickJohnson states. If so, check that the value of EBP is what you would expect. Are you using the Cross-Compiler detailed on the wiki? If so, at least we have some idea of your compilation environment.

Otherwise (with 64 bit), the first few parameters are passed in registers and you need to take account of that.

Another useful piece of information would be whether %c works? That is the only format that uses the monitor_put function.

Cheers,
Adam

[EDIT: Oh, one thing you may like to do is to try defining a char* called 'fmt'. Set this using (char*)get_arg(0). Then, parse fmt rather than 'format'. If your function then fails to even print a basic string, you know that get_arg is your problem].

Re: help with printf code

Posted: Tue Jan 03, 2012 4:15 pm
by yemista
i am using gcc on 32-bit linux. Here is the code for get_arg, sorry I didnt post it sooner

Code: Select all

get_arg:
        mov ecx, [esp +4]
        mov eax, [esp + ecx*4 + 36]
        ret
I got the 36 by analyzing how much extra things gcc pushes onto the stack

Re: help with printf code

Posted: Tue Jan 03, 2012 5:00 pm
by AJ
Hi,

OK - firstly, you'd be better off using EBP if you plan to do this manually, but be prepared that this will not work in a cross-platform manner. How did that test go, using get_arg to obtain char* format?

A better way would be to use va_list, va_arg etc. In a freestanding environment, I believe you can use __builtin_va_list, __builtin_va_start and so on.

Cheers,
Adam

[edit - just found this http://forum.osdev.org/viewtopic.php?f=1&t=11058 ]

Re: help with printf code

Posted: Tue Jan 03, 2012 6:49 pm
by Solar
PDCLib code for stdargs.h, x86_64:

Code: Select all

typedef __builtin_va_list va_list;
#define va_arg( ap, type ) ( __builtin_va_arg( ap, type ) )
#define va_copy( dest, src ) ( __builtin_va_copy( dest, src ) )
#define va_end( ap ) ( __builtin_va_end( ap ) )
#define va_start( ap, parmN ) ( __builtin_va_start( ap, parmN ) )
Generic PDCLib code without builtins:

Code: Select all

/* Internal helper macro. va_round is not part of <stdarg.h>. */
#define _PDCLIB_va_round( type ) ( (sizeof(type) + sizeof(void *) - 1) & ~(sizeof(void *) - 1) )
	
typedef char * va_list;
#define va_arg( ap, type ) ( (ap) += (_PDCLIB_va_round(type)), ( *(type*) ( (ap) - (_PDCLIB_va_round(type)) ) ) )
#define va_copy( dest, src ) ( (dest) = (src), (void)0 )
#define va_end( ap ) ( (ap) = (void *)0, (void)0 )
#define va_start( ap, parmN ) ( (ap) = (char *) &parmN + ( _PDCLIB_va_round(parmN) ), (void)0 )
(This works for e.g. x86, but not for x86_64 due to the latter using registers for argument passing. It still might help you understanding what the macros actually do.)

Provided under CC0 terms. Header guards required.

Re: help with printf code

Posted: Wed Jan 04, 2012 4:29 am
by davidv1992
You could also just use the compiler versions of these headers, the standards specify they do exist in a freestanding environment.

Re: help with printf code

Posted: Wed Jan 04, 2012 12:09 pm
by Brynet-Inc
davidv1992 wrote:You could also just use the compiler versions of these headers, the standards specify they do exist in a freestanding environment.
But they're not always licensed the way you prefer, and having a implementation shared between compilers reduces the chance of future conflict.

Re: help with printf code

Posted: Wed Jan 04, 2012 1:39 pm
by yemista
ok, the test you recommended failed. It just printed a bunch of 'S''s, so I guess get_arg is faulty. Do you think its because it might be clobbered the ecx or eax register? I will try a new version that stores the arg to a pointer passed in and see if this helps and report back.

Re: help with printf code

Posted: Wed Jan 04, 2012 2:43 pm
by gerryg400
yemista wrote:I will try a new version that stores the arg to a pointer passed in and see if this helps and report back.
Even if you get it to work this is a stupid way to do it. Please read Solar's post and do it properly.

Re: help with printf code

Posted: Thu Jan 05, 2012 7:29 am
by yemista
ok i will do it your way. keep in mind my os is purely for my own knowledge and enjoyment, and i never plan to port it, but im guessing there are other reasons i am not seeing to do it this way. It is not working. I have some questions though on the code, as I like to understand things. I get most of it but I dont get PDCLIB_va_round at all. Thats still black magic to me. Also, I dont get what the code "*(type*)" does in va_arg. Does it create a double pointer? Or does it create a pointer of type type and then deference it? Im guessing the later since its supposed to return an actual data item

Re: help with printf code

Posted: Thu Jan 05, 2012 7:38 am
by Solar
yemista wrote:ok i will do it your way. keep in mind my os is purely for my own knowledge and enjoyment, and i never plan to port it, but im guessing there are other reasons i am not seeing to do it this way.
The most important reason is that this is the way it is done in the programming language "C". There is a standard way to do it, i.e. no reason to go through an ASM hack to do it a different way. Not only can you rely on other people's work to get it done, your code will also be better readable for the lack of a custom construct.

(Of course, since you are apparently unfamiliar with the standard construct, this doesn't help you much. On the other hand, you do now know what the standard way is, i.e. you have learned something that will help you outside of your OS project, too.)
here is my new code:
Not having done much C coding so far, have you? The idea is to put the source into the header "stdarg.h", wrapped in an include guard, and include that. Someone who already used <stdarg.h> before should have gotten the idea, which I thought was obvious from the comment under my source snippet. (I didn't want to press my way of doing include guards on you, or I would have pasted the complete header.)
now I am getting the following error:
lib/stdio.c: In function ‘kprintf’:
lib/stdio.c:35: warning: implicit declaration of function ‘var_arg’
That's pretty obvious, isn't it? The compiler even tells you the line of the error. One good look at what you wrote there, compared with what you wrote when you defined the macro va_arg (hint, hint) should have solved that.
Also, I understand how the macros work, all except for PDCLIB_va_round. thats still black magic to me.
Different types have different alignment rules. The macro does some math to get the alignment right for types that have other alignment rules than void *. (The double type would be a good example here.)
I also dont understand what the code "*(type*)" does in va_arg. Is that just a double pointer? Or is it making a new pointer of type type and deferencing it?
(type *) is a cast of the expression to the right to "pointer to type".

The * in front of that dereferences the pointer.

I strongly suggest you try yourself at some non-trivial user-space application first. You need to be proficient in your language of choice before tackling kernel-space programming. If the language gives you such trouble already, kernel space is not the environment to practice in, as it is a definitely hostile environment to be in. The usual tools (e.g. debuggers) fail you, you don't get much error output other than a triple-fault / reboot, and people in relevant forums expect you to be a competent developer in your own right... :wink: In user-space, there is much more patience applied to newbie questions...

Re: help with printf code

Posted: Thu Jan 05, 2012 8:01 am
by yemista
Solar, I actually edited this post after you responded to it. I found the bug(like I said, Ive been up all night), and the reason the macro wasnt guarded in a header file was because I was just testing it out. I plan to move it to its own file. I am proficient in C, but I have been away from this project and C coding for about 3 years, so I am a little rusty. I had a nasty bug while trying to implement multitasking, and I gave up on it, but it has always bothered me so Im back to get it going again. While going through it I found the printf bug which I mustve not noticed before

Re: help with printf code

Posted: Thu Jan 05, 2012 8:06 am
by Solar
I would expect this particular bug to have some really hard-to-track results if going undetected. :-D

(BTW, and while we're at it, va_arg and C++ references don't mix. Just for completeness' sake. ;-) )