Page 1 of 1

Unable to link with libsupc++

Posted: Sun Apr 03, 2022 8:44 pm
by suncloudsmoon
Hi everyone,

I recently felt the need to add exception/runtime type info (RTTI) support to my C++ kernel. But, I enountered several unrecognizable linker errors while doing so. For example, I don't understand why the linker is complaining about these undefined references.

Code: Select all

 undefined reference to `_Unwind_Resume' 

Code: Select all

 i386-elf-ld: bin/ata.odriver:(.eh_frame+0x73): undefined reference to `__gxx_personality_v0' 
I tried to follow the libsupcxx OSDev tutorial and still didn't get to resolve these linker issues. I suspect either my Makefile to be the culprit or incorrect binary format (is the kernel only supposed be in ELF for exception/RTTI support?). Any help is appreciated :D . (source code attached below)

Makefile:

Code: Select all

# Build the kernel
CC := i386-elf-gcc
LD := i386-elf-ld
BIN_DIR = bin
ALL_SOURCE_FILES := $(wildcard kernel/*.cpp) $(wildcard lib/*.cpp)
ALL_OBJS := $(BIN_DIR)/kernel_entry.o $(BIN_DIR)/kernel_init.okernel $(BIN_DIR)/basic_io.okernel $(BIN_DIR)/basic_mem_util.okernel $(BIN_DIR)/fs.okernel $(BIN_DIR)/zio.olib $(BIN_DIR)/zstring.olib $(BIN_DIR)/zstringutil.olib $(BIN_DIR)/zassert.olib $(BIN_DIR)/zmem.olib $(BIN_DIR)/vga.odriver $(BIN_DIR)/ata.odriver $(BIN_DIR)/instr.odriver
CXX_FLAGS := -static -O2 -m32 -std=c++20 -ffreestanding -nostdlib -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
LD_FLAGS := -static -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++

# First, create a blank 1.44 MB (1,440 KB) floppy disk
# Second, copy os.bin to os_floppy.img
$(BIN_DIR)/os.hdd: $(BIN_DIR)/os.bin
	dd if=/dev/zero of=$@ bs=1024 count=1024 conv=notrunc
	dd if=$^ of=$@ bs=512 conv=notrunc

$(BIN_DIR)/os.bin: $(BIN_DIR)/boot.bin $(BIN_DIR)/kernel.bin
	cat $^ > $@

$(BIN_DIR)/boot.bin: boot/boot.asm
	nasm -f bin $^ -o $@

$(BIN_DIR)/kernel_entry.o: boot/kernel_entry.asm
	nasm -f elf32 $^ -o $@

$(BIN_DIR)/%.okernel: kernel/%.cpp
	$(CC) -Iinclude -c -o $@ $^ $(CXX_FLAGS) -lstdc++

$(BIN_DIR)/%.odriver: drivers/%.cpp
	$(CC) -Iinclude -c -o $@ $^ $(CXX_FLAGS) -lstdc++

$(BIN_DIR)/%.olib: lib/%.cpp
	$(CC) -Iinclude -c -o $@ $^ $(CXX_FLAGS) -lstdc++

$(BIN_DIR)/kernel.elf: $(ALL_OBJS)
	$(LD) -o $@ -Ttext=0x1000 $(LD_FLAGS) -elf32 $^

$(BIN_DIR)/kernel.bin: $(BIN_DIR)/kernel.elf
	$(LD) -o $@ -Ttext=0x1000 $^ --oformat=binary

clean:
	rm $(BIN_DIR)/*
Result from "make" command:

Code: Select all

boot/macros.asm:25: warning: redefining multi-line macro `outch' [-w+other]   
boot/macros.asm:31: warning: redefining multi-line macro `read_hdd' [-w+other]
boot/macros.asm:25: warning: redefining multi-line macro `outch' [-w+other]   
boot/macros.asm:31: warning: redefining multi-line macro `read_hdd' [-w+other]
i386-elf-ld: warning: cannot find entry symbol lf32; defaulting to 0000000000001000
i386-elf-ld: bin/kernel_init.okernel: in function `zl::growing_array<zl::string>::~growing_array()':      
kernel_init.cpp:(.text._ZN2zl13growing_arrayINS_6stringEED2Ev[_ZN2zl13growing_arrayINS_6stringEED5Ev]+0x66): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/kernel_init.okernel: in function `zl::vector<zl::string>::~vector()':
kernel_init.cpp:(.text._ZN2zl6vectorINS_6stringEED2Ev[_ZN2zl6vectorINS_6stringEED5Ev]+0x66): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/kernel_init.okernel: in function `zl::growing_array<zl::string>::~growing_array()':      
kernel_init.cpp:(.text._ZN2zl13growing_arrayINS_6stringEED0Ev[_ZN2zl13growing_arrayINS_6stringEED5Ev]+0x66): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: kernel_init.cpp:(.text._ZN2zl13growing_arrayINS_6stringEED0Ev[_ZN2zl13growing_arrayINS_6stringEED5Ev]+0x90): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/kernel_init.okernel: in function `zl::vector<zl::string>::~vector()':
kernel_init.cpp:(.text._ZN2zl6vectorINS_6stringEED0Ev[_ZN2zl6vectorINS_6stringEED5Ev]+0x66): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/kernel_init.okernel:kernel_init.cpp:(.text._ZN2zl6vectorINS_6stringEED0Ev[_ZN2zl6vectorINS_6stringEED5Ev]+0x90): more undefined references to `operator delete(void*, unsigned long)' follow       
i386-elf-ld: bin/kernel_init.okernel: in function `os::fs::cactus_fs::~cactus_fs()':
kernel_init.cpp:(.text._ZN2os2fs9cactus_fsD2Ev[_ZN2os2fs9cactus_fsD5Ev]+0x14): undefined reference to `operator delete[](void*)'
i386-elf-ld: kernel_init.cpp:(.text._ZN2os2fs9cactus_fsD2Ev[_ZN2os2fs9cactus_fsD5Ev]+0x40): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: kernel_init.cpp:(.text._ZN2os2fs9cactus_fsD2Ev[_ZN2os2fs9cactus_fsD5Ev]+0x55): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: kernel_init.cpp:(.text._ZN2os2fs9cactus_fsD2Ev[_ZN2os2fs9cactus_fsD5Ev]+0x88): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/kernel_init.okernel: in function `zl::shared_ptr<os::driv::ata::atapio>::~shared_ptr()': 
kernel_init.cpp:(.text._ZN2zl10shared_ptrIN2os4driv3ata6atapioEED2Ev[_ZN2zl10shared_ptrIN2os4driv3ata6atapioEED5Ev]+0x2d): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: kernel_init.cpp:(.text._ZN2zl10shared_ptrIN2os4driv3ata6atapioEED2Ev[_ZN2zl10shared_ptrIN2os4driv3ata6atapioEED5Ev]+0x42): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/kernel_init.okernel:kernel_init.cpp:(.text._ZN2zl10shared_ptrIN2os4driv3ata6atapioEED2Ev[_ZN2zl10shared_ptrIN2os4driv3ata6atapioEED5Ev]+0x74): more undefined references to `operator delete(void*, unsigned long)' follow
i386-elf-ld: bin/kernel_init.okernel: in function `main.cold':
kernel_init.cpp:(.text.unlikely+0x5): undefined reference to `operator delete[](void*)'
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0xd): undefined reference to `_Unwind_Resume'
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0x51): undefined reference to `_Unwind_Resume'
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0x5a): undefined reference to `operator delete[](void*)'     
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0x62): undefined reference to `_Unwind_Resume'
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0x6b): undefined reference to `operator delete[](void*)'     
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0x73): undefined reference to `_Unwind_Resume'
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0x7d): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: kernel_init.cpp:(.text.unlikely+0xa1): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/kernel_init.okernel: in function `main':
kernel_init.cpp:(.text.startup+0x46): undefined reference to `operator new[](unsigned long)'
i386-elf-ld: kernel_init.cpp:(.text.startup+0x85): undefined reference to `operator delete[](void*)'      
i386-elf-ld: kernel_init.cpp:(.text.startup+0xa4): undefined reference to `operator new[](unsigned long)' 
i386-elf-ld: kernel_init.cpp:(.text.startup+0xe3): undefined reference to `operator delete[](void*)'      
i386-elf-ld: kernel_init.cpp:(.text.startup+0x102): undefined reference to `operator new[](unsigned long)'i386-elf-ld: kernel_init.cpp:(.text.startup+0x141): undefined reference to `operator delete[](void*)'     
i386-elf-ld: kernel_init.cpp:(.text.startup+0x24f): undefined reference to `operator new(unsigned long)'  
i386-elf-ld: kernel_init.cpp:(.text.startup+0x26d): undefined reference to `operator new(unsigned long)'  
i386-elf-ld: bin/kernel_init.okernel:(.rodata._ZTIN2zl13growing_arrayINS_6stringEEE[_ZTIN2zl13growing_arrayINS_6stringEEE]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
i386-elf-ld: bin/kernel_init.okernel:(.rodata._ZTIN2zl6vectorINS_6stringEEE[_ZTIN2zl6vectorINS_6stringEEE]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
i386-elf-ld: bin/kernel_init.okernel:(.eh_frame+0x13): undefined reference to `__gxx_personality_v0'      
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::read_fs_info(unsigned long)':
fs.cpp:(.text+0x6e): undefined reference to `operator new[](unsigned long)'
i386-elf-ld: fs.cpp:(.text+0xbe): undefined reference to `operator delete[](void*)'
i386-elf-ld: fs.cpp:(.text+0x100): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::write_filesystem_info(unsigned long, os::fs::filesystem_info&)':
fs.cpp:(.text+0x20b): undefined reference to `operator new[](unsigned long)'
i386-elf-ld: fs.cpp:(.text+0x289): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::write_file_info(unsigned long, os::fs::file_info const&)':
fs.cpp:(.text+0x42c): undefined reference to `operator delete[](void*)'
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::format(unsigned long)':
fs.cpp:(.text+0x508): undefined reference to `operator new[](unsigned long)'
i386-elf-ld: fs.cpp:(.text+0x52f): undefined reference to `operator delete[](void*)'
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::read_fs_info(unsigned long) [clone .cold]':  
fs.cpp:(.text.unlikely+0xa): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: fs.cpp:(.text.unlikely+0x16): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::write_filesystem_info(unsigned long, os::fs::filesystem_info&) [clone .cold]':
fs.cpp:(.text.unlikely+0x20): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: fs.cpp:(.text.unlikely+0x28): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/fs.okernel: in function `os::fs::cactus_fs::cactus_fs(zl::shared_ptr<os::driv::ata::atapio> const&, unsigned long) [clone .cold]':
fs.cpp:(.text.unlikely+0x38): undefined reference to `operator delete[](void*)'
i386-elf-ld: fs.cpp:(.text.unlikely+0x5e): undefined reference to `operator delete(void*, unsigned long)' 
i386-elf-ld: fs.cpp:(.text.unlikely+0x6a): undefined reference to `_Unwind_Resume'
i386-elf-ld: fs.cpp:(.text.unlikely+0x8c): undefined reference to `operator delete(void*, unsigned long)' 
i386-elf-ld: fs.cpp:(.text.unlikely+0xa0): undefined reference to `operator delete(void*, unsigned long)' 
i386-elf-ld: bin/fs.okernel:(.eh_frame+0x3f): undefined reference to `__gxx_personality_v0'
i386-elf-ld: bin/zstring.olib: in function `zl::growing_array<zl::string>::add(zl::string const&) [clone .isra.0]':
zstring.cpp:(.text+0x7b): undefined reference to `operator new(unsigned long)'
i386-elf-ld: zstring.cpp:(.text+0x150): undefined reference to `operator delete(void*, unsigned long)'    
i386-elf-ld: bin/zstring.olib: in function `zl::growing_array<zl::string>::add(zl::string const&) [clone .isra.0] [clone .cold]':
zstring.cpp:(.text.unlikely+0x6): undefined reference to `operator delete(void*, unsigned long)'
i386-elf-ld: zstring.cpp:(.text.unlikely+0xe): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/zstring.olib: in function `zl::string::substr(unsigned long, unsigned long) [clone .cold]':
zstring.cpp:(.text.unlikely+0x20): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/zstring.olib: in function `zl::string::split(zl::string_view) [clone .cold]':
zstring.cpp:(.text.unlikely+0x48): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/zstring.olib:(.eh_frame+0x13): undefined reference to `__gxx_personality_v0'
i386-elf-ld: bin/ata.odriver: in function `os::driv::ata::atapio::read(int, unsigned long, unsigned short)':
ata.cpp:(.text+0x201): undefined reference to `operator new[](unsigned long)'
i386-elf-ld: ata.cpp:(.text+0x331): undefined reference to `operator delete[](void*)'
i386-elf-ld: bin/ata.odriver: in function `os::driv::ata::atapio::atapio(unsigned int) [clone .cold]':    
ata.cpp:(.text.unlikely+0xd): undefined reference to `_Unwind_Resume'
i386-elf-ld: bin/ata.odriver:(.eh_frame+0x73): undefined reference to `__gxx_personality_v0'
make: *** [Makefile:35: bin/kernel.elf] Error 1

Re: Unable to link with libsupc++

Posted: Sun Apr 03, 2022 8:55 pm
by kzinti
The first step would probably be to link with libgcc. This is where the exception unwinding code lives for GCC. You will have to either implement pthread in your kernel (libgcc depends on it) or modify libgcc and add your own threading model to it. Finally if you want to use the C++ library in your kernel, you might have to make significant changes to libsupc++ (or write your own C++ library). See viewtopic.php?f=15&t=42502 for more info.

Re: Unable to link with libsupc++

Posted: Sun Apr 03, 2022 9:29 pm
by suncloudsmoon
kzinti wrote:The first step would probably be to link with libgcc. This is where the exception unwinding code lives for GCC. You will have to either implement pthread in your kernel (libgcc depends on it) or modify libgcc and add your own threading model to it. Finally if you want to use the C++ library in your kernel, you might have to make significant changes to libsupc++ (or write your own C++ library). See viewtopic.php?f=15&t=42502 for more info.
I believe I did link with libgcc inside the Makefile (correct me if I'm wrong):

Code: Select all

CXX_FLAGS := -static -O2 -m32 -std=c++20 -ffreestanding -nostdlib -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
LD_FLAGS := -static -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
I don't know much about implemeting pthreads (or threading/scheduling on the kernel level), but the Libsupcxx OSDev article states that it's a weak symbol and it is optional to define them (correct me if I'm wrong).
There are even more functions you could support, like pthread_*, but since these are weak symbols, you don't need to define them.
Could you elaborate more on "[writing] your own C++ library". Are you implying that libsupc++ is not reliable to be incorporated inside my kernel without modifications?

Thank you for the help.

Re: Unable to link with libsupc++

Posted: Sun Apr 03, 2022 10:01 pm
by klange
suncloudsmoon wrote:

Code: Select all

CXX_FLAGS := -static -O2 -m32 -std=c++20 -ffreestanding -nostdlib -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
LD_FLAGS := -static -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
Try putting -lgcc after everything else. Ordering matters when linking archives, and dependencies must be to the right of the things that depend on them.

Re: Unable to link with libsupc++

Posted: Sun Apr 03, 2022 11:21 pm
by suncloudsmoon
klange wrote:
suncloudsmoon wrote:

Code: Select all

CXX_FLAGS := -static -O2 -m32 -std=c++20 -ffreestanding -nostdlib -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
LD_FLAGS := -static -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
Try putting -lgcc after everything else. Ordering matters when linking archives, and dependencies must be to the right of the things that depend on them.
I tried the above method and I still get the same linker errors.

Re: Unable to link with libsupc++

Posted: Sun Apr 03, 2022 11:28 pm
by suncloudsmoon
klange wrote:
suncloudsmoon wrote:

Code: Select all

CXX_FLAGS := -static -O2 -m32 -std=c++20 -ffreestanding -nostdlib -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
LD_FLAGS := -static -L/usr/local/i386elfgcc/lib/gcc/i386-elf/11.2.0 -lgcc -L/usr/local/i386elfgcc/lib -lsupc++
Try putting -lgcc after everything else. Ordering matters when linking archives, and dependencies must be to the right of the things that depend on them.
Strangely, replacing the below line(s) with the elf flag compiles fine. I have no idea why.

Code: Select all

$(BIN_DIR)/kernel.elf: $(ALL_OBJS)
   $(LD) -o $@ -Ttext=0x1000 $(LD_FLAGS) -elf32 $^
$(BIN_DIR)/kernel.bin: $(BIN_DIR)/kernel.elf
   $(LD) -o $@ -Ttext=0x1000 $^ --oformat=binary
with

Code: Select all

$(BIN_DIR)/kernel.bin: $(ALL_OBJS)
	$(LD) -elf32 -Ttext=0x1000 $^ -o $@ $(LD_FLAGS) 

Re: Unable to link with libsupc++

Posted: Mon Apr 04, 2022 7:39 am
by davmac314
suncloudsmoon wrote:Strangely, replacing the below line(s) with the elf flag compiles fine. I have no idea why.

Code: Select all

$(BIN_DIR)/kernel.elf: $(ALL_OBJS)
   $(LD) -o $@ -Ttext=0x1000 $(LD_FLAGS) -elf32 $^
$(BIN_DIR)/kernel.bin: $(BIN_DIR)/kernel.elf
   $(LD) -o $@ -Ttext=0x1000 $^ --oformat=binary
with

Code: Select all

$(BIN_DIR)/kernel.bin: $(ALL_OBJS)
	$(LD) -elf32 -Ttext=0x1000 $^ -o $@ $(LD_FLAGS) 
"-elf32" isn't a valid ld option, unless I'm missing something. It's certainly not documented. "-e" sets the start address so "-elf32" would set it to "lf32". Indeed when I try it on my system:

Code: Select all

ld: warning: cannot find entry symbol lf32; defaulting to 0000000000401000
Did you mean "--oformat=elf32-i386"?

That said, it makes no sense at all to me that the replacement line would link correctly when the original lines didn't.

Re: Unable to link with libsupc++

Posted: Mon Apr 04, 2022 7:27 pm
by suncloudsmoon
davmac314 wrote:
suncloudsmoon wrote:Strangely, replacing the below line(s) with the elf flag compiles fine. I have no idea why.

Code: Select all

$(BIN_DIR)/kernel.elf: $(ALL_OBJS)
   $(LD) -o $@ -Ttext=0x1000 $(LD_FLAGS) -elf32 $^
$(BIN_DIR)/kernel.bin: $(BIN_DIR)/kernel.elf
   $(LD) -o $@ -Ttext=0x1000 $^ --oformat=binary
with

Code: Select all

$(BIN_DIR)/kernel.bin: $(ALL_OBJS)
	$(LD) -elf32 -Ttext=0x1000 $^ -o $@ $(LD_FLAGS) 
"-elf32" isn't a valid ld option, unless I'm missing something. It's certainly not documented. "-e" sets the start address so "-elf32" would set it to "lf32". Indeed when I try it on my system:

Code: Select all

ld: warning: cannot find entry symbol lf32; defaulting to 0000000000401000
Did you mean "--oformat=elf32-i386"?

That said, it makes no sense at all to me that the replacement line would link correctly when the original lines didn't.
In my linker (the latest GNU Linker 2.38 configured for i386 platform), it strangely allowed me to compile "-elf32" without any errors. Anyway, thanks for the suggestion, I didn't know it was an invalid flag.

Re: Unable to link with libsupc++

Posted: Mon Apr 04, 2022 7:31 pm
by klange
suncloudsmoon wrote:In my linker (the latest GNU Linker 2.38 configured for i386 platform), it strangely allowed me to compile "-elf32" without any errors. Anyway, thanks for the suggestion, I didn't know it was an invalid flag.
I'm reasonably certain what you've done there is told the linker you want to make the entrypoint symbol "lf32" (which your linker script may then be overriding).
suncloudsmoon wrote:

Code: Select all

$(BIN_DIR)/kernel.elf: $(ALL_OBJS)
   $(LD) -o $@ -Ttext=0x1000 $(LD_FLAGS) -elf32 $^
$(BIN_DIR)/kernel.bin: $(BIN_DIR)/kernel.elf
   $(LD) -o $@ -Ttext=0x1000 $^ --oformat=binary
with

Code: Select all

$(BIN_DIR)/kernel.bin: $(ALL_OBJS)
	$(LD) -elf32 -Ttext=0x1000 $^ -o $@ $(LD_FLAGS) 
What you've actually changed here is moving the LD_FLAGS to the end, which is probably fine. Really, you want FLAGS to be the command-line options and LIBS to be the objects and archives, but that's nitpicking Makefile style...

Re: Unable to link with libsupc++

Posted: Mon Apr 04, 2022 10:39 pm
by nullplan
I am reasonably certain the option is supposed to be "-m elf32". Although my version of LD does not support that and requires "-m elf_i386" instead. However, all confusion could be avoided by just using a cross compiler, that would also set the architecture once and for all.