diff --git a/configure b/configure
index efa836ca97646..fd3ebb2856c81 100755
--- a/configure
+++ b/configure
@@ -593,6 +593,7 @@ valopt musl-root "/usr/local" "MUSL root installation directory"
 opt_nosave manage-submodules 1 "let the build manage the git submodules"
 opt_nosave clang 0 "prefer clang to gcc for building the runtime"
 opt_nosave jemalloc 1 "build liballoc with jemalloc"
+opt elf-tls 1 "elf thread local storage on platforms where supported"
 
 valopt_nosave prefix "/usr/local" "set installation prefix"
 valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary"
diff --git a/mk/crates.mk b/mk/crates.mk
index 62dc1019066f0..93be1e6ba6326 100644
--- a/mk/crates.mk
+++ b/mk/crates.mk
@@ -150,3 +150,7 @@ TOOL_INPUTS_$(1) := $$(call rwildcard,$$(dir $$(TOOL_SOURCE_$(1))),*.rs)
 endef
 
 $(foreach crate,$(TOOLS),$(eval $(call RUST_TOOL,$(crate))))
+
+ifdef CFG_DISABLE_ELF_TLS
+RUSTFLAGS_std := --cfg no_elf_tls
+endif
diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs
index 0eafd4d5f12e9..cdd7dff010837 100644
--- a/src/libstd/thread/local.rs
+++ b/src/libstd/thread/local.rs
@@ -99,37 +99,60 @@ pub struct LocalKey<T> {
 
 /// Declare a new thread local storage key of type `std::thread::LocalKey`.
 ///
-/// See [LocalKey documentation](thread/struct.LocalKey.html) for more information.
+/// See [LocalKey documentation](thread/struct.LocalKey.html) for more
+/// information.
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow_internal_unstable]
+#[cfg(not(no_elf_tls))]
 macro_rules! thread_local {
     (static $name:ident: $t:ty = $init:expr) => (
-        static $name: ::std::thread::LocalKey<$t> = {
-            #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
-                           not(target_arch = "aarch64")),
-                       thread_local)]
-            static __KEY: ::std::thread::__LocalKeyInner<$t> =
-                ::std::thread::__LocalKeyInner::new();
-            fn __init() -> $t { $init }
-            fn __getit() -> &'static ::std::thread::__LocalKeyInner<$t> { &__KEY }
-            ::std::thread::LocalKey::new(__getit, __init)
-        };
+        static $name: ::std::thread::LocalKey<$t> =
+            __thread_local_inner!($t, $init,
+                #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
+                               not(target_arch = "aarch64")),
+                           thread_local)]);
     );
     (pub static $name:ident: $t:ty = $init:expr) => (
-        pub static $name: ::std::thread::LocalKey<$t> = {
-            #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
-                           not(target_arch = "aarch64")),
-                       thread_local)]
-            static __KEY: ::std::thread::__LocalKeyInner<$t> =
-                ::std::thread::__LocalKeyInner::new();
-            fn __init() -> $t { $init }
-            fn __getit() -> &'static ::std::thread::__LocalKeyInner<$t> { &__KEY }
-            ::std::thread::LocalKey::new(__getit, __init)
-        };
+        pub static $name: ::std::thread::LocalKey<$t> =
+            __thread_local_inner!($t, $init,
+                #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
+                               not(target_arch = "aarch64")),
+                           thread_local)]);
     );
 }
 
+#[macro_export]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[allow_internal_unstable]
+#[cfg(no_elf_tls)]
+macro_rules! thread_local {
+    (static $name:ident: $t:ty = $init:expr) => (
+        static $name: ::std::thread::LocalKey<$t> =
+            __thread_local_inner!($t, $init, #[]);
+    );
+    (pub static $name:ident: $t:ty = $init:expr) => (
+        pub static $name: ::std::thread::LocalKey<$t> =
+            __thread_local_inner!($t, $init, #[]);
+    );
+}
+
+#[doc(hidden)]
+#[unstable(feature = "thread_local_internals",
+           reason = "should not be necessary")]
+#[macro_export]
+#[allow_internal_unstable]
+macro_rules! __thread_local_inner {
+    ($t:ty, $init:expr, #[$($attr:meta),*]) => {{
+        $(#[$attr])*
+        static __KEY: ::std::thread::__LocalKeyInner<$t> =
+            ::std::thread::__LocalKeyInner::new();
+        fn __init() -> $t { $init }
+        fn __getit() -> &'static ::std::thread::__LocalKeyInner<$t> { &__KEY }
+        ::std::thread::LocalKey::new(__getit, __init)
+    }}
+}
+
 /// Indicator of the state of a thread local storage key.
 #[unstable(feature = "std_misc",
            reason = "state querying was recently added")]
