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
It works either way. So, I really don't think it has anything to do with the actual problem.