Using string literals

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Using string literals

Post by Aphex »

Hi Forum

In protected mode, I am attempting to write a function in C that prints a string to the screen. Here is my code so far...

Code: Select all

#define NULL 0

#define HEIGHT 25
#define WIDTH 80

#define WHITE_BACKGROUND 0x0F
#define BRIGHT_RED_TEXT 0x0C

void printChar(unsigned char c, unsigned char forecolour, unsigned char backcolour, int x, int y);

void start()
{
	const char *message = "Hello world\0";
	char tmpChar = 1;
	int counter;
	
	for (counter = 0; tmpChar != NULL; counter++)
	{
		tmpChar = message[counter];
		printChar(tmpChar, BRIGHT_RED_TEXT, WHITE_BACKGROUND, counter, 10);
	}
}

void printChar(unsigned char c, unsigned char forecolour, unsigned char backcolour, int x, int y)
{
    unsigned short attrib = (backcolour << 4) | (forecolour & 0x0F);
    volatile unsigned short *where;
    where = (volatile unsigned short *)0xB8000 + (y * 80 + x);
    *where = c | (attrib << 8);
}
Although, it just crashes and reboots everytime. I know why this is happening as explained here(http://www.osdever.net/tutorials/ckernel.php), it is apparently because the string literal is placed by GCC before the main or start in my case, causing a crash. The solutions offered were
* Write a short function which just calls main() and halts. This way, the first function in the program doesn’t contain any literal strings.
* Use the gcc option -fwritable-strings. This will cause gcc to put literal strings in the data section of the executable, away from any code.
I tried both and they dont work, for example, the first option.

Code: Select all

#define NULL 0

#define HEIGHT 25
#define WIDTH 80

#define WHITE_BACKGROUND 0x0F
#define BRIGHT_RED_TEXT 0x0C

void printChar(unsigned char c, unsigned char forecolour, unsigned char backcolour, int x, int y);
void start();

void call_start()
{
	start();
	
	while (1);	
}

void start()
{
	const char *message = "Hello world\0";
	char tmpChar = 1;
	int counter;
	
	for (counter = 0; tmpChar != NULL; counter++)
	{
		tmpChar = message[counter];
		printChar(tmpChar, BRIGHT_RED_TEXT, WHITE_BACKGROUND, counter, 10);
	}
}

void printChar(unsigned char c, unsigned char forecolour, unsigned char backcolour, int x, int y)
{
    unsigned short attrib = (backcolour << 4) | (forecolour & 0x0F);
    volatile unsigned short *where;
    where = (volatile unsigned short *)0xB8000 + (y * 80 + x);
    *where = c | (attrib << 8);
}

Still crashes

Second option (compiling like this)

gcc -ffreestanding -fwritable-strings. -c main.c -o main.o

doesnt work either...

Can anyone help me here?

thanks

EDIT: Just realised, I forgot to tell GCC the entry point when using the first option. But still, I just get repeated reboots

Here are my compile option

ld -e _call_start -Ttext 0x1000 -o kernel.o main.o
ld -i -e _call_start -Ttext 0x1000 -o kernel.o main.o
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:

Re: Using string literals

Post by Combuster »

Do you even know how file formats work? You're not executing start(), you're not even executing strings. You're executing the information in the object file's header.

Also, the tutorials at osdever.net are known to be broken. Try the Bare Bones tutorial.
"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 ]
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Re: Using string literals

Post by Aphex »

Do you even know how file formats work? You're not executing start(), you're not even executing strings. You're executing the information in the object file's header.
I simply had a problem, tried to solve it but did not succeed. I showed what solutions I had attempted and there outcome. Have you never had a problem and asked for help?

Where did I show the lack of understanding?
nedbrek
Member
Member
Posts: 44
Joined: Tue Dec 15, 2009 6:36 pm

Re: Using string literals

Post by nedbrek »

Is this related to the code from "OS keeps crashing"?

Can you print constants reliably now?
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Re: Using string literals

Post by Aphex »

No, I still can't print literal strings. The reason why not is fairly clear to me but the solutions arn't working. I know that the function to print chars to the screen works, for example, this works fine.

Code: Select all

void start()
{
   char tmpChar = 1;
   int counter = 0;
  
   printChar('H', BRIGHT_RED_TEXT, WHITE_BACKGROUND, counter, 10);
   counter ++;
   printChar('I', BRIGHT_RED_TEXT, WHITE_BACKGROUND, counter, 10);
   counter ++;
   printChar('!', BRIGHT_RED_TEXT, WHITE_BACKGROUND, counter, 10);
}
Although, when a string of chars is stored, I get a crash.

I notice that the string literal is actually stored at the very start of the second sector which is also home to the start() function, but the literal is before the start() function. Execution starts at the very start of the second segment after it is loaded into location memory location 0x1000, so I guess this is what is causing the crash.

is there a way to stop GCC from putting these strings before the entry point?
User avatar
Firestryke31
Member
Member
Posts: 550
Joined: Sat Nov 29, 2008 1:07 pm
Location: Throw a dart at central Texas
Contact:

Re: Using string literals

Post by Firestryke31 »

Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Re: Using string literals

Post by Aphex »

Well, I dont actually use a linker script, I link all my seperate files all together manually into one binary file, mostly using programs I've written myself, which is sorta a side project. I know this can't go on forever, that would just be stupid if I wanted to join lots of files together, there is too much room for error. I should learn about linker scripts asap, which I am.

Will a correct linker script resolve this problem with putting the literal string in a more appropriate place?
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: Using string literals

Post by NickJohnson »

What are you using as a bootloader? iirc, GRUB doesn't usually like loading into lower memory. Are you sure you have a sane stack as well?
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:

Re: Using string literals

Post by Combuster »

Aphex wrote:Where did I show the lack of understanding?

Failing to ask a smart question (omitting important information), using the linker twice, mixing arguments that sensibly apply only to executable (-e, -T) or object file (-i) output, but not both (therefore breaking either the RTFM or required knowledge rule, of your choice).
"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 ]
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Re: Using string literals

Post by Aphex »

What are you using as a bootloader? iirc, GRUB doesn't usually like loading into lower memory. Are you sure you have a sane stack as well?
Although I did not write this myself, I have had some practice writing bootloaders but I decided to use this, since I understand all that I written and it's purpose. The stack is located at 090000h

Code: Select all

mov esp, 090000h        ; Move the stack pointer to 090000h
Here is the bootloader I am using.

Code: Select all

[BITS 16]       ; We need 16-bit intructions for Real mode

[ORG 0x7C00]    ; The BIOS loads the boot sector into memory location 0x7C00

reset_drive:
        mov ah, 0               ; RESET-command
        int 13h                 ; Call interrupt 13h
        or ah, ah               ; Check for error code
        jnz reset_drive         ; Try again if ah != 0

        mov ax, 0
        mov es, ax
        mov bx, 0x1000          ; Destination address = 0000:1000

        mov ah, 02h             ; READ SECTOR-command
        mov al, 02h             ; Number of sectors to read
        mov ch, 0               ; Cylinder = 0
        mov cl, 02h             ; Sector = 2
        mov dh, 0               ; Head = 0
        int 13h                 ; Call interrupt 13h
        or ah, ah               ; Check for error code
        jnz reset_drive         ; Try again if ah != 0

        cli                     ; Disable interrupts, we want to be alone

        xor ax, ax
        mov ds, ax              ; Set DS-register to 0 - used by lgdt

        lgdt [gdt_desc]         ; Load the GDT descriptor

        mov eax, cr0            ; Copy the contents of CR0 into EAX
        or eax, 1               ; Set bit 0
        mov cr0, eax            ; Copy the contents of EAX into CR0

        jmp 08h:clear_pipe      ; Jump to code segment, offset clear_pipe


[BITS 32]                       ; We now need 32-bit instructions
clear_pipe:
        mov ax, 10h             ; Save data segment identifyer
        mov ds, ax              ; Move a valid data segment into the data segment register
        mov ss, ax              ; Move a valid data segment into the stack segment register
        mov esp, 090000h        ; Move the stack pointer to 090000h

        jmp 08h:01000h          ; Jump to section 08h (code), offset 01000h


gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
        dd 0
        dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0

gdt_end:                ; Used to calculate the size of the GDT



gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                  ; Address of the GDT




times 510-($-$$) db 0           ; Fill up the file with zeros

        dw 0AA55h                ; Boot sector identifyer

It sets up a GDT and puts the CPU in protected mode, loads the kernel into memory (written in C) and then jumps to it.
Failing to ask a smart question (omitting important information), using the linker twice, mixing arguments that sensibly apply only to executable (-e, -T) or object file (-i) output, but not both (therefore breaking either the RTFM or required knowledge rule, of your choice).
To be fair, that Is why I am on this forum. I have not written an OS like you have, I make mistakes, even if they sound stupid to you, they are not to me. So telling me to RTFM is not going to help me, but only put me down below you.

I am following a tutorial, this is the reason that he gave the linker script to be used twice.
"'-i' says that the build should be incremental. First link without it, because when '-i' is used, the linker doesn't report unresolved symbols (misspelled function names for example). When it linkes without errors, put '-i' to reduce the size."
I thought that sounded quite logical. Although, I sometimes simply dont bother using both.

I feel like we are moving away from the subject matter here though, constructive critisism is welcomed but insults are ignored.

thanks for any advice
I dont claim to know everything, so I appreciate any advice.
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:

Re: Using string literals

Post by Combuster »

Aphex wrote:
Failing to ask a smart question (omitting important information), using the linker twice, mixing arguments that sensibly apply only to executable (-e, -T) or object file (-i) output, but not both (therefore breaking either the RTFM or required knowledge rule, of your choice).
To be fair, that Is why I am on this forum. I have not written an OS like you have, I make mistakes, even if they sound stupid to you, they are not to me. So telling me to RTFM is not going to help me, but only put me down below you.
I didn't tell you to RTFM, you asked for an explanation, and knowing the difference between binaries and object files is what distinguishes a good programmer from a noob. Not ever blindly copying things is also a good skill. I can forgive you for being misinformed (like I said, osdever.net is the worst source of tutorials), but it doesn't excuse you for using object files where binaries are needed.

OS development is part of the top three of most difficult programming tasks. Nobody would be helping you by talking you through the easy obstacles, since that makes me know for sure you would never get past the larger ones. Expecting to be helped is deadly.
I am following a tutorial, this is the reason that he gave the linker script to be used twice.
"'-i' says that the build should be incremental. First link without it, because when '-i' is used, the linker doesn't report unresolved symbols (misspelled function names for example). When it linkes without errors, put '-i' to reduce the size."
I thought that sounded quite logical. Although, I sometimes simply dont bother using both.
"Assume Nothing" is a key skill when debugging. The description is utterly bull. incremental linking saves you a bunch of file headers and padding, and some relocations that map relatively into the same section. It does not, like final linking can, remove all relocations and symbol tables, and have only one file header (which would even be shorter since you don't need to mention the unused sections)
Similarly, all offsets into other sections are still zero-based (with a relocation info telling it should point relative to .text or .data), and you'd need to link those yourself. Essentially, only function calls will be linked properly this way (relative offsets from .text to .text, they don't change when the code or individual sections get mapped somewhere else)
"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 ]
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Re: Using string literals

Post by Aphex »

Thank you Combuster. I know I have a alot to learn about OS development, I appreciate your replies, even if they prove me a noob in OS dev, Which I pretty much am :oops: , no shame :)

