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?