@@ -163,7 +186,10 @@ pub enum LocalKeyState {
 
 impl<T: 'static> LocalKey<T> {
     #[doc(hidden)]
-    pub const fn new(inner: fn() -> &'static __KeyInner<T>, init: fn() -> T) -> LocalKey<T> {
+    #[unstable(feature = "thread_local_internals",
+               reason = "recently added to create a key")]
+    pub const fn new(inner: fn() -> &'static __KeyInner<T>,
+                     init: fn() -> T) -> LocalKey<T> {
         LocalKey {
             inner: inner,
             init: init
@@ -240,7 +266,9 @@ impl<T: 'static> LocalKey<T> {
     }
 }
 
-#[cfg(all(any(target_os = "macos", target_os = "linux"), not(target_arch = "aarch64")))]
+#[cfg(all(any(target_os = "macos", target_os = "linux"),
+          not(target_arch = "aarch64"),
+          not(no_elf_tls)))]
 #[doc(hidden)]
 mod imp {
     use prelude::v1::*;
@@ -371,7 +399,9 @@ mod imp {
     }
 }
 
-#[cfg(any(not(any(target_os = "macos", target_os = "linux")), target_arch = "aarch64"))]
+#[cfg(any(not(any(target_os = "macos", target_os = "linux")),
+          target_arch = "aarch64",
+          no_elf_tls))]
 #[doc(hidden)]
 mod imp {
     use prelude::v1::*;
diff --git a/src/libstd/thread/scoped_tls.rs b/src/libstd/thread/scoped_tls.rs
index dda1db9aecea4..f0a4c318d9127 100644
--- a/src/libstd/thread/scoped_tls.rs
+++ b/src/libstd/thread/scoped_tls.rs
@@ -60,9 +60,11 @@ pub struct ScopedKey<T> { inner: imp::KeyInner<T> }
 /// This macro declares a `static` item on which methods are used to get and
 /// set the value stored within.
 ///
-/// See [ScopedKey documentation](thread/struct.ScopedKey.html) for more information.
+/// See [ScopedKey documentation](thread/struct.ScopedKey.html) for more
+/// information.
 #[macro_export]
 #[allow_internal_unstable]
+#[cfg(not(no_elf_tls))]
 macro_rules! scoped_thread_local {
     (static $name:ident: $t:ty) => (
         #[cfg_attr(not(any(windows,
@@ -86,6 +88,20 @@ macro_rules! scoped_thread_local {
     );
 }
 
+#[macro_export]
+#[allow_internal_unstable]
+#[cfg(no_elf_tls)]
+macro_rules! scoped_thread_local {
+    (static $name:ident: $t:ty) => (
+        static $name: ::std::thread::ScopedKey<$t> =
+            ::std::thread::ScopedKey::new();
+    );
+    (pub static $name:ident: $t:ty) => (
+        pub static $name: ::std::thread::ScopedKey<$t> =
+            ::std::thread::ScopedKey::new();
+    );
+}
+
 #[unstable(feature = "scoped_tls",
            reason = "scoped TLS has yet to have wide enough use to fully consider \
                      stabilizing its interface")]
@@ -187,7 +203,8 @@ impl<T> ScopedKey<T> {
               target_os = "android",
               target_os = "ios",
               target_os = "openbsd",
-              target_arch = "aarch64")))]
+              target_arch = "aarch64",
+              no_elf_tls)))]
 mod imp {
     use std::cell::Cell;
 
@@ -208,7 +225,8 @@ mod imp {
           target_os = "android",
           target_os = "ios",
           target_os = "openbsd",
-          target_arch = "aarch64"))]
+          target_arch = "aarch64",
+          no_elf_tls))]
 mod imp {
     use prelude::v1::*;