Page 1 of 1

Linker runs but does not generate output

Posted: Mon Jan 23, 2023 8:36 pm
by Schol-R-LEA
I've been sweating bullets today over the paging (which is still a mess, though that's beside the point), but along the way ended up with a different problem entirely: the linker invocation runs without any error messages given, but it doesn't produce the kernel.elf file. I only noticed this because changes I was making were having no effect on the kernel output - the existing copy of the kernel file was being re-used, until I deleted it from the obj/ and found the installer failing.

The error messages I am getting are:

Code: Select all

 make
nasm -w+all -f bin -IVerbum/src/PC-x86/nasm/fat12 Verbum/src/PC-x86/nasm/fat12/verbum.asm -o obj/verbum.bin -l obj/verbum.lst
nasm -w+all -f bin -IVerbum/src/PC-x86/nasm/fat12 Verbum/src/PC-x86/nasm/fat12/stagetwo.asm -o obj/stagetwo.bin -l obj/stagetwo.lst
aux.asm:209: warning: uninitialized space declared in .text section: zeroing [-w+zeroing]
aux.asm:23: warning: uninitialized space declared in .text section: zeroing [-w+zeroing]
aux.asm:62: warning: uninitialized space declared in .text section: zeroing [-w+zeroing]
tables.asm:88: warning: uninitialized space declared in .text section: zeroing [-w+zeroing]
nasm -w+all -f elf32 src/kstart.asm -o obj/kstart.o -l obj/kstart.lst
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -I src/include -c src/terminal.c -o obj/terminal.o
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -I src/include -c src/mem.c -o obj/mem.o
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -I src/include -c src/paging.c -o obj/paging.o
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -mgeneral-regs-only -I src/include -c src/idt.c -o obj/idt.o
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -I src/include -c src/gdt.c -o obj/gdt.o
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -I src/include -c src/acpi.c -o obj/acpi.o
i686-elf-gcc -Wall -Werror -Wpedantic -std=c2x -ffreestanding -I src/include -c src/kernel.c -o obj/kernel.o
i686-elf-ld -T linker.ld
dd if=/dev/zero of=obj/boot.img count=1440 bs=1k
1440+0 records in
1440+0 records out
1474560 bytes (1.5 MB, 1.4 MiB) copied, 0.00553826 s, 266 MB/s
mkfs.msdos -F 12 -n "ORDO" obj/boot.img
mkfs.fat 4.2 (2021-01-31)
dd if=obj/verbum.bin of=obj/boot.img count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 0.000110209 s, 4.6 MB/s
mkdir -p temp
sudo mount obj/boot.img temp
[sudo] password for schol-r-lea: 
sudo cp obj/stagetwo.bin temp/STAGETWO.SYS
sudo cp obj/kernel.elf temp/KERNEL.SYS
cp: cannot stat 'obj/kernel.elf': No such file or directory
make: *** [Makefile:35: install] Error 1
The linker script is

Code: Select all

/* The bootloader will look at this image and start execution at the symbol
   designated at the entry point. */
ENTRY(kstart)

INPUT(
	obj/kernel.o
	obj/terminal.o
	obj/mem.o
	obj/idt.o
	obj/gdt.o
    obj/paging.o
	obj/acpi.o
)

OUTPUT(kernel.elf)

OUTPUT_FORMAT(elf32-i386)
STARTUP(obj/kstart.o)

/* Tell where the various sections of the object files will be put in the final
   kernel image. */
SECTIONS
{
	/* Begin putting sections at the higher half. */
	. = 0xC0000000;

	/* the .text section. */
	.text : ALIGN(4K)
	{
		*(.text)
	}

	/* Read-only data. */
	.rodata : ALIGN(4K)
	{
		*(.rodata)
	}

	/* Read-write data (initialized) */
	.data : ALIGN(4K)
	{
		*(.data)
	}

	/* Read-write data (uninitialized) and stack */
	.bss : ALIGN(4K)
	{
		*(COMMON)
		*(.bss)
	}

    /* hardware tables */
    . = 0xC0100000;
    .tables :
	{
		tables_base = .;
	}

    .boot_data BLOCK(4K) : ALIGN (4K)
    {
        boot_data = .;
		. = . + 4K;
    }

	.gdt BLOCK(64K) :
	{
		gdt = .;
		. = . + 64K;
	}

	.tss BLOCK(4K) : ALIGN(4K)
	{
		default_tss = .;
		. = . + 4K;
	}

    .idt BLOCK(4K) : ALIGN(4K)
	{
		idt = .;
		. = . + 4K;
	}

    .paging BLOCK(4K) : ALIGN(4K)
	{
        page_directory = .;
        . = . + 4K;
		page_tables = .;
		. = . + (1K * 4K);
	}

    /* set up the kernel stack */
    . = 0xC1000000;

    .stack :
	{
		kernel_stack_base = .;
		kernel_stack_top = . + 16K;
	}
}
while the Makefile is

