diff --git a/.cargo/config b/.cargo/config
new file mode 100644
index 00000000..fb871b1b
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1,2 @@
+[build]
+target = "x86_64-bootloader.json"
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 3915a6fd..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-language: rust
-
-rust:
-- nightly
-
-os:
-  - linux
-  - osx
-  - windows
-
-cache:
-  directories:
-    - $HOME/.cargo
-    - $HOME/Library/Caches/Homebrew
-    - $TRAVIS_BUILD_DIR/target
-    - $TRAVIS_BUILD_DIR/example-kernel/target
-
-addons:
-  apt:
-    packages:
-      - qemu-system-x86
-  homebrew:
-    packages:
-      - qemu
-
-install:
-  - if [ $TRAVIS_OS_NAME = windows ]; then choco install qemu; export PATH="/c/Program Files/qemu:$PATH"; fi
-
-before_script:
-- rustup component add rust-src
-- "(test -x $HOME/.cargo/bin/cargo-xbuild || cargo install cargo-xbuild)"
-
-sudo: false
-
-notifications:
-  email:
-    on_success: never
-    on_failure: change
-
-script:
-- cd example-kernel; cargo xbuild --target x86_64-example-kernel.json; cd ..
-- cd builder; cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel; cd ..
-- qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootimage.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none; if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi
-- cd builder; cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel --features vga_320x200; cd ..
-- qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootimage.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none; if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi
diff --git a/Cargo.lock b/Cargo.lock
index 1a8182d5..97cb59c6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,6 +24,7 @@ version = "0.4.0"
 dependencies = [
  "fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "font8x8 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -60,6 +61,11 @@ name = "libc"
 version = "0.2.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "llvm-tools"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "nodrop"
 version = "0.1.13"
@@ -206,6 +212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
 "checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
 "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
+"checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf"
 "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
 "checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a"
 "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
diff --git a/Cargo.toml b/Cargo.toml
index 635717a4..56282710 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,7 @@ description = "An experimental pure-Rust x86 bootloader."
 repository = "https://github.com/rust-osdev/bootloader"
 publish-lockfile = true
 edition = "2018"
+build = "build.rs"
 
 [dependencies]
 xmas-elf = "0.6.2"
@@ -21,6 +22,9 @@ version = "0.2.4"
 default-features = false
 features = ["unicode"]
 
+[build-dependencies]
+llvm-tools = "0.1"
+
 [features]
 default = []
 vga_320x200 = []
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e6111986..c7b8f1ae 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -10,8 +10,6 @@ trigger:
     - trying
     # Build pull requests.
     - master
-    # Build post braches
-    - post-*
 
 strategy:
   matrix:
@@ -64,11 +62,11 @@ steps:
   displayName: 'Print Rust Version'
   continueOnError: true
 
-- script: rustup component add rust-src
-  displayName: 'Install Rustup Src Component'
+- script: rustup component add rust-src llvm-tools-preview
+  displayName: 'Install Rustup Components'
 
-- script: cargo install cargo-xbuild bootimage --debug
-  displayName: 'Install cargo-xbuild and bootimage'
+- script: cargo install cargo-xbuild cargo-binutils --debug
+  displayName: 'Install cargo-xbuild cargo-binutils'
 
 - script: sudo apt update && sudo apt install qemu-system-x86
   condition: eq( variables['Agent.OS'], 'Linux' )
@@ -96,20 +94,15 @@ steps:
   workingDirectory: example-kernel
   displayName: 'Build Example Kernel'
 
-- script: cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel
-  workingDirectory: builder
+- script: cargo xbuild --release
   displayName: 'Build Bootloader'
+  env: { KERNEL: "example-kernel/target/x86_64-example-kernel/debug/example-kernel" }
+
+- script: cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin
+  displayName: 'Convert Bootloader ELF to Binary'
 
 - bash: |
-    qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootimage.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none
+    qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none
     if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi
   displayName: 'Test Bootloader'
 
-- script: cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel --features vga_320x200
-  workingDirectory: builder
-  displayName: 'Build Bootloader (Feature vga_320x200)'
-
-- bash: |
-    qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootimage.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none
-    if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi
-  displayName: 'Test Bootloader (Feature vga_320x200)'
diff --git a/build.rs b/build.rs
new file mode 100644
index 00000000..765e3f56
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,82 @@
+use std::{env, path::{Path, PathBuf}, process::{self, Command}};
+
+fn main() {
+    let target = env::var("TARGET").expect("TARGET not set");
+    if Path::new(&target)
+        .file_stem()
+        .expect("target has no file stem")
+        != "x86_64-bootloader"
+    {
+        return;
+    }
+
+    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
+    let kernel = PathBuf::from(match env::var("KERNEL") {
+        Ok(kernel) => kernel,
+        Err(_) => {
+            eprintln!(
+                "The KERNEL environment variable must be set for building the bootloader."
+            );
+            process::exit(1);
+        }
+    });
+
+    let kernel_file_name = kernel.file_name().expect("KERNEL has no valid file name").to_str().expect("kernel file name not valid utf8");
+    let kernel_file_name_replaced = kernel_file_name.replace('-', "_");
+    let kernel_out_path = out_dir.join(format!("kernel_bin-{}.o", kernel_file_name));
+    let kernel_archive_path = out_dir.join(format!("libkernel_bin-{}.a", kernel_file_name));
+
+    let llvm_tools = match llvm_tools::LlvmTools::new() {
+        Ok(tools) => tools,
+        Err(llvm_tools::Error::NotFound) => {
+            eprintln!("Error: llvm-tools not found");
+            eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
+            eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
+            process::exit(1);
+        }
+        Err(err) => {
+            eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
+            process::exit(1);
+        }
+    };
+    let objcopy = llvm_tools.tool(&llvm_tools::exe("llvm-objcopy")).expect("llvm-objcopy not found in llvm-tools");
+
+    let mut cmd = Command::new(objcopy);
+    cmd.arg("-I").arg("binary");
+    cmd.arg("-O").arg("elf64-x86-64");
+    cmd.arg("--binary-architecture=i386:x86-64");
+    cmd.arg("--rename-section").arg(".data=.kernel");
+    cmd.arg("--redefine-sym").arg(format!("_binary_{}_start=_kernel_start_addr", kernel_file_name_replaced));
+    cmd.arg("--redefine-sym").arg(format!("_binary_{}_end=_kernel_end_addr", kernel_file_name_replaced));
+    cmd.arg("--redefine-sym").arg(format!("_binary_{}_size=_kernel_size", kernel_file_name_replaced));
+    cmd.current_dir(kernel.parent().expect("KERNEL has no valid parent dir"));
+    cmd.arg(&kernel_file_name);
+    cmd.arg(&kernel_out_path);
+    let exit_status = cmd.status().expect("failed to run objcopy");
+    if !exit_status.success() {
+        eprintln!("Error: Running objcopy failed");
+        process::exit(1);
+    }
+
+    let ar = llvm_tools.tool(&llvm_tools::exe("llvm-ar")).unwrap_or_else(|| {
+        eprintln!("Failed to retrieve llvm-ar component");
+        eprint!("This component is available since nightly-XXXX-XX-XX,");
+        eprintln!("so try updating your toolchain if you're using an older nightly");
+        process::exit(1);
+    });
+    let mut cmd = Command::new(ar);
+    cmd.arg("crs");
+    cmd.arg(&kernel_archive_path);
+    cmd.arg(&kernel_out_path);
+    let exit_status = cmd.status().expect("failed to run ar");
+    if !exit_status.success() {
+        eprintln!("Error: Running ar failed");
+        process::exit(1);
+    }
+
+    println!("cargo:rerun-if-env-changed=KERNEL");
+    println!("cargo:rerun-if-changed={}", kernel.display());
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rustc-link-search=native={}", out_dir.display());
+    println!("cargo:rustc-link-lib=static=kernel_bin-{}", kernel_file_name);
+}
diff --git a/builder/.gitignore b/builder/.gitignore
deleted file mode 100644
index eccd7b4a..00000000
--- a/builder/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/target/
-**/*.rs.bk
diff --git a/builder/Cargo.lock b/builder/Cargo.lock
deleted file mode 100644
index 2493a19c..00000000
--- a/builder/Cargo.lock
+++ /dev/null
@@ -1,74 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "args"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "builder"
-version = "0.1.0"
-dependencies = [
- "args 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
- "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "byteorder"
-version = "1.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "getopts"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "log"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "unicode-width"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "xmas-elf"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "zero"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[metadata]
-"checksum args 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4b7432c65177b8d5c032d56e020dd8d407e939468479fc8c300e2d93e6d970b"
-"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
-"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
-"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
-"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
-"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
-"checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58"
-"checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5"
diff --git a/builder/Cargo.toml b/builder/Cargo.toml
deleted file mode 100644
index 30eb8319..00000000
--- a/builder/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "builder"
-version = "0.1.0"
-authors = ["Philipp Oppermann <dev@phil-opp.com>"]
-edition = "2018"
-
-[dependencies]
-xmas-elf = "0.6.2"
-byteorder = "1.2.7"
-args = "2.2.0"
-getopts = "0.2.18"
diff --git a/builder/src/main.rs b/builder/src/main.rs
deleted file mode 100644
index 8046388a..00000000
--- a/builder/src/main.rs
+++ /dev/null
@@ -1,224 +0,0 @@
-use args::Args;
-use byteorder::{ByteOrder, LittleEndian};
-use std::{
-    fs::File,
-    io::{self, Read, Write},
-    path::Path,
-    process,
-};
-
-const PROGRAM_NAME: &'static str = "builder";
-const PROGRAM_DESC: &'static str = "Builds the bootloader crate.";
-
-const BLOCK_SIZE: usize = 512;
-type KernelInfoBlock = [u8; BLOCK_SIZE];
-
-fn main() {
-    let mut args = args();
-
-    if help_arg_present() {
-        println!("{}", args.full_usage());
-        process::exit(0);
-    }
-
-    if let Err(args_err) = args.parse_from_cli() {
-        writeln!(io::stderr(), "{}", args_err).expect("Failed to write to stderr");
-        process::exit(1);
-    };
-
-    // load kernel
-
-    let kernel_path: String = args.value_of("kernel").unwrap();
-    let kernel_path = Path::new(&kernel_path);
-    let mut kernel_file = match File::open(kernel_path) {
-        Ok(file) => file,
-        Err(err) => {
-            writeln!(
-                io::stderr(),
-                "Failed to open kernel at {:?}: {}",
-                kernel_path,
-                err
-            )
-            .expect("Failed to write to stderr");
-            process::exit(1);
-        }
-    };
-    let kernel_size = kernel_file
-        .metadata()
-        .map(|m| m.len())
-        .unwrap_or_else(|err| {
-            writeln!(io::stderr(), "Failed to read size of kernel: {}", err)
-                .expect("Failed to write to stderr");
-            process::exit(1);
-        });
-    let kernel_info_block = create_kernel_info_block(kernel_size, None);
-
-    // build bootloader
-
-    let mut build_args = vec![
-        "--manifest-path".into(),
-        "../Cargo.toml".into(),
-        "--target".into(),
-        "../x86_64-bootloader.json".into(),
-        "--release".into(),
-    ];
-    if args.value_of("no-default-features").unwrap() {
-        build_args.push("--no-default-features".into());
-    }
-    if args.value_of("all-features").unwrap() {
-        build_args.push("--all-features".into());
-    }
-    if let Some(features) = args.optional_value_of("features").unwrap() {
-        build_args.push("--features".into());
-        build_args.push(features);
-    }
-
-    let exit_status = run_xbuild(&build_args);
-    if !exit_status.map(|s| s.success()).unwrap_or(false) {
-        process::exit(1)
-    }
-
-    let bootloader_elf_path = Path::new("../target/x86_64-bootloader/release/bootloader");
-    let mut bootloader_elf_bytes = Vec::new();
-    File::open(bootloader_elf_path)
-        .and_then(|mut f| f.read_to_end(&mut bootloader_elf_bytes))
-        .expect("failed to read bootloader ELF file");
-
-    // read bootloader section of ELF file
-
-    let elf_file = xmas_elf::ElfFile::new(&bootloader_elf_bytes).unwrap();
-    xmas_elf::header::sanity_check(&elf_file).unwrap();
-    let bootloader_section = elf_file
-        .find_section_by_name(".bootloader")
-        .expect("bootloader must have a .bootloader section");
-    let bootloader_bytes = bootloader_section.raw_data(&elf_file);
-
-    // create output file
-
-    let output_file_path = Path::new("../target/x86_64-bootloader/release/bootimage.bin");
-
-    let mut output_file = File::create(output_file_path).expect("Failed to create output file");
-    output_file
-        .write_all(bootloader_bytes)
-        .expect("Failed to write bootloader bytes to output file");
-    output_file
-        .write_all(&kernel_info_block)
-        .expect("Failed to write kernel info block to output file");
-
-    write_file_to_file(&mut output_file, &mut kernel_file)
-        .expect("Failed to write kernel to output file");
-    pad_file(&mut output_file, kernel_size as usize, &[0; 512]).expect("Failed to pad file");
-}
-
-fn args() -> Args {
-    use getopts::Occur;
-
-    let mut args = Args::new(PROGRAM_NAME, PROGRAM_DESC);
-    args.flag("h", "help", "Prints the help message");
-    args.option(
-        "",
-        "kernel",
-        "Path to the kernel ELF file",
-        "KERNEL_PATH",
-        Occur::Req,
-        None,
-    );
-    args.option(
-        "",
-        "features",
-        "Space-separated list of features to activate",
-        "FEATURES",
-        Occur::Optional,
-        None,
-    );
-    args.flag("", "all-features", "Activate all available features");
-    args.flag(
-        "",
-        "no-default-features",
-        "Do not activate the `default` feature",
-    );
-    args
-}
-
-fn help_arg_present() -> bool {
-    std::env::args()
-        .find(|a| a == "--help" || a == "-h")
-        .is_some()
-}
-
-fn run_xbuild(args: &[String]) -> io::Result<process::ExitStatus> {
-    let mut command = process::Command::new("cargo");
-    command.arg("xbuild");
-    command.args(args);
-    let exit_status = command.status()?;
-
-    if !exit_status.success() {
-        let mut help_command = process::Command::new("cargo");
-        help_command.arg("xbuild").arg("--help");
-        help_command.stdout(process::Stdio::null());
-        help_command.stderr(process::Stdio::null());
-        if let Ok(help_exit_status) = help_command.status() {
-            if !help_exit_status.success() {
-                let mut stderr = io::stderr();
-                writeln!(
-                    stderr,
-                    "Failed to run `cargo xbuild`. Perhaps it is not installed?"
-                )?;
-                writeln!(stderr, "Run `cargo install cargo-xbuild` to install it.")?;
-            }
-        }
-    }
-
-    Ok(exit_status)
-}
-
-fn create_kernel_info_block(kernel_size: u64, maybe_package_size: Option<u64>) -> KernelInfoBlock {
-    let kernel_size = if kernel_size <= u64::from(u32::max_value()) {
-        kernel_size as u32
-    } else {
-        panic!("Kernel can't be loaded by BIOS bootloader because is too big")
-    };
-
-    let package_size = if let Some(size) = maybe_package_size {
-        if size <= u64::from(u32::max_value()) {
-            size as u32
-        } else {
-            panic!("Package can't be loaded by BIOS bootloader because is too big")
-        }
-    } else {
-        0
-    };
-
-    let mut kernel_info_block = [0u8; BLOCK_SIZE];
-    LittleEndian::write_u32(&mut kernel_info_block[0..4], kernel_size);
-    LittleEndian::write_u32(&mut kernel_info_block[8..12], package_size);
-
-    kernel_info_block
-}
-
-fn write_file_to_file(output: &mut File, datafile: &mut File) -> io::Result<()> {
-    let data_size = datafile.metadata()?.len();
-    let mut buffer = [0u8; 1024];
-    let mut acc = 0;
-    loop {
-        let (n, interrupted) = match datafile.read(&mut buffer) {
-            Ok(0) => break,
-            Ok(n) => (n, false),
-            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (0, true),
-            Err(e) => Err(e)?,
-        };
-        if !interrupted {
-            acc += n;
-            output.write_all(&buffer[..n])?
-        }
-    }
-
-    assert!(data_size == acc as u64);
-
-    Ok(())
-}
-
-fn pad_file(output: &mut File, written_size: usize, padding: &[u8]) -> io::Result<()> {
-    let padding_size = (padding.len() - (written_size % padding.len())) % padding.len();
-    output.write_all(&padding[..padding_size])
-}
diff --git a/linker.ld b/linker.ld
index 119e697a..4e58320a 100644
--- a/linker.ld
+++ b/linker.ld
@@ -40,13 +40,11 @@ SECTIONS {
         *(.got)
         . = ALIGN(512);
         _rest_of_bootloader_end_addr = .;
+        __bootloader_end = .;
     }
 
-    _kernel_info_block_start = .;
-    _kib_kernel_size = .;
-    . += 512; /* kernel info block */
-    _kernel_info_block_end = .;
-
-    __bootloader_end = .;
-    _kernel_start_addr = .;
+    .kernel :
+    {
+        KEEP(*(.kernel))
+    }
 }
