Page 1 of 1

How to link 32- and 64-bit code together?

Posted: Wed Mar 18, 2020 7:47 am
by aerkiaga
So, I'm compiling my 32-bit bootstrap code with the following commands:

Code: Select all

i686-elf-gcc -c $(C_FILE) -o $(O_FILE) -std=gnu99 -ffreestanding -O2 -Wall -Wextra -DKERNEL_MODE=64 #for C source files
i686-elf-gcc $(S_FILE) -o $(O_FILE) -ffreestanding -nostdlib -r -DKERNEL_MODE=64 #for .S assembly files
i686-elf-as $(S_FILE) -o $(O_FILE) #for .s assembly files
i686-elf-gcc -T boot32.ld -o boot32.o -ffreestanding -O2 -nostdlib -r $(OBJ32) -lgcc
Then compiling my 64-bit kernel with:

Code: Select all

x86_64-elf-gcc -c $(C_FILE) -o $(O_FILE) -std=gnu99 -ffreestanding -O2 -Wall -Wextra -DKERNEL_MODE=64 #for C source files
x86_64-elf-gcc $(S_FILE) -o $(O_FILE) -ffreestanding -nostdlib -r -DKERNEL_MODE=64 #for .S assembly files
x86_64-elf-objcopy -O elf64-x86-64 -B i386 -I binary $(BINARY_DATA) $(BINARY_OBJECT) #for binary data
Finally, I link everything with:

Code: Select all

x86_64-elf-gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot32.o $(OBJ64) -lgcc
At which point I get an error message:

Code: Select all

[...]x86_64-elf/bin/ld: i386 architecture of input file `boot32.o' is incompatible with i386:x86-64 output
And then, a couple undefined references in boot32.o to symbols defined in the 64-bit code. Now, what is the best way to link this together? I know I could manually parse the 64-bit symbol table, or even use objcopy somehow to move the 64-bit sections into the 32-bit object file by hand, with the two exported symbols at defined addresses. But this looks extremely ugly to do, and I think I'm missing something about 32- and 64-bit executable formats.

Also, I'm using Multiboot, so the bootstrap code *must* be 32-bit, and the entire image loadable by the bootloader.

Re: How to link 32- and 64-bit code together?

Posted: Thu Mar 19, 2020 1:13 am
by pvc
Compile everything as 64 bit. Link as 64 bit too. In your .S files add .code32 directive in places where you have 32 bit code and .code64 where you have 64 bit code (I am not very familiar with GNU assembler, but I think, this is how you do it). There is even .code16 directive if you need it. Loader file can be either raw binary, 32 bit ELF or 64 bit ELF. QEMU's built-in loader can't load 64 bit ELF files (not without a patch), but GRUB can. You will still start in 32 bit mode, but ELF file itself can be 64 bit.
Try lower half kernel first, since high half one needs some extra linker script tweaks and compiler flags.

Re: How to link 32- and 64-bit code together?

Posted: Thu Mar 19, 2020 4:19 am
by iansjack
Have a look at this wiki article, but be sure to read the linked material too: https://wiki.osdev.org/Creating_a_64-bit_kernel

I use Grub to start a 32-bit "kernel" which then jumps to a 64-bit module, as described in the article. Some information needs to be passed to the 64-bit code; I do this in a structure located at a fixed address (which can be overwritten once the data has been transferred).

Re: How to link 32- and 64-bit code together?

Posted: Thu Mar 19, 2020 8:06 am
by aerkiaga
I think I've come up with a solution:

Code: Select all

x86_64-elf-objcopy -O elf32-x86-64 -I elf32-i386 boot32.o boot32.o
x86_64-elf-objcopy -O elf32-x86-64 -I elf64-x86-64 kernel.o kernel.o
x86_64-elf-gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot32.o kernel.o -Xlinker -m -Xlinker elf32_x86_64
x86_64-elf-objcopy -O elf32-i386 -I elf32-x86-64 myos.bin myos.bin
It links everything together and emits no errors. All symbols are linked properly and have the right addresses as far as I've tested. I still haven't managed to get the 64-bit code to run though...

Re: How to link 32- and 64-bit code together?

Posted: Thu Mar 19, 2020 10:22 am
by aerkiaga
Okay, now it works perfectly. Does anybody want me to put this in the wiki? I mean, the solution wasn't really that obvious, it would be useful to have it in "Creating a 64-bit kernel" in case somebody wants to code (parts of) their 32-bit bootstrap in C and not just use assembly and ".code32" or have to write a dedicated loader.

Re: How to link 32- and 64-bit code together?

Posted: Thu Mar 19, 2020 10:52 am
by pvc
Correct me if I am wrong, but I think this method may be a little bit problematic for high half kernels and address spans larger that 4GiB. I mean, 32 bit ELF expects 32 bit addresses. Some relocation types may be impossible if you use 64 bit addresses in 32 bit ELF.

Re: How to link 32- and 64-bit code together?

Posted: Fri Mar 20, 2020 5:34 am
by aerkiaga
Don't worry, my original idea was to put my kernel in the first 4 GiB so addresses were the same in 32 and 64 bit modes. In fact, I'll possibly make a lower half kernel. I know it's a bad idea, but I think I know what I'm doing. Well, if someone finds out how to link this better, please tell me :)