Code: Select all

ASM = nasm -w+all
COPY = dd
FORMAT = mkfs.msdos -F 12 -n "ORDO"
REIMAGE=qemu-img
SYS_INSTALL = ~/Deployments/ms-sys-2.5.3/bin/ms-sys --fat12
BOOTPATH=Verbum/src/PC-x86/nasm/fat12
BOOT = verbum
STAGE_TWO = stagetwo
DISKTARGET = boot.img
DISKSIZE = 1440
CC=i686-elf-gcc
LD=i686-elf-ld
LINK_SCRIPT=linker.ld
CFLAGS=-Wall -Werror -Wpedantic -std=c2x -ffreestanding
C_SRC=src
C_INCLUDES=-I $(C_SRC)/include
OBJPATH=obj
KERNEL=kernel
KSTART=kstart
GDT=gdt
IDT=idt
TERMINAL=terminal
MEM=mem
PAGING=paging
ACPI=acpi


install: boot stage2 link
	$(COPY) if=/dev/zero of=$(OBJPATH)/$(DISKTARGET) count=$(DISKSIZE) bs=1k
	$(FORMAT) $(OBJPATH)/$(DISKTARGET)
	$(COPY) if=$(OBJPATH)/$(BOOT).bin of=$(OBJPATH)/$(DISKTARGET) count=1 conv=notrunc
	mkdir -p temp
	sudo mount $(OBJPATH)/$(DISKTARGET) temp
	sudo cp $(OBJPATH)/$(STAGE_TWO).bin temp/STAGETWO.SYS
	sudo cp $(OBJPATH)/$(KERNEL).elf temp/KERNEL.SYS
	sudo umount temp
	rmdir temp
	$(REIMAGE) convert -f raw -O qcow2 $(OBJPATH)/$(DISKTARGET) ordo.qcow2

link: kstart kernel terminal mem idt acpi paging gdt
	$(LD) -T $(LINK_SCRIPT)

kernel: terminal paging mem idt gdt acpi
	$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(KERNEL).c -o $(OBJPATH)/$(KERNEL).o

acpi: terminal
	$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(ACPI).c -o $(OBJPATH)/$(ACPI).o

idt: terminal
	$(CC) $(CFLAGS) -mgeneral-regs-only $(C_INCLUDES) -c $(C_SRC)/$(IDT).c -o $(OBJPATH)/$(IDT).o

gdt:
	$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(GDT).c -o $(OBJPATH)/$(GDT).o

paging: terminal mem
	$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(PAGING).c -o $(OBJPATH)/$(PAGING).o

mem: terminal
	$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(MEM).c -o $(OBJPATH)/$(MEM).o

terminal:
	$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(TERMINAL).c -o $(OBJPATH)/$(TERMINAL).o

kstart:
	$(ASM) -f elf32 $(C_SRC)/$(KSTART).asm -o $(OBJPATH)/$(KSTART).o -l $(OBJPATH)/$(KSTART).lst

boot:
	$(ASM) -f bin -I$(BOOTPATH) $(BOOTPATH)/$(BOOT).asm -o $(OBJPATH)/$(BOOT).bin -l $(OBJPATH)/$(BOOT).lst

stage2:
	$(ASM) -f bin -I$(BOOTPATH) $(BOOTPATH)/$(STAGE_TWO).asm -o $(OBJPATH)/$(STAGE_TWO).bin -l $(OBJPATH)/$(STAGE_TWO).lst
What puzzles me most is the lack of any sort of error message from ld.

Re: Linker runs but does not generate output

Posted: Mon Jan 23, 2023 8:46 pm
by Octocontrabass

Code: Select all

i686-elf-ld -T linker.ld
You're not passing any input files to the linker.

Re: Linker runs but does not generate output

