Preface
This tutorial assumes you have a compiler / assembler / linker toolchain capable of handling ELF files. On a Windows machine, you are strongly encouraged to set up a GCC Cross-Compiler, as it removes all the various toolchain-specific issues you might have ("PE operation on a non-PE file", "unsupported file format", and a number of others). While technically a Linux machine already has an ELF-capable toolchain, you are still encouraged to build a cross-compiler, as it is the first step to do, and it keeps you from relying on things you shouldn't (header files, for example).
The code in this tutorial assumes you are using GRUB to boot the system. GRUB (a Multiboot compliant boot loader) puts the system in to the correct state for your kernel to start executing. This includes enabling the A20 line (to give you access to all available memory addresses) and putting the system in to 32-bit Protected Mode, giving you access to a theoretical 4GiB of memory.
Overview
Even when using GRUB, some setup is required before entering an int main() type function. The most basic setup to get an ELF format kernel to be booted by GRUB consists of three files:
loader.s - assembler "glue" between bootloader and kernel
kernel.c - your actual kernel routines
linker.ld - for linking the above files
The second part of this tutorial briefly describes how to boot the compiled kernel.
loader.s
loader.s takes over over control from the Multiboot bootloader, and jumps into the kernel proper.
NASM
global loader ; making entry point visible to linker
extern kmain ; kmain is defined elsewhere
; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ 1<<0 ; align loaded modules on page boundaries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MODULEALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum required
section .text
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
; reserve initial kernel stack space
STACKSIZE equ 0x4000 ; that's 16k.
loader:
mov esp, stack+STACKSIZE ; set up the stack
push eax ; pass Multiboot magic number
push ebx ; pass Multiboot info structure
call kmain ; call kernel proper
...
Read more: OsDev.org wiki