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

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.
brickhead20
Member
Member
Posts: 38
Joined: Mon Mar 24, 2008 4:17 pm

Post by brickhead20 »

Ok, fair point.

I tried a longer string, and adding the '\0' worked.

Heres the assembly that I pinched for this

Code: Select all

[BITS 32]
global start           ; making entry point visible to linker
extern main             ; _main is defined elsewhere

; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
FLAGS       equ  MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ    0x1BADB002           ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum required

section .text
align 4
MultiBootHeader:
   dd MAGIC
   dd FLAGS
   dd CHECKSUM

; reserve initial kernel stack space
STACKSIZE equ 0x4000          ; that's 16k.

start:
   mov esp, stack+STACKSIZE           ; set up the stack
   push eax                           ; pass Multiboot magic number
   push ebx                           ; pass Multiboot info structure

   call  main                        ; call kernel proper
               hlt                    ; halt machine should kernel return

section .bss
align 32
stack:
   resb STACKSIZE      ; reserve 16k stack on a quadword boundary
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Post by -m32 »

Ok, two things that may cause issues:

1) Your 'start:' appears AFTER your multiboot spec, yet you're using the linker to put start's offset at 0x100000. This would mean, the multiboot info is being loaded at 0x100000 - 6 if I'm not mistaken. So, it's not being loaded correctly.

2) You said you're using a binary output file from your linker, yet you don't have an AOUT kludge here. The AOUT kludge is required if you're using a flat binary kernel.

Try something like this:

Code: Select all


start:
   mov esp, stack+STACKSIZE           ; set up the stack
   push eax                           ; pass Multiboot magic number
   push ebx                           ; pass Multiboot info structure

   call  main                        ; call kernel proper
               hlt                    ; halt machine should kernel return 

ALIGN 4
MultiBootHeader:
   dd MAGIC
   dd FLAGS
   dd CHECKSUM

   ; + AOUT kludge if kernel is flat binary

Give it a try, hope it helps. Good luck!
brickhead20
Member
Member
Posts: 38
Joined: Mon Mar 24, 2008 4:17 pm

Post by brickhead20 »

OK, I tried both, but it does not appear to be helping. The AOUT kludge should have been there, but I still get the same error 13.
User avatar
Wave
Member
Member
Posts: 50
Joined: Sun Jan 20, 2008 5:51 am

Post by Wave »

Code: Select all

.string "Your String"
will be null-terminated, so don't worry.

If you get an error from GRUB then something is wrong with your executable.

If you are really using binary you need to use the A.OUT kludge. However, your linker script doesn't define the output format, so it will be the default that ld was compiled with. OUTPUT_FORMAT("binary") will make it use binary. But ELF is nicer to work with, since you don't need the A.OUT kludge.

If you're using binary you need to specify the entry point in the A.OUT kludge. If it starts running from the start as it's now, the computer will try to run your multiboot header.

You should use a separate section for the multiboot header to ensure it's placed first in the file.
Conway's Law: If you have four groups working on a compiler, you'll get a 4-pass compiler.
Melvin Conway
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Post by -m32 »

I suspect that your 'binary' isn't flat binary then. Are you doing this in Windows? It could be a PE file

try

objcopy -R .note -R .comment -S -O binary <input_file> <output_file>

eg:
objcopy -R .note -R .comment -S -O binary kernel.o kernel.bin

and then use grub to boot the output file from objcopy.

I was able to recreate your problem with a PE output file.
Last edited by -m32 on Tue Mar 25, 2008 7:25 pm, edited 1 time in total.
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Post by -m32 »

Do this:

Code: Select all

[BITS 32]
global start           ; making entry point visible to linker
extern main             ; _main is defined elsewhere

; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
AOUT        equ   1<<16                ;<-------------------------------------- NOTE
FLAGS       equ  MODULEALIGN | MEMINFO | AOUT  ; this is the Multiboot 'flag' field  <----------------------------------
MAGIC       equ    0x1BADB002           ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum required

section .text

; reserve initial kernel stack space
STACKSIZE equ 0x4000          ; that's 16k.

start:
   mov esp, stack+STACKSIZE           ; set up the stack
   push eax                           ; pass Multiboot magic number
   push ebx                           ; pass Multiboot info structure

   call  main                        ; call kernel proper
               hlt                    ; halt machine should kernel return

align 4
MultiBootHeader:
   dd MAGIC
   dd FLAGS
   dd CHECKSUM

   ; AOUT KLUDGE
   extern code, bss, end

   dd MultiBootHeader
   dd code
   dd bss
   dd end
   dd start

section .bss
align 32
stack:
   resb STACKSIZE      ; reserve 16k stack on a quadword boundary
and at the top of your linkerscript add OUTPUT_FORMAT("binary"):

Code: Select all

OUTPUT_FORMAT("binary")

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 = .;
} 
If the linker complains about converting a PE to binary, then, remove the 'OUTPUT_FORMAT' bit and use objcopy to convert the PE to binary 'manually'
JJeronimo
Member
Member
Posts: 202
Joined: Wed Oct 18, 2006 3:29 pm

Post by JJeronimo »

Wave wrote:

Code: Select all