Posted: Mon Jan 23, 2023 9:25 pm
by Schol-R-LEA
I... sigh I must have misunderstood the documentation for linker scripts. I had the impression that the entries in the INPUTS() and OUTPUT() fields were could be used in place of the command line arguments.

Code: Select all

INPUT(
	obj/kernel.o
	obj/terminal.o
	obj/mem.o
	obj/idt.o
	obj/gdt.o
    obj/paging.o
	obj/acpi.o
)

OUTPUT(kernel.elf)
Having reinstated the arguments, I am now finding that the line intended to move the kernel stack is causing a triple fault.

Code: Select all

   extern kernel_main
   extern kernel_stack_top
   global kstart

[SECTION .text]
kstart:
        mov esp, kernel_stack_top

        call kernel_main

   .halted_loop:
        hlt
        jmp short .halted_loop
If I comment out that mov it works as before, more or less.

Re: Linker runs but does not generate output

Posted: Mon Jan 23, 2023 9:29 pm
by Schol-R-LEA
RE: the linker script, the problem was that I hadn't included the path for the kernel.elf file in the OUTPUT() statement.

Re: Linker runs but does not generate output

Posted: Mon Jan 23, 2023 11:51 pm
by Octocontrabass
Schol-R-LEA wrote:I had the impression that the entries in the INPUTS() and OUTPUT() fields were could be used in place of the command line arguments.
I did not know the linker could do that. Whoops.

I've never seen anyone use that before. Other projects use the makefile to generate the list of file names and pass it to the linker; that way only the makefile needs to be updated when making changes instead of both the makefile and the linker script.
Schol-R-LEA wrote:If I comment out that mov it works as before, more or less.
It's hard to say for sure without more debugging information, but it sounds like your bootloader isn't preparing appropriate pages for the kernel stack. Does your bootloader actually use the program headers to decide which pages it maps, or does it create a fixed mapping that doesn't cover enough memory to include your new kernel stack?

Re: Linker runs but does not generate output

Posted: Mon Jan 23, 2023 11:58 pm
by Schol-R-LEA
Octocontrabass wrote:
Schol-R-LEA wrote:If I comment out that mov it works as before, more or less.
It's hard to say for sure without more debugging information, but it sounds like your bootloader isn't preparing appropriate pages for the kernel stack. Does your bootloader actually use the program headers to decide which pages it maps, or does it create a fixed mapping that doesn't cover enough memory to include your new kernel stack?
I'm not even certain what you mean by program headers, in this context. It is creating a fixed mapping.

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 12:04 am
by Schol-R-LEA
Just to clarify what I mean, here is the entirety of the boot loader's paging declarations:

Code: Select all

%ifndef _PAGING__INC__
%define _PAGING__INC__


%define PDE_Present           0b00000000000000000000000000000001
%define PDE_Read_Write        0b00000000000000000000000000000010
%define PDE_User              0b00000000000000000000000000000100
%define PDE_Write_Thru        0b00000000000000000000000000001000
%define PDE_Cache_Disable     0b00000000000000000000000000010000
%define PDE_Acccessed         0b00000000000000000000000000100000
%define PDE_Dirty             0b00000000000000000000000001000000
%define PDE_Page_Size         0b00000000000000000000000010000000
%define PDE_Global            0b00000000000000000000000100000000
%define PDE_Availability_Mask 0b00000000000000000000111000000000
%define PDE_Page_Attr_Table   0b00000000000000000001000000000000
%define PDE_Page_Index_Mask   0b11111111111111111110000000000000



%define PTE_Present           0b00000000000000000000000000000001
%define PTE_Read_Write        0b00000000000000000000000000000010
%define PTE_User              0b00000000000000000000000000000100
%define PTE_Write_Through     0b00000000000000000000000000001000
%define PTE_Cache_Disable     0b00000000000000000000000000010000
%define PTE_Acccessed         0b00000000000000000000000000100000
%define PTE_Dirty             0b00000000000000000000000001000000
%define PTE_Page_Attr_Table   0b00000000000000000000000010000000
%define PTE_Global            0b00000000000000000000000100000000
%define PTE_Availability_Mask 0b00000000000000000000111000000000
%define PTE_Page_Index_Mask   0b11111111111111111111000000000000

%endif
and the code that uses them:

Code: Select all

%ifndef _PAGING_CODE__INC__
%define _PAGING_CODE__INC__

