diff --git a/linkers/i686.ld b/linkers/i686.ld
index 386ceff5129128a65ed65953b05842614fc14b99..0405e100a2e137ba82ea3769bd5b9703b9fa30de 100644
--- a/linkers/i686.ld
+++ b/linkers/i686.ld
@@ -33,19 +33,6 @@ SECTIONS {
         . = ALIGN(4K);
     }
 
-    .tdata ALIGN(4K) : AT(ADDR(.tdata) - KERNEL_OFFSET) {
-        __bss_end = .;
-        __tdata_start = .;
-        *(.tdata*)
-        . = ALIGN(4K);
-        __tdata_end = .;
-        __tbss_start = .;
-        *(.tbss*)
-        . += 8;
-        . = ALIGN(4K);
-        __tbss_end = .;
-    }
-
     __end = .;
 
     /DISCARD/ : {
diff --git a/linkers/x86_64.ld b/linkers/x86_64.ld
index fc8a626d60ef9dba1e81b0c202b59233df2a635a..9f90acf8daff72273e2a81369f8c326c3449d704 100644
--- a/linkers/x86_64.ld
+++ b/linkers/x86_64.ld
@@ -32,17 +32,6 @@ SECTIONS {
         *(.bss*)
     }
 
-    .tdata ALIGN(4K) : AT(ADDR(.tdata) - KERNEL_OFFSET) {
-        __bss_end = .;
-        __tdata_start = .;
-        *(.tdata*)
-        __tdata_end = .;
-        __tbss_start = .;
-        *(.tbss*)
-        . = ALIGN(4K);
-        __tbss_end = .;
-    }
-
     __end = .;
 
     /DISCARD/ : {
diff --git a/src/arch/x86/gdt.rs b/src/arch/x86/gdt.rs
index b3dc9d5ee896aeccb44269a9a9248d15ceee6c4d..fb73e23a79805847c7068e51b659a5d5958fb7e6 100644
--- a/src/arch/x86/gdt.rs
+++ b/src/arch/x86/gdt.rs
@@ -74,10 +74,9 @@ const BASE_GDT: [GdtEntry; 9] = [
 
 #[repr(C, align(4096))]
 pub struct ProcessorControlRegion {
-    pub tcb_end: usize,
+    pub self_ref: usize,
     pub user_rsp_tmp: usize,
     pub tss: TssWrapper,
-    pub self_ref: usize,
     pub gdt: [GdtEntry; 9],
     percpu: crate::percpu::PercpuBlock,
 }
@@ -138,8 +137,6 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: usize) {
         base: pcr.gdt.as_ptr() as *const SegmentDescriptor,
     };
 
-    pcr.tcb_end = init_percpu();
-
     {
         let tss = &pcr.tss.0 as *const _ as usize as u32;
 
@@ -172,28 +169,6 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: usize) {
     };
 }
 
-// TODO: Share code with x86. Maybe even with aarch64?
-/// Copy tdata, clear tbss, calculate TCB end pointer
-#[cold]
-unsafe fn init_percpu() -> usize {
-    use crate::kernel_executable_offsets::*;
-
-    let size = __tbss_end() - __tdata_start();
-    assert_eq!(size % PAGE_SIZE, 0);
-
-    let tbss_offset = __tbss_start() - __tdata_start();
-
-    let base_frame = crate::memory::allocate_frames(size / PAGE_SIZE).expect("failed to allocate percpu memory");
-    let base = RmmA::phys_to_virt(base_frame.start_address());
-
-    let tls_end = base.data() + size;
-
-    core::ptr::copy_nonoverlapping(__tdata_start() as *const u8, base.data() as *mut u8, tbss_offset);
-    core::ptr::write_bytes((base.data() + tbss_offset) as *mut u8, 0, size - tbss_offset);
-
-    tls_end
-}
-
 #[derive(Copy, Clone, Debug)]
 #[repr(packed)]
 pub struct GdtEntry {
diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs
index 411386e8b452feb787df0eff26ba072cc7964fbf..5add48cb46b268f5048c9eb55d6ea6c83bbee93e 100644
--- a/src/arch/x86_64/gdt.rs
+++ b/src/arch/x86_64/gdt.rs
@@ -3,7 +3,7 @@
 use core::convert::TryInto;
 use core::mem;
 
-use crate::paging::{PAGE_SIZE, RmmA, RmmArch};
+use crate::paging::{RmmA, RmmArch};
 use crate::percpu::PercpuBlock;
 
 use x86::bits64::task::TaskStateSegment;
@@ -74,12 +74,11 @@ const BASE_GDT: [GdtEntry; 8] = [
 pub struct ProcessorControlRegion {
     // TODO: When both KASLR and KPTI are implemented, the PCR may need to be split into two pages,
     // such that "secret" kernel addresses are only stored in the protected half.
+    pub self_ref: usize,
 
-    pub tcb_end: usize,
     pub user_rsp_tmp: usize,
     // TODO: The I/O permissions bitmap can require more than 8192 bytes of space.
     pub tss: TaskStateSegment,
-    pub self_ref: usize,
     // The GDT *must* be stored in the PCR! The paranoid interrupt handler, lacking a reliable way
     // to correctly obtain GSBASE, uses SGDT to calculate the PCR offset.
     pub gdt: [GdtEntry; 8],
@@ -164,8 +163,6 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: usize) {
         base,
     };
 
-    pcr.tcb_end = init_percpu();
-
     {
         pcr.tss.iomap_base = 0xFFFF;
 
@@ -220,27 +217,6 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: usize) {
     };
 }
 
-/// Copy tdata, clear tbss, calculate TCB end pointer
-#[cold]
-unsafe fn init_percpu() -> usize {
-    use crate::kernel_executable_offsets::*;
-
-    let size = __tbss_end() - __tdata_start();
-    assert_eq!(size % PAGE_SIZE, 0);
-
-    let tbss_offset = __tbss_start() - __tdata_start();
-
-    let base_frame = crate::memory::allocate_frames(size / PAGE_SIZE).expect("failed to allocate percpu memory");
-    let base = RmmA::phys_to_virt(base_frame.start_address());
-
-    let tls_end = base.data() + size;
-
-    core::ptr::copy_nonoverlapping(__tdata_start() as *const u8, base.data() as *mut u8, tbss_offset);
-    core::ptr::write_bytes((base.data() + tbss_offset) as *mut u8, 0, size - tbss_offset);
-
-    tls_end
-}
-
 #[derive(Copy, Clone, Debug)]
 #[repr(packed)]
 pub struct GdtEntry {
diff --git a/src/lib.rs b/src/lib.rs
index e4fcae030531d3d8a862eb6f354a59b2fca42787..e743be86d3b650ecb958de16d6829341bfec8ef1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -282,5 +282,5 @@ macro_rules! linker_offsets(
     }
 );
 pub mod kernel_executable_offsets {
-    linker_offsets!(__text_start, __text_end, __rodata_start, __rodata_end, __data_start, __data_end, __bss_start, __bss_end, __tdata_start, __tdata_end, __tbss_start, __tbss_end, __usercopy_start, __usercopy_end);
+    linker_offsets!(__text_start, __text_end, __rodata_start, __rodata_end, __data_start, __data_end, __bss_start, __bss_end, __usercopy_start, __usercopy_end);
 }
diff --git a/targets/x86_64-unknown-kernel.json b/targets/x86_64-unknown-kernel.json
index 1ab209b0a343ff496974387194df4cffc1a9cee3..db03c07d01b660e1bfb384dfa7bd9d3c623c12c3 100644
--- a/targets/x86_64-unknown-kernel.json
+++ b/targets/x86_64-unknown-kernel.json
@@ -23,6 +23,5 @@
     "exe-suffix": "",
     "has-rpath": false,
     "no-default-libraries": true,
-    "position-independent-executables": false,
-    "tls-model": "global-dynamic"
+    "position-independent-executables": false
 }