I took a look at the Wikiapidia entry on Object Files, here. http://en.wikipedia.org/wiki/Object_file

You say that binary files and object files are different and that knowing the difference defines a newbie from a more educated OS Dever. But that wiki entry states
Object files contain compact code, and are often called "binaries".
Is Wiki wrong, you wrong or (most likely) am I wrong?

Dont get me wrong, I know what object files are used for, they can be used to add to just about any binary file like a function to an executable for example. I use the term "add" loosely, I mean that they fill in the gaps more than anything, such as the defininition for a function. I cant be far off...

Thanks :)
User avatar
Firestryke31
Member
Member
Posts: 550
Joined: Sat Nov 29, 2008 1:07 pm
Location: Throw a dart at central Texas
Contact:

Re: Using string literals

Post by Firestryke31 »

The problem is that there are multiple definitions of "object files." In this case, "object file" indicates source that is compiled but not linked yet (hence the .o extension), so it does not form a functioning executable. Binaries usually mean a fully compiled, linked, and functioning executable (like Linux's ELF or Windows' .exe) or shared library (like Linux's .so or Windows' .dll). From what I gather (though I honestly have not fully processed the thread) you seem to be trying to execute an "object file" in the sense of what I described here, when you should be trying to execute a "binary."
Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
User avatar
gravaera
Member
Member
Posts: 737
Joined: Tue Jun 02, 2009 4:35 pm
Location: Supporting the cause: Use \tabs to indent code. NOT \x20 spaces.