%line 0, "paging.asm"
bits 32

page_directory           equ 0x0040000
page_table_0             equ page_directory + 0x1000
page_table_768           equ page_table_0   + 0x1000

init_page_directory:
        mov ebx, dword page_directory
        memset32 0, 0x0400, ebx                ; clear the page dir table

        ; start by setting up the base page table
        mov ebx, dword page_table_0            ; get index into the base page table
        memset32 0, 0x0400, ebx                ; clear the table entries
        ; entries 0-1024 - identity mapping the first 1 MiB of memory
        mov ecx, 0x0100                        ; 256 entries * 4KiB = 1 MiB
        mov eax, 0
    .pt_0_fill:
        mov edx, eax
        or edx, PTE_Present
        mov [ebx], dword edx
        add eax, 0x1000
        add ebx, 4
        loop .pt_0_fill

        ; set up the kernel code table
        mov ebx, page_table_768                ; get index into the kernel code page table
        memset32 0, 0x0400, ebx                ; clear the table entries
        ; entries 0-4096 - mapping the start of higher half
        mov ecx, 0x1000                        ; 4096 entries * 4KiB = 4 MiB
        mov eax, 0x00100000                    ; the 1MiB entry point
    .pt_768_fill:
        mov edx, eax
        or edx, PTE_Present
        mov [ebx], dword edx
        add eax, 0x1000
        add ebx, 4
        loop .pt_768_fill


    .setup_directory:
        mov ebx, page_directory
    .pd_fill:
        mov eax, page_table_0
        or eax, PDE_Present
        mov [ebx], eax
        add ebx, 768 * 4
        mov eax, page_table_768
        or eax, PDE_Present
        mov [ebx], eax


        ; set the page directory
        mov eax, page_directory
        mov cr3, eax
        ret


%endif
So clearly, it doesn't map the area I am trying to access.

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 12:41 am
by Schol-R-LEA
As a quick update, I simplified the existing code somewhat by writing a macro which populates a given page table.

Code: Select all

%macro populate_pte 3
        ; set up the page table
        mov ebx, %3                            ; get location to save the table entry
        memset32 0, 0x0400, ebx                ; clear the table entries
        ; entries 0-4096
        mov ecx, 0x1000                        ; 4096 entries * 4KiB = 4 MiB
        mov eax, %2                            ; the physical address to  map
    %%pt_fill:
        mov edx, eax
        or edx, PTE_Present
        mov [ebx], dword edx
        add eax, 0x1000
        add ebx, 4
        loop %%pt_fill

%endmacro
While this does not directly address the issue of mapping the new pages, it does make it easier to add new entries, provided that they are filling an entire page table at a time.

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 1:24 am
by Octocontrabass
Schol-R-LEA wrote:I'm not even certain what you mean by program headers, in this context.
I'm referring to the ELF program headers.

Since your bootloader performs fixed mappings, you could move the stack somewhere within the mapped area.

Or, if you're fine spending even more time on bootloader development, you could make your bootloader set up the higher half mappings according to the program headers. (Stuff like this is why I still haven't finished a bootloader...)

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 2:14 am
by Schol-R-LEA
I took the simpler option of writing macros to automate the entries, and just over-mapping the whole section. Which did in fact work, if crudely.

Code: Select all

%ifndef _PAGING__INC__
%define _PAGING__INC__


%define PDE_Present           0b00000000000000000000000000000001
%define PDE_Read_Write        0b00000000000000000000000000000010
%define PDE_User              0b00000000000000000000000000000100
%define PDE_Write_Thru        0b00000000000000000000000000001000
%define PDE_Cache_Disable     0b00000000000000000000000000010000
%define PDE_Acccessed         0b00000000000000000000000000100000
%define PDE_Dirty             0b00000000000000000000000001000000
%define PDE_Page_Size         0b00000000000000000000000010000000
%define PDE_Global            0b00000000000000000000000100000000
%define PDE_Availability_Mask 0b00000000000000000000111000000000
%define PDE_Page_Attr_Table   0b00000000000000000001000000000000
%define PDE_Page_Index_Mask   0b11111111111111111110000000000000



