I'm finally at a point in my OSdev journey where i'm able to run a kernel executable from disk, so far it's been pretty good.
However, today I stumbled upon an issue that i'm not sure how to resolve and I'm also not sure if my approach is even remotely correct.
I used to call global constructors manually (via init_array in the linker script) and it worked fine, however, today I decided to use a less hacky(is it even?) approach,
where you have crt* files that define _init and _fini from this https://wiki.osdev.org/Calling_Global_Constructors tutorial. This worked fine for constructors, however,
when I decided to define a destructor for my class it resulted in an undefined symbol __cxa_atexit and __cxa_finalize, as far as I understand I'm supposed to define them myself,
so I did and it fixed the error, but calling _fini still doesn't call my destructor and for whatever reason __cxa_atexit is getting called in _init... I understand that you don't really need
destructors for a kernel, because it's not supposed to exit ever but I still don't understand why this isn't working and only constructors are getting called. Also please let me know how I
can make this better, and what i'm missing. Also, is this even the right approach?
Here are my files:
Entrypoint.asm
Code: Select all
extern _init
extern _fini
extern run
global __cxa_atexit
global __cxa_finalize
__cxa_atexit:
mov eax, 0
ret
__cxa_finalize:
mov eax, 0
ret
section .entry
global start
start:
; Set up the kernel stack
mov esp, kernel_stack_begin
; Call all global constructors
call _init
; Jump into kernel main
call run
; call global destructors
call _fini
hang:
cli
hlt
jmp hang
align 16
kernel_stack_end:
times 16384 db 0
kernel_stack_begin:
Code: Select all
ENTRY(start)
SECTIONS
{
/* also defined in UKLoader.asm */
. = 0x20000;
.text BLOCK(4K) : ALIGN(4K)
{
*(.entry)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}
Code: Select all
static constexpr unsigned long video_memory = 0xB8000;
void write_string(const char* string, unsigned char color = 0xF);
class Checker
{
public:
Checker()
{
write_string("Ctor called");
}
~Checker()
{
write_string("Dtor called");
}
};
static Checker c;
void write_string(const char* string, unsigned char color)
{
static unsigned short* memory = reinterpret_cast<unsigned short*>(video_memory);
while (*string)
{
unsigned short colored_char = *(string++);
colored_char |= color << 8;
*(memory++) = colored_char;
}
}
extern "C" void run()
{
write_string("Hello from the kernel!", 0x4);
}
Code: Select all
gpp = i686-elf-g++
gpp_compile_flags = -std=c++17 -ffreestanding -O2 -Wall -Wextra
nasm_compile_flags = -felf32
ld_flags = -Wl,--oformat=binary -ffreestanding -O2 -nostdlib -lgcc
object_files = Kernel.o Entrypoint.o
crti_obj = crti.o
crtbegin_obj:=$(shell $(gpp) $(gpp_compile_flags) -print-file-name=crtbegin.o)
crtend_obj :=$(shell $(gpp) $(gpp_compile_flags) -print-file-name=crtend.o)
crtn_obj = crtn.o
# Has to be in this order
obj_link_list := $(crti_obj) $(crtbegin_obj) $(object_files) $(crtend_obj) $(crtn_obj)
Kernel.bin: Linker.ld $(obj_link_list)
$(gpp) -T Linker.ld -o $@ $(ld_flags) $(obj_link_list)
%.o: %.cpp
$(gpp) $(gpp_compile_flags) -c $< -o $@
%.o: %.asm
nasm $(nasm_compile_flags) $< -o $@
.PHONY: clean
clean:
rm $(obj_link_list) Kernel.bin
Code: Select all
section .init
global _init
_init:
push ebp
mov ebp, esp
; ctors ^v dtors
section .fini
global _fini
_fini:
push ebp
mov ebp, esp
Code: Select all
section .init
pop ebp
ret
section .fini
pop ebp
ret
https://prnt.sc/s5pq5u
Thank you!