Page 1 of 1

[SOLVED] Problem with a bootloader written in GNU-Assembler

Posted: Sun Dec 19, 2021 2:45 pm
by liwinux
Hey everyone, as the title says, I'm writing a bootloader using GNU-Assembler with intel syntax. But I have a problem after loading sectors into memory and jumping to that address. Here I'm loading sectors to '0x9000'. Here is my bootloader :

Code: Select all

.intel_syntax noprefix
.code16 # use 16 bits
.global _init
.global _startup
.text

_init:
  .org 80 # Some BIOSes need a BPB, therefore we fill up space for a fake one

  jmp 0x0000, _startup # in case BIOS set cs to 0x7c00. We work with cs:ip

_startup:

  cld
  mov bp, 0x8000
  mov sp, bp  # 0x0000:0x7c00 below bootloader

  xor ax, ax
  mov ds, ax
  mov ss, ax
  mov es, ax

  movb [BOOT_DRIVE], dl

  mov bx, offset flat:start_16_str
  call print

  mov bx, offset flat:read_disk_str
  call print

  mov dh, 0x1
  mov bx, 0x9000 # memory location to load disk to
  call load_disk

  mov bx, offset flat:read_disk_success_str
  call print

  call check_lm

  mov bx, 0x9000 # Loading test strig from second sectors
  call print # It works perfectly
  
  jmp .

check_lm:
  pusha

  # Check if CPU support Long mode (64 bits)
  mov eax, 0x80000001
  cpuid
  test edx, (1 << 29)
  jz .lm_error
  mov bx, offset flat:lm_success_str
  call print
  jmp .lm_done


.lm_error:
  mov bx, offset flat:lm_error_str
  call print
  jmp .lm_done

.lm_done:
  popa
  ret


.include "print_16.S"
.include "read_disk.S"

start_16_str:
  .asciz "Starting in 16-bit mode"
read_disk_str:
  .asciz "Loading disk into memory"

read_disk_success_str:
  .asciz "Loaded disk successfully !"

lm_error_str:
  .asciz "ERROR: FATAL: CPU does not support Long Mode"

lm_success_str:
  .asciz "CPU support Long mode "
.set BOOT_DRIVE, 0
.space 510-(.-_init), 0 # add zeroes to make it 510 bytes long
.word 0xaa55 # magic bytes that tell BIOS that this is bootable

msg:
.asciz "Test string"

As Wrote in comment, after calling load_disk, we have loaded sector 2 to 0x0000:0x9000, which is fine as I can access the string. But here is the thing, I want to jump to that address but I can't. In other words, after setting BX to 0x9000 and calling 0x13, I want to go straight to that address. So for example, modifying my bootloader as this :

Code: Select all

...
.space 510-(.-_init), 0 # add zeroes to make it 510 bytes long
.word 0xaa55 # magic bytes that tell BIOS that this is bootable
#Beginning of second sector loaded to 0x0000:0x9000
mov bx, offset flat:msg
call print

msg:
.asciz "Test string"

I'm also leaving my linker script below :

Code: Select all

ENTRY(_init)
OUTPUT_FORMAT(binary)
SECTIONS {
    . = 0x7c00;
    .text : {
        *(.text*)
    }
    .rodata : {
        *(.rodata*)
    }
    .data : {
        *(.data*)
    }
    .bss : {
        *(.bss*)
    }
}
I know I'm doing something wrong but I can't figure out what the issue... Please be kind to me, I'm new to this topic and I'm really looking forward to learn new stuff !
Thank you all !

Re: Problem with a bootloader written using GNU-Assembler

Posted: Sun Dec 19, 2021 4:55 pm
by davmac314
What is "jmp ." supposed to be? Why don't you just jump to the intended address? 0x9200 or whatever (since you have a string at 0x9000 I guess the code is elsewhere, 0x9200 is an example, you need to use the correct value according to where the code is).