%define PTE_Present           0b00000000000000000000000000000001
%define PTE_Read_Write        0b00000000000000000000000000000010
%define PTE_User              0b00000000000000000000000000000100
%define PTE_Write_Through     0b00000000000000000000000000001000
%define PTE_Cache_Disable     0b00000000000000000000000000010000
%define PTE_Acccessed         0b00000000000000000000000000100000
%define PTE_Dirty             0b00000000000000000000000001000000
%define PTE_Page_Attr_Table   0b00000000000000000000000010000000
%define PTE_Global            0b00000000000000000000000100000000
%define PTE_Availability_Mask 0b00000000000000000000111000000000
%define PTE_Page_Index_Mask   0b11111111111111111111000000000000


%macro populate_pte 3
        ; set up the page table
        mov ebx, dword %3                      ; get location to save the table entry
        memset32 0, 0x0400, ebx                ; clear the table entries
        mov ecx, dword %2                      ; number of entries to fill
        mov eax, dword %1                      ; the physical address to map
    %%pt_fill:
        mov edx, eax
        or edx, PTE_Present
        mov [ebx], dword edx
        add eax, 0x1000
        add ebx, 4
        loop %%pt_fill
%endmacro


%macro populate_pde 2
        mov ebx, page_directory
        add ebx, %1 * 4
        mov eax, %2
        or eax, PDE_Present
        mov [ebx], eax
%endmacro

%endif

Code: Select all

%ifndef _PAGING_CODE__INC__
%define _PAGING_CODE__INC__

%line 0, "paging.asm"
bits 32

page_directory           equ 0x0040000
page_table_0             equ page_directory    + 0x1000
page_table_0x0300        equ page_table_0      + 0x1000
page_table_0x0301        equ page_table_0x0300 + 0x1000
page_table_0x0302        equ page_table_0x0301 + 0x1000
page_table_0x0303        equ page_table_0x0302 + 0x1000
page_table_0x0304        equ page_table_0x0303 + 0x1000
page_table_0x0305        equ page_table_0x0304 + 0x1000

init_page_directory:
        mov ebx, dword page_directory
        memset32 0, 0x0400, ebx                ; clear the page dir table

        populate_pte 0x00000000, 0x0100, page_table_0
        populate_pte 0x00100000, 0x1000, page_table_0x0300
        populate_pte 0x00400000, 0x1000, page_table_0x0301
        populate_pte 0x00800000, 0x1000, page_table_0x0302
        populate_pte 0x00C00000, 0x1000, page_table_0x0303
        populate_pte 0x01000000, 0x1000, page_table_0x0304
        populate_pte 0x01400000, 0x1000, page_table_0x0305

    .setup_directory:
        populate_pde 0, page_table_0
        populate_pde 0x0300, page_table_0x0300
        populate_pde 0x0301, page_table_0x0301
        populate_pde 0x0302, page_table_0x0302
        populate_pde 0x0303, page_table_0x0303
        populate_pde 0x0304, page_table_0x0304
        populate_pde 0x0305, page_table_0x0305

        ; set the page directory
        mov eax, page_directory
        mov cr3, eax
        ret

%endif

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 1:04 pm
by MichaelPetch
I'm curious if you have tried to git clone your project and build it from scratch in a new directory? I notice there is no `obj` directory so Make will fail and then there seems to be errors with your Color enum among other things.

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 4:30 pm
by Schol-R-LEA
Ah, OK, thanks for pointing that out. I'll add a mkdir -p obj to the Makefile.

Also, aside from calling magenta purple in two places, what problems are there with the Color enum? I'm sure there are some, but I don't see them offhand. I do know that you need to use -std=c2x for the typed enum support, but that's deliberate.

Re: Linker runs but does not generate output

Posted: Tue Jan 24, 2023 10:50 pm
by MichaelPetch
While I had a compiler that supports `-std=c2x` (still experimental) it lacks a feature that you now use. N2963 Enhancements to Enumerations. Appears this is added for milestone GCC 13, and I'm currently on GCC 12.2. As a result I get errors with enums defined as:

Code: Select all

enum Color : uint8_t
(with the underlying type being specified).

I assume you are using a bleeding edge version of GCC?

Re: Linker runs but does not generate output

Posted: Wed Jan 25, 2023 1:11 am
by Schol-R-LEA
Yes, I am. Perhaps a bit too bleeding edge, then.

Re: Linker runs but does not generate output

Posted: Wed Jan 25, 2023 3:04 am
by MichaelPetch
No problem. Might be something to mention in a README.