diff --git a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
index 845aae51..1dee48e5 100644
--- a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
+++ b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
@@ -17,9 +17,84 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <sysdep.h>
+#include <stdint.h>
+#include <sys/auxv.h>
 
+/*
+ * vDSO data structure - must match the kernel's m68k_vdso_data struct.
+ * The TLS pointer is at offset 0 in the vDSO data page.
+ */
+struct m68k_vdso_data
+{
+  unsigned long tls_ptr;
+  unsigned long reserved[3];
+};
+
+/*
+ * Pointer to the vDSO data page.
+ * 0 = not yet initialized
+ * -1 = vDSO not available (fallback to syscall)
+ * other = valid vDSO data page address
+ */
+static volatile struct m68k_vdso_data *__m68k_vdso_data;
+
+/*
+ * Initialize the vDSO pointer on first use.
+ * Returns the pointer to the vDSO data, or NULL if not available.
+ */
+static inline struct m68k_vdso_data *
+__m68k_vdso_get (void)
+{
+  struct m68k_vdso_data *vdso = (struct m68k_vdso_data *) __m68k_vdso_data;
+
+  if (__glibc_unlikely (vdso == NULL))
+    {
+      /* Not yet initialized - try to get vDSO base from auxv */
+      unsigned long sysinfo_ehdr = __getauxval (AT_SYSINFO_EHDR);
+      if (sysinfo_ehdr != 0)
+	{
+	  /*
+	   * The kernel passes the address of the vDSO ELF header (code page).
+	   * The vDSO memory layout is:
+	   *   base + 0x0000: vvar (data page with TLS pointer, clock state)
+	   *   base + 0x1000: timer page (hardware timer registers)
+	   *   base + 0x2000: vDSO code (ELF, AT_SYSINFO_EHDR points here)
+	   * So the data page is 2 * PAGE_SIZE before the code page.
+	   */
+	  vdso = (struct m68k_vdso_data *) (sysinfo_ehdr - 2 * 4096);
+	}
+      else
+	{
+	  /* vDSO not available - mark as unavailable */
+	  vdso = (struct m68k_vdso_data *) (uintptr_t) -1;
+	}
+      __m68k_vdso_data = vdso;
+    }
+
+  /* Return NULL if vDSO is marked as unavailable */
+  if ((uintptr_t) vdso == (uintptr_t) -1)
+    return NULL;
+
+  return vdso;
+}
+
+/*
+ * Get the thread pointer.
+ *
+ * Fast path: Read directly from the vDSO data page if available.
+ * The kernel updates this on every context switch and set_thread_area() call.
+ *
+ * Slow path: Fall back to the get_thread_area syscall.
+ */
 void *
 __m68k_read_tp (void)
 {
-  return (void*) INTERNAL_SYSCALL_CALL (get_thread_area);
+  struct m68k_vdso_data *vdso = __m68k_vdso_get ();
+
+  /* Fast path: use vDSO if available */
+  if (__glibc_likely (vdso != NULL))
+    return (void *) vdso->tls_ptr;
+
+  /* Slow path: syscall fallback */
+  return (void *) INTERNAL_SYSCALL_CALL (get_thread_area);
 }