.string "Your String"
will be null-terminated, so don't worry.
Yeah. .string is one of the directives that implicitly add the C endstring.
But ELF is nicer to work with, since you don't need the A.OUT kludge.
Let me disagree with you. I think there's nothing special with declaring the entry point in the MB header. It's rather easy...........
Actually, I think it's a more proper way to format a kernel (just doesn't make sense having an executable that has 2 headers, each from it's standard). Also, it's rather stupid the idea of presenting a boot protocol aimed to facilitate the interoperability between kernels and boot loaders, and then say "here is the format. then, if you are a boot loader, you also have to learn ELF (an OS specific format)"...

Sorry if I'm telling garbage! It's rather possible that there are many arguments for the multiboot spec to preview that the boot loader supports ELF, but I don't see them...
You should use a separate section for the multiboot header to ensure it's placed first in the file.
Not needed. Because of that strange file format that MB proposes, it just needs to be in the first X KB (see the multiboot specs) of the exe image. there's a way to do this with the linker script:

Code: Select all

.text :
{
  start.o(.text)       /* Or something like that... */
  *(.text)                /* Don't accept this code as correct...*/
     /*it's not likely to be so, but get the idea and see the  */
}   /* LD documentation (yes, I agreee with the TR;DR */
     /*portion of your argument!) */
The first statement includes everything that the file "start.o" has to contribute for the .text section, and the second one includes the other .text sections (i'm really not sure whether this includes the crowd from start.o again)...

I do prefer reserving for the header it's it's own section, though...

JJ
brickhead20
Member
Member
Posts: 38
Joined: Mon Mar 24, 2008 4:17 pm

Post by brickhead20 »

No I'm running linux.

I tried all of the above except objcopy, since the command isn't quite right. I'm toying, trying to get it right, it says "kernel.bin - File format not recognized"

The linker never complained though, so I guess its ok.
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Post by -m32 »

Well, if the linker didn't complain about 'OUTPUT_FORMAT("binary")' then you won't need to use objcopy.

All you should have to do is:

nasm -f elf -o start.o start.asm
gcc -o kernel.o -c kernel.c -ffreestanding -fno-builtin
ld -Tlink.ld -o kernel.bin start.o kernel.o

That's all I did and it booted fine.
brickhead20
Member
Member
Posts: 38
Joined: Mon Mar 24, 2008 4:17 pm

Post by brickhead20 »

That worked!

It turned out that using the elf flag in nasm did the trick. Before I had aout instead, and it didn't work.

Thanks m32, you've been really really helpful!

Now I'm interested as to why this is the case.

So why would it not work with the aout flag specified, but with the elf flag instead?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

So why would it not work with the aout flag specified, but with the elf flag instead?
Try disassembling the file - it's possible your readonly data is being put in a different section in the a.out object file and not being correctly mapped into the resulting binary executable. Using a fully expressive intermediate object file format (ELF/PE) is always recommended.
Actually, I think it's a more proper way to format a kernel (just doesn't make sense having an executable that has 2 headers, each from it's standard). Also, it's rather stupid the idea of presenting a boot protocol aimed to facilitate the interoperability between kernels and boot loaders, and then say "here is the format. then, if you are a boot loader, you also have to learn ELF (an OS specific format)"...

Sorry if I'm telling garbage! It's rather possible that there are many arguments for the multiboot spec to preview that the boot loader supports ELF, but I don't see them...
ELF is used by all UNIX operating system, so while it is specific, it's specific to an entire class/range of OS's. The advantage to a bootloader being able to understand the file format of a kernel is that it can load the extra, ancilliary sections into memory too. GRUB will load in your ELF symbol table for you, along with every other section you may need, including debug information. This is extremely useful, and much nicer than embedding the information directly in the executable (at a known location, for example).

One of the architectures I use has no support for ELF kernels, only SREC (a very similar format to raw binary). I ended up embedding my ELF kernel in the .data section of a 'third stage bootloader' SREC, parsing that embedded ELF and executing it, because that was the only way I could get all the ancilliary data I wanted.
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Post by -m32 »

Thanks m32, you've been really really helpful!
No problem. I'm glad it's finally working :)
brickhead20
Member
Member
Posts: 38
Joined: Mon Mar 24, 2008 4:17 pm

Post by brickhead20 »

ELF does seem to be quite a bit cleaner to use. I'm going to try to use that instead.

If I start with one format, would it be relatively easy to switch to another? Presumably the file type can be to some extent abstracted quite early, so only a small amount of code would need to be changed (provided the system is sufficiently modular).

I've just GASed myself my own assembly file, to help me understand, but the a.out kludge just seems messy to me.

Clearly ELF can be "wrapped" to work on other systems then, so sufficiently flexible.

Will look into compiling to ELF file format, while writing my own linker script (ish), so I don't have to pinch yours (JamesM)! I just like to feel I understand it all enough to write it.

Thanks again
brickhead20
Member
Member
Posts: 38
Joined: Mon Mar 24, 2008 4:17 pm

Post by brickhead20 »

Ooo, working with an elf file now I believe. And it takes less work?

Interesting.

Hopefully, C files will now work with no problems too. Rewriting from scratch.
Post Reply