Re: Using string literals

Post by gravaera »

Hi, and welcome to the forums :) ;

There are three main conventional types of files which are used to store object code: Executable Object files, Relocatable Object files, and Shared Object files. To that end, wikipedia is right. Technically they all contain instructions, and as the ld manuals state, even when you use complex abstractions like sections, all you're essentially doing is grouping off bytes of instructions and data in the object file.

You may choose not to even use an executable file format, and encode your work in pure binary, with no fancy wrapping. The reason why the 'wrapping' up of binary code is considered necessary is that files may be copied from one computer to another, from one drive rto another and so on. There is no guarantee that the binary file downloaded off boo.com is a file of instructions for say a PPC cpu.

The notion of executable formats places information in the file itself stating which architecture and OS it was meant for.

To sum up, Relocatable files are file containing object code that is meant to be wholesale copied into a binary file that links against it. Shared files contain object code just like relocatable files, but when they are linked against, they the linker simply inserts a dependency statement syaing that 'shared object file libshared.so is needed', instead of copying the contents of the object file into the final binary.

And of course, executable files are, executable files built from relocatable files and linked against shared files.

--All the best
gravaera.
17:56 < sortie> Paging is called paging because you need to draw it on pages in your notebook to succeed at it.
Aphex
Posts: 15
Joined: Mon Nov 23, 2009 5:57 pm

Re: Using string literals

Post by Aphex »

I have read your post's well and all your comments are very helpful, you have bought alot to my attention, thank you :)
you seem to be trying to execute an "object file" in the sense of what I described here, when you should be trying to execute a "binary."
Out of curiosity, what makes you think I am not trying to execute a fully functioning executable or binary file? This is what i am always working towards, infact, in my opinion, the only reason that my binary wasn't fully functional is because GCC placed the string literals at the start of the second segment. Although, I may be wrong.

As for your post gravaera, it is much appreciated. I do understand alot of what you are saying there, you are a good teacher :). cheers..

Still, does anyone know a solution to the problem of my original post? I know its not really your job to solve such trivial questions so, I'll learn about linker scripts, can anyone reccomend and tutorials abotu linker scripts? I think they are hard to find.
Post Reply