Page 1 of 1
Compiling to 32 bit Kernel from 64 bit linux using gcc
Posted: Mon May 22, 2017 1:04 am
by yerri07
Hello,
I am trying to compile the kernel to 32 bit, under linux 64 bit using gcc compiler. I am succeeded in compiling the kernel to 32 bit but when i use
command kernel=Kernel/kernel.bin (using grub1) getting error message Invalid or unsupport executable format .
gcc -c -m32 entry.c -o entry.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
gcc -m32 -T linker.ld -o kernel.bin -ffreestanding -O2 -nostdlib entry.o -lgcc
By using above commands i am able to get 32 bit kernel elf executable format
My menu.lst
Os
root (fd0)
kernel=/Kernel/kernel.bin
My linker.ld
OUTPUT_FORMAT("elf32-i386")
ENTRY(Kernel_entry)
SECTIONS
{
. = 1M;
.text BLOCK(512) : ALIGN(512)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(512) : ALIGN(512)
{
*(.rodata)
}
.data BLOCK(512) : ALIGN(512)
{
*(.data)
}
.bss BLOCK(512) : ALIGN(512)
{
*(COMMON)
*(.bss)
}
}
Re: Compiling to 32 bit Kernel from 64 bit linux using gcc
Posted: Mon May 22, 2017 2:54 am
by no92
Please learn to read. You are
required to have a
GCC_Cross-Compiler. You should never just pass -m32 to your system compiler.
I don't know anything about GRUB Legacy, mostly because I haven't seen any reasonable justification to use it. Just compile GRUB2 from source and use that, it takes 3 minutes of your lifetime to do it properly.
At least set a cross compiler up, and report back.
Re: Compiling to 32 bit Kernel from 64 bit linux using gcc
Posted: Mon May 22, 2017 7:04 am
by MichaelPetch
Two concerns I have. The linker script suggests you are trying to make a multiboot compliant bootloader. You will need a multiboot header in your code. Most people place that in a separate assembly file and then make a call to their kernel's C entry point. In your case the header is expected to be located in the .multiboot section according to the linker script.
So first question is. Do you have a multiboot header?
Edit: Originally I commented about the multiboot directive in grub.cfg, but as was pointed out you are using Legacy Grub so using kernel= would be correct.
Re: Compiling to 32 bit Kernel from 64 bit linux using gcc
Posted: Mon May 22, 2017 7:14 am
by no92
He's using the kernel line because he's using GRUB
Legacy (OP called GRUB 1 in his post). There's no multiboot command there, so the
kernel command is right in this case. As I said above, I would still advise to just use GRUB2 instead.
Other than that, you're right.
Re: Compiling to 32 bit Kernel from 64 bit linux using gcc
Posted: Mon May 22, 2017 10:57 am
by MichaelPetch
In my first comment I suggested that most people simply create their multiboot header (and bootstrap) code in a separate assembly module. Although that is normal and cleaner it is possible to do in
C as well using basic inline assembly statements outside a function. A basic
entry.c that should work and be compatible with legacy multiboot:
Code: Select all
#include <multiboot/multiboot.h>
#include <stdint.h>
/* STRINGIZE is a C macro that allow us to convert an integer to a string
* for use by the C pre-processor */
#define STRINGIZE_INTERNAL(x) #x
#define STRINGIZE(x) STRINGIZE_INTERNAL(x)
/* 32k stack */
#define STACK_SIZE 32768
/* Define the multiboot structure that will be detectable by the multiboot
* loader. Request the loader to provide us a memory information */
#define MULTIBOOT_FLAGS MULTIBOOT_MEMORY_INFO
struct multiboot_header mb_header
__attribute__ ((aligned (4), section(".multiboot"))) = {
.magic = MULTIBOOT_HEADER_MAGIC,
.flags = MULTIBOOT_FLAGS,
.checksum = -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_FLAGS)
};
/* Allocate space for a stack */
uint8_t stack[STACK_SIZE];
/* Entry point set in linker script that the mulitboot loader will transfer control to */
extern void Kernel_entry(void);
__asm__ (".global Kernel_entry\n"
"Kernel_entry:\n\t"
/* Set stack pointer to end of stack variable.
Stack grows down. Align stack to 16 byte boundary */
"mov $stack + " STRINGIZE(STACK_SIZE) ", %esp\n\t"
"and $-16, %esp\n\t"
"cld\n\t" /* Ensure string instructions have forward movement */
"sub $8, %esp\n\t"/* For alignment on call to kmain */
"push %eax\n\t" /* Pass magicnum in EAX as 2nd parameter */
"push %ebx\n\t" /* Pass multiboot info struct in EBX as 1st parameter */
"call kmain\n\t" /* At this point stack 16 byte aligned, call kernel */
"add $16, %esp\n\t"
/* Infinite loop to end */
"cli\n"
".L0:\n\t"
"hlt\n\t"
"jmp .L0\n"
);
/* Text mode video pointer */
volatile uint16_t *const video_memory = (uint16_t *)0xb8000;
/* kmain is main C entry point */
void kmain(multiboot_info_t *mb_info, uint32_t magicnum)
{
(void)mb_info; /* Suppress warning */
/* Verify we were booted from multiboot loader and print MB to the display */
if (magicnum == MULTIBOOT_BOOTLOADER_MAGIC) {
video_memory[0] = 0x57 << 8 | 'M';
video_memory[1] = 0x57 << 8 | 'B';
}
}
As was mentioned by no92 using a cross compiler is much preferred to avoid potential problems. Using your host GCC may also present a particular problem if the build-id gets generated. This build id section may end up getting placed in such a way as to push your mulitboot header beyond the first 8k of the file where a multiboot loader may not detect it. I'd add the
-Wl,--build-id=none option to the GCC command that links and generated your final executable.
Re: Compiling to 32 bit Kernel from 64 bit linux using gcc
Posted: Mon May 22, 2017 11:35 pm
by yerri07
Thank you everyone and MichaelPetch for replies.