Page 1 of 2

Problem with strings and printing to screen - no '\0' added?

Posted: Mon Mar 24, 2008 4:34 pm
by brickhead20
I'm playing around with skeleton "kernels" before starting out on a bigger project. I'm currently toying around with tutorial code, and trying to print to the screen, running the kernel on qemu from a cd image with GRUB eltorito boot. After playing around trying to fix the resulting error 13 that I was getting (couldn't run the type of executable), I found that my print function was fixed if I added the '\0' symbol to the end of my strings manually. However, it does not work, and gives me the error 13 if I do not add this symbol. Here is my C code

Code: Select all

#define WHITE_TEXT 0x07	/* White text on a black screen */
#define VIDMEMPTR 0xb8000;
#define TXTWIDTH 80
#define TXTHEIGHT 25
#define BYTESPERCHAR 2

/* Define functions */

void clearScreen();
unsigned int printf(char *message, unsigned int line);

int main()
{
	clearScreen();
	printf("Hello, World", 1);
	for(;;)
		;
}

void clearScreen()	/* Clear the screen of text */
{
	/* We set a pointer to the video memory; we declare it char so we can write to it a byte at a time */
	char *vidmem = (char *) VIDMEMPTR;
	unsigned int i = 0;
	
	/* Write to the video memory to clear the screen */
	
	while( i < ( TXTWIDTH * TXTHEIGHT * BYTESPERCHAR ) )
	{
		vidmem[i] = ' ';	/* Set character to blank space */
		i++;
		vidmem[i] = WHITE_TEXT;	/* Set style to WHITE_TEXT */
		i++;
	}
	
}	/* (End clear screen function) */


unsigned int printf(char *message, unsigned int line) /* Prints a message at the given line */
{
	/* We set a pointer to the video memory; we declare it char so we can write to it a byte at a time */
	char *vidmem = (char *) VIDMEMPTR;

	unsigned int i = 0;
	i = ( line * TXTWIDTH * BYTESPERCHAR );

	
	while( *message != 0 )
		{
			vidmem[i] = *message;
			i++;
			vidmem[i] = WHITE_TEXT;
			i++;
			++message;
		}
	
	
	return 1;
	
}	/* End printf function */
So printf("Hello World", 1); does not work, but printf("Hello World\0", 1); does. I compile with:

gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o kernel.o kernel.c

I don't know if I'm just being an idiot or what, so any help would be much appreciated.

Posted: Mon Mar 24, 2008 4:58 pm
by t0xic
No, you're not being an idiot, but you need the null terminated string, because if you look in your printf code, you are looping until until you find a char with a value = 0 (hence '\0'). Some C compilers do not automatically null terminate free standing standing strings, so you could try upgrading your compiler. Hope this helps

Posted: Mon Mar 24, 2008 5:04 pm
by brickhead20
Yeah I can see thats how my printf works, but I thought that the '\0' was added automatically. I guess I need to find a command to make gcc ensure the \0 is added, or upgrade etc.

I will try. Thanks for the help.

Posted: Mon Mar 24, 2008 5:05 pm
by t0xic
Are you using a standard linker script? Like the one from Bran's tutorial? That might also be the problem.

Although... if your printf will work when you add the \0, that shouldn't be the issue.

Posted: Mon Mar 24, 2008 5:12 pm
by brickhead20
Tried gcc-4.2

I still get the same problems, and thats a pretty recent compiler. Is there a flag I can pass to gcc to force it to add '\0' to the end of each string?

Is it possibly another problem?

Posted: Mon Mar 24, 2008 5:15 pm
by brickhead20
I was using a linker script from Bran's tutorial, but then I changed it to one that fixed a different error. My linker script is

Code: Select all

/* Link.ld -- Linker script for the kernel - ensure everything goes in the */
/*            Correct place.  */
/*            Original file taken from Bran's Kernel Development */
/*            tutorials: http://www.osdever.net/bkerndev/index.php. */

ENTRY(start)
SECTIONS
{
  .text 0x100000 :
  {
    code = .; _code = .; __code = .;
    *(.text)
    . = ALIGN(4096);
  }

  .data :
  {
     data = .; _data = .; __data = .;
     *(.data)
     *(.rodata)
     . = ALIGN(4096);
  }

  .bss :
  {
    bss = .; _bss = .; __bss = .;
    *(.bss)
    . = ALIGN(4096);
  }

  end = .; _end = .; __end = .;
}
I think its modified from the original.

Posted: Mon Mar 24, 2008 6:41 pm
by JJeronimo
brickhead20 wrote:Yeah I can see thats how my printf works, but I thought that the '\0' was added automatically. I guess I need to find a command to make gcc ensure the \0 is added, or upgrade etc.
Try looking at the asm code gcc generates (-S flag) and see if the string "Hello world" is declared with the .asciz or if it's declared with the .ascii directive. That's not difficult, but if you can't figure out how to find the string declaration, just post here the resulting asm file...

.asciz appends a '\0' to the string. .ascii does not.

JJ

Posted: Mon Mar 24, 2008 6:59 pm
by JJeronimo
brickhead20 wrote:I was using a linker script from Bran's tutorial, but then I changed it to one that fixed a different error. My linker script is (...)
I don't know whether this has anything to do with your problem, but my linker script would be something like:

Code: Select all

ENTRY(start)

SECTIONS
{
   . = 0x100000;

  START_ADDRESS = .;
  CODE_SECTION = .;

  .text :
  {
    *(.text)
  }

  . = ALIGN(4096);
  DATA_SECTION = .;

  .data :
  {
     *(.data)
     *(.rodata)
  }

   . = ALIGN(4096);
  BSS_SECTION = .;

  .bss :
  {
    *(.bss)
  }

  . = ALIGN(4096);
  END_ADDRESS = . ;
}
I don't know if that ".text 0x100000:" really does what it looks like.
I've not tested the script, so try to debug it if ld complains. It's possible that the ALIGN statements must be inside the sections...

Also, see the asm output of gcc to see if the problem is in the code that's being generated or if it's worth to find errors in the linking process...

JJ

Posted: Tue Mar 25, 2008 2:48 am
by JamesM
I don't know if that ".text 0x100000:" really does what it looks like.
It does. I know, because I wrote that linker script.


@OP: Null-terminators are automatically put on the end of *any* string constant. Try manually assembling the string in an array and see if that works:

Code: Select all

char pStr[] = {'h','e','l','l','o','\0'};
printf("%s", pStr);
Hope this helps,

James

Posted: Tue Mar 25, 2008 2:44 pm
by -m32
I'm not so sure that not having the NULL character actually has anything to do with the problem at all.

GRUB Error 13 means there's something wrong with the executable itself. You're not just loading the .o file with GRUB are you? Is your linker spitting out an ELF executable? Are you trying to use MULTIBOOT?

I think more info is needed on how you're compiling. Can we see your assembly for start: ?

Posted: Tue Mar 25, 2008 2:54 pm
by brickhead20
-m32 wrote:I'm not so sure that not having the NULL character actually has anything to do with the problem at all.

GRUB Error 13 means there's something wrong with the executable itself. You're not just loading the .o file with GRUB are you? Is your linker spitting out an ELF executable? Are you trying to use MULTIBOOT?

I think more info is needed on how you're compiling. Can we see your assembly for start: ?
Its not the executable, since adding the '\0' to the end of the string works. The linker script (which the guy wrote above, and doesn't give any errors) outputs a flat binary file. Unless theres a way that this effects this.

I'll take a look at the assembly that gcc generates. So is there a flag I can pass to gcc to force the '\0' to be added to all strings?

Yeah, I already manually constructed the string with the extra '\0' and that worked. So I want to find a way of getting the compiler to add it for me.

Thanks for all the help so far.

Especially for the linker script, that made life much easier.

Posted: Tue Mar 25, 2008 2:58 pm
by -m32
A NULL character is added by GCC, there's no reason it shouldn't.


Try printing a longer string printf("This is a longer string!!!\0", 1); to see if it still works.

It HAS to be the executable if grub is giving you error 13. It doesn't care about your string.

Posted: Tue Mar 25, 2008 3:00 pm
by -m32
I think adding the \0 is just mitigating your problem. Which means, it will come back and will be even harder to fix at that point.

Posted: Tue Mar 25, 2008 3:02 pm
by brickhead20
This is the gcc assembly output:

Code: Select all

	.file	"kernel.c"
	.text
	.p2align 4,,15
.globl putch
	.type	putch, @function
putch:
	pushl	%ebp
	movl	%esp, %ebp
	movl	12(%ebp), %eax
	movl	8(%ebp), %edx
	leal	(%eax,%eax,4), %eax
	sall	$4, %eax
	addl	16(%ebp), %eax
	movb	%dl, 753664(%eax,%eax)
	xorl	%eax, %eax
	popl	%ebp
	ret
	.size	putch, .-putch
	.p2align 4,,15
.globl clearScreen
	.type	clearScreen, @function
clearScreen:
	pushl	%ebp
	xorl	%eax, %eax
	movl	%esp, %ebp
	.p2align 4,,7
.L4:
	movb	$32, 753664(%eax)
	movb	$7, 753665(%eax)
	addl	$2, %eax
	cmpl	$4000, %eax
	jne	.L4
	popl	%ebp
	ret
	.size	clearScreen, .-clearScreen
	.p2align 4,,15
.globl printf
	.type	printf, @function
printf:
	pushl	%ebp
	movl	%esp, %ebp
	movl	8(%ebp), %edx
	movl	12(%ebp), %eax
	movzbl	(%edx), %ecx
	testb	%cl, %cl
	je	.L11
	leal	(%eax,%eax,4), %eax
	sall	$5, %eax
	.p2align 4,,7
.L13:
	movb	%cl, 753664(%eax)
	movb	$7, 753665(%eax)
	movzbl	1(%edx), %ecx
	addl	$2, %eax
	addl	$1, %edx
	testb	%cl, %cl
	jne	.L13
.L11:
	popl	%ebp
	movl	$1, %eax
	ret
	.size	printf, .-printf
	.section	.rodata.str1.1,"aMS",@progbits,1
.LC0:
	.string	"Hello, World"
	.text
	.p2align 4,,15
.globl main
	.type	main, @function
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$8, %esp
	call	clearScreen
	movl	$1, 4(%esp)
	movl	$.LC0, (%esp)
	call	printf
.L17:
	jmp	.L17
	.size	main, .-main
	.ident	"GCC: (GNU) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)"
	.section	.note.GNU-stack,"",@progbits
My assembly needs some attention, but from what I can see, its not adding a '\0' to the end of the "Hello, World" string.

Thanks again.

Posted: Tue Mar 25, 2008 3:15 pm
by -m32
Nor does mine:

Code: Select all

	.string	":"
.LC15:
	.string	", Len: "
.LC16:
	.string	", Type: "
.LC17:
	.string	", Size: "
.LC18:
	.string	" KiB"

and yet, it works.

That's on linux, If I compile it using GCC with Cygwin, it gives me

Code: Select all

LC21:
	.ascii ", Size: \0"
It works either way. So, I really don't think it has anything to do with the actual problem.