diff --git a/src/main.rs b/src/main.rs
index 742985fe..19160543 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,6 +7,9 @@
 #![no_std]
 #![no_main]
 
+#[cfg(not(target_os = "none"))]
+compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader.json` target");
+
 use bootloader::bootinfo::{BootInfo, FrameRange};
 use core::panic::PanicInfo;
 use core::{mem, slice};
@@ -63,7 +66,9 @@ impl IdentityMappedAddr {
 extern "C" {
     static mmap_ent: usize;
     static _memory_map: usize;
-    static _kib_kernel_size: usize;
+    static _kernel_start_addr: usize;
+    static _kernel_end_addr: usize;
+    static _kernel_size: usize;
     static __page_table_start: usize;
     static __page_table_end: usize;
     static __bootloader_end: usize;
@@ -77,7 +82,7 @@ pub unsafe extern "C" fn stage_4() -> ! {
           mov ss, bx" ::: "bx" : "intel");
 
     let kernel_start = 0x400000;
-    let kernel_size = _kib_kernel_size as u64;
+    let kernel_size = &_kernel_size as *const _ as u64;
     let memory_map_addr = &_memory_map as *const _ as u64;
     let memory_map_entry_count = (mmap_ent & 0xff) as u64; // Extract lower 8 bits
     let page_table_start = &__page_table_start as *const _ as u64;
diff --git a/src/stage_1.s b/src/stage_1.s
index 74ba5f7a..ebfa1a08 100644
--- a/src/stage_1.s
+++ b/src/stage_1.s
@@ -84,7 +84,7 @@ load_rest_of_bootloader_from_disk:
     mov [dap_buffer_addr], ax
 
     # number of disk blocks to load
-    lea ebx, _kernel_info_block_end
+    lea ebx, _rest_of_bootloader_end_addr
     sub ebx, eax # end - start
     shr ebx, 9 # divide by 512 (block size)
     mov [dap_blocks], bx
diff --git a/src/stage_2.s b/src/stage_2.s
index 7a39088e..5486439a 100644
--- a/src/stage_2.s
+++ b/src/stage_2.s
@@ -50,7 +50,7 @@ load_kernel_from_disk:
     mov edi, 0x400000
 
     # block count
-    mov ecx, _kib_kernel_size
+    lea ecx, _kernel_size
     add ecx, 511 # align up
     shr ecx, 9
 
diff --git a/src/stage_3.s b/src/stage_3.s
index 71121f96..0e7c03f7 100644
--- a/src/stage_3.s
+++ b/src/stage_3.s
@@ -52,7 +52,7 @@ set_up_page_tables:
     mov [_p2], eax
     mov eax, (0x400000 | 1 | 2 | (1 << 7))
     mov ecx, 2
-    mov edx, _kib_kernel_size
+    lea edx, _kernel_size
     add edx, 0x400000 # start address
     add edx, 0x200000 - 1 # align up
     shr edx, 12 + 9 # end huge page number