(More generally, when you say "I want to jump to that address but I can't", what do you mean by you can't? What exactly did you try, in what way did it fail?)

Re: Problem with a bootloader written using GNU-Assembler

Posted: Sun Dec 19, 2021 6:57 pm
by Octocontrabass
You're loading the second sector at 0x9000, but you told the assembler that the second sector will be loaded at 0x7e00. Addresses calculated by the assembler won't work if you do that.

Also, I don't see a near or short JMP at the beginning of your fake BPB. Are you executing the BPB?

Re: Problem with a bootloader written using GNU-Assembler

Posted: Sun Dec 19, 2021 7:53 pm
by liwinux
davmac314 wrote:What is "jmp ." supposed to be? Why don't you just jump to the intended address? 0x9200 or whatever (since you have a string at 0x9000 I guess the code is elsewhere, 0x9200 is an example, you need to use the correct value according to where the code is).
" jmp. " was used for debugging purpose. I used GDB in order to have a look at registers and stuff.
davmac314 wrote:(More generally, when you say "I want to jump to that address but I can't", what do you mean by you can't? What exactly did you try, in what way did it fail?)
My bad, what I wanted to say by this was, I couldn’t get to that particular address and continue executing my bootloader

Re: Problem with a bootloader written using GNU-Assembler

Posted: Sun Dec 19, 2021 8:02 pm
by liwinux
Octocontrabass wrote:You're loading the second sector at 0x9000, but you told the assembler that the second sector will be loaded at 0x7e00. Addresses calculated by the assembler won't work if you do that.
I must be blind, but I don’t see where I tell my assembler to load my second sector at 0x7e00 ?
Octocontrabass wrote: Also, I don't see a near or short JMP at the beginning of your fake BPB. Are you executing the BPB?
No, I’m not executing it in fact, it’s because I have a buggy BIOS that looks after a BPB, and if doesn’t find anyone, well It refused to execute my bootloader, so I’m king of faking it with zeros ..

As for my issue, I found some more informations. So my code was actually correct, but the problem here, is that I can’t print anything since labels after the first sector are not at 0x9000 for the assembler, so if I ask to load the address of “msg” well, it won’t load it from the memory segment es:bx but from 0x7exx since I’m using a linker script. So the goal is to tell my assembler that I load my second stage at 0x9000 and therefore labels that are in my second stage should be calculate correctly and not thinking that my second stage will be at 0x7e00

Maybe it’s not possible and therefore I have to load my second stage at 0x7e00 ?

Re: Problem with a bootloader written using GNU-Assembler

Posted: Sun Dec 19, 2021 8:42 pm
by Octocontrabass
liwinux wrote:I must be blind, but I don’t see where I tell my assembler to load my second sector at 0x7e00 ?
You told it to start at 0x7c00 and put the second sector 0x200 bytes later. That adds up to 0x7e00.
liwinux wrote:Maybe it’s not possible and therefore I have to load my second stage at 0x7e00 ?
It's possible, but you need to put each sector into its own section (or file) and update your linker script to tell the linker how they'll be laid out on disk and in memory. You'll end up with something that looks a bit like this.

Or like this:

Code: Select all

SECTIONS
{
   .sector1 0x7c00 : AT(0) {
      *(.sector1)
   }
   .sector2 0x9000 : AT(0x200) {
      *(.sector2)
   }
}
(I haven't tested this one.)

Re: Problem with a bootloader written using GNU-Assembler

Posted: Sun Dec 19, 2021 8:48 pm
by Schol-R-LEA
liwinux wrote:
Octocontrabass wrote:You're loading the second sector at 0x9000, but you told the assembler that the second sector will be loaded at 0x7e00. Addresses calculated by the assembler won't work if you do that.
I must be blind, but I don’t see where I tell my assembler to load my second sector at 0x7e00 ?
It isn't what you are telling the assembler to do, but what you aren't telling it.

Code: Select all

.space 510-(.-_init), 0 # add zeroes to make it 510 bytes long
.word 0xaa55 # magic bytes that tell BIOS that this is bootable 

msg:
.asciz "Test string"
As this stands, msg and the data following it is being generated at the first byte past the boot sector, which is at 0000:7e00.

Re: Problem with a bootloader written using GNU-Assembler

Posted: Mon Dec 20, 2021 5:40 am
by liwinux
Schol-R-LEA wrote:
liwinux wrote:
Octocontrabass wrote:You're loading the second sector at 0x9000, but you told the assembler that the second sector will be loaded at 0x7e00. Addresses calculated by the assembler won't work if you do that.
I must be blind, but I don’t see where I tell my assembler to load my second sector at 0x7e00 ?
It isn't what you are telling the assembler to do, but what you aren't telling it.

Code: Select all

.space 510-(.-_init), 0 # add zeroes to make it 510 bytes long
.word 0xaa55 # magic bytes that tell BIOS that this is bootable 

msg:
.asciz "Test string"
As this stands, msg and the data following it is being generated at the first byte past the boot sector, which is at 0000:7e00.
Now I see where my error was, that's what I thought would happen. So I was kind of correct but I didn't know how to fix it. Thank you !

Re: Problem with a bootloader written using GNU-Assembler

Posted: Mon Dec 20, 2021 5:48 am
by liwinux
Octocontrabass wrote:
liwinux wrote:I must be blind, but I don’t see where I tell my assembler to load my second sector at 0x7e00 ?
You told it to start at 0x7c00 and put the second sector 0x200 bytes later. That adds up to 0x7e00.
As I wrote previously, I now see where my error was !
Octocontrabass wrote:
liwinux wrote:Maybe it’s not possible and therefore I have to load my second stage at 0x7e00 ?
It's possible, but you need to put each sector into its own section (or file) and update your linker script to tell the linker how they'll be laid out on disk and in memory. You'll end up with something that looks a bit like this.

Or like this:

Code: Select all

SECTIONS
{
   .sector1 0x7c00 : AT(0) {
      *(.sector1)
   }
   .sector2 0x9000 : AT(0x200) {
      *(.sector2)
   }
}
(I haven't tested this one.)
Now, for this linker script part, I can't be sure if it's a problem with your linker script or not, because I've already tried something similar but after the linking part, the linker part doesn't produce any data within the final file. And it's the same story for the one you gave me !

So for example having this in the bootloader won't produce anything:

Code: Select all

.section .sector1
... some code ...
space 510-(.-_start), 0
.word 0xaa55
but having it like this is not an issue :

Code: Select all

.text
... some code ...
space 510-(.-_start), 0
.word 0xaa55
And I can't figure out why it does that