Page 1 of 2
GCC wrong .data addresses
Posted: Thu Mar 22, 2018 7:48 am
by AlexKuz
Hi,
I'm working on the second stage loader(in C).
When I switch to the protected mode, an initialized global variables don't work (.data section).
This code doesn't work:
Code: Select all
unsigned short* vmem = (unsigned short*)0xb8000;
void lmain()
{
for (unsigned int i = 0; i < 80 * 24; i++)
vmem[i] = 0;
while (1);
}
But this code works perfectly:
Code: Select all
unsigned short* vmem;
void lmain()
{
vmem = (unsigned short*)0xb8000;
for (unsigned int i = 0; i < 80 * 24; i++)
vmem[i] = 0;
while (1);
}
I'm compiling using x86_64-elf cross-compiler with these flags:
Code: Select all
-Wall -std=gnu99 -mcmodel=large -ffreestanding -fno-builtin -mno-red-zone -g -fno-stack-protector -fomit-frame-pointer
.
Here's my linker script:
Code: Select all
OUTPUT_FORMAT("binary")
ENTRY(start)
SECTIONS
{
. = 0x7c00;
.text : ALIGN(0x200)
{
*(.entry)
*(.text)
}
.rodata : ALIGN(0x200)
{
*(.rodata)
}
.data : ALIGN(0x200)
{
*(.data)
}
.bss : ALIGN(0x200)
{
*(.bss)
}
loader_end = .;
}
Help me please! Thanks in advance.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:05 am
by pvc
And what is the first stage loader? BIOS? MBR? Judging from your linker script it is one of these. If it is then you should know that BIOS and MBR loader only loads one sector of data (512 bytes). Since your sections are 512 byte aligned only first section (.text) gets loaded. That would explain why initialized data is not there. Change your section alignment to 1 and see if it helps. BTW. I don't think C is a good language choice for this.
EDIT: Ohh… and when do you switch to protected mode? Custom MBR or something?
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:12 am
by AlexKuz
pvc wrote:And what is the first stage loader? BIOS? MBR? Judging from your linker script it is one of these. If it is then you should know that BIOS and MBR loader only loads one sector of data (512 bytes). Since your sections are 512 byte aligned only first section (.text) gets loaded. That would explain why initialized data is not there. Change your section alignment to 1 and see if it helps. BTW. I don't think C is a good language choice for this.
The first stage loader is an MBR. It reads first sector from the active partition and jumps to the VBR.
The VBR is loading remaining sectors and switches to protected mode.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:20 am
by pvc
Ok. That changes things a little bit. But I would still suggest that you change section alignment to 1 byte to see what happens. Also, are you sure your start address (0x7C00) is right? If your code is linked for that address and you load it somewhere else it may not work since absolute adresses are simply wrong.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:29 am
by AlexKuz
pvc wrote:Ok. That changes things a little bit. But I would still suggest that you change section alignment to 1 byte to see what happens. Also, are you sure your start address (0x7C00) is right? If your code is linked for that address and you load it somewhere else it may not work since absolute adresses are simply wrong.
Changed section alignment to 1 and start address to 0x8000 but nothing changed.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:36 am
by pvc
It would help to see your loader code.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:41 am
by AlexKuz
Here it is:
Code: Select all
extern loader_end
extern lmain
section .entry
bits 16
global start
start:
mov ecx, [bx + 8]
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
mov eax, [loader_size]
shr eax, 9
inc eax
mov [dap.sectors], ax
mov eax, ecx
inc eax
mov [dap.lba], eax
mov [driveid], dl
mov sp, 0x8000
mov ah, 0x42
mov si, dap
int 0x13
jc .error
in al, 0x92
or al, 2
out 0x92, al
cli
lgdt [GDT32.Pointer]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:start32
.error:
mov si, disk_error
call print
jmp $
print:
lodsb
or al, al
jz .print_done
mov ah, 0x0e
int 0x10
jmp print
.print_done:
ret
loader_size dd loader_end - 0x8000
driveid db 0
dap:
.size db 0x10
.reserved db 0
.sectors dw 1
.offset dw 0x8200
.segment dw 0
.lba dd 0, 0
disk_error db 'Error: disk read failure!', 13, 10, 0
bits 32
GDT32:
.Null: equ $ - GDT32
dw 0
dw 0
db 0
db 0
db 0
db 0
.Code: equ $ - GDT32
dw 0xFFFF
dw 0
db 0
db 10011010b
db 11001111b
db 0
.Data: equ $ - GDT32
dw 0xFFFF
dw 0
db 0
db 10010010b
db 11001111b
db 0
.Code16: equ $ - GDT32
dw 0xFFFF
dw 0
db 0
db 10011010b
db 10001111b
db 0
.Data16: equ $ - GDT32
dw 0xFFFF
dw 0
db 0
db 10010010b
db 00001111b
db 0
.Pointer:
dw $ - GDT32 - 1
dd GDT32
section .text
start32:
xor eax, eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x8000
call lmain
jmp $
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:54 am
by pvc
I am trying to test your code and I ran into something like this:
Code: Select all
cc1: error: code model ‘large’ not supported in the 32 bit mode
when compiling the .c file with options you provided. I've added -m32 option because my compiler generates 64 bit code by default. Also -mno-red-zone is used for x86-64.
Are you sure you are generating 32 bit code?
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 8:59 am
by AlexKuz
pvc wrote:I am trying to test your code and I ran into something like this:
Code: Select all
cc1: error: code model ‘large’ not supported in the 32 bit mode
when compiling the .c file with options you provided. I've added -m32 option because my compiler generates 64 bit code by default. Also -mno-red-zone is used for x86-64.
Are you sure you are generating 32 bit code?
Should I generate exactly 32-bit code?
I'm planning to load a 64-bit kernel.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 9:06 am
by pvc
Yes. You should be generating 32 bit protected mode code for now. You have to get this working before you can switch to 64 bit long mode.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 9:08 am
by Schol-R-LEA
AlexKuz wrote:pvc wrote:I am trying to test your code and I ran into something like this:
Code: Select all
cc1: error: code model ‘large’ not supported in the 32 bit mode
when compiling the .c file with options you provided. I've added -m32 option because my compiler generates 64 bit code by default. Also -mno-red-zone is used for x86-64.
Are you sure you are generating 32 bit code?
Should I generate exactly 32-bit code?
I'm planning to load a 64-bit kernel.
That depends on just what mode you are in at this point of the loader. It sounds as if you have already switched to protected mode, but you would still need to switch again into long mode to run a 64-bit kernel. To the best of my knowledge, you cannot go directly from real mode to long mode; you need to switch to 32-bit protected mode first, then bump to long mode.
Comments and corrections welcome.
EDIT: hanzo'ed. Oh well.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 9:17 am
by pvc
@Schol-R-LEA It is possible to do 16->64 bit switch but it's a little bit tricky. It's easier to go 16->32->64.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 9:20 am
by AlexKuz
pvc wrote:@Schol-R-LEA It is possible to do 16->64 bit switch but it's a little bit tricky. It's easier to go 16->32->64.
Before switching to long mode, I need to do some things(e.g. load initrd, setup paging, set videomode).
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 9:34 am
by pvc
Actually setting up paging is mandatory for long mode to work.
Personally, I would suggest you to use GRUB. It gets you to stable protected mode environment, allows to load initird, changes video mode for you if you want to and what's most important… allows you to use multiboot image (which is plain old ELF with some extra header in it). What I do myself for testing is to use grub-mkrescue to make .iso image of my OS using files stored in a single directory.
EDIT: Here is a little something for you. You can use it if you want to.
Re: GCC wrong .data addresses
Posted: Thu Mar 22, 2018 1:01 pm
by AlexKuz
pvc wrote:Actually setting up paging is mandatory for long mode to work.
Personally, I would suggest you to use GRUB. It gets you to stable protected mode environment, allows to load initird, changes video mode for you if you want to and what's most important… allows you to use multiboot image (which is plain old ELF with some extra header in it). What I do myself for testing is to use grub-mkrescue to make .iso image of my OS using files stored in a single directory.
EDIT: Here is a little something for you. You can use it if you want to.
Thank you, but I want to write my own bootloader.
Maybe better to look aside UEFI?