diff --git a/gcc/config.in b/gcc/config.in
index 3657c46f349..8ef075a0ff3 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1346,6 +1346,12 @@
 #endif
 
 
+/* Define if your assembler supports the .retain directive. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GAS_RETAIN
+#endif
+
+
 /* Define if your assembler supports specifying the exclude section flag. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_GAS_SECTION_EXCLUDE
diff --git a/gcc/config/elfos.h b/gcc/config/elfos.h
index 74a3eafda6b..fab7b0e8ea4 100644
--- a/gcc/config/elfos.h
+++ b/gcc/config/elfos.h
@@ -474,3 +474,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #undef TARGET_LIBC_HAS_FUNCTION
 #define TARGET_LIBC_HAS_FUNCTION no_c99_libc_has_function
+
+/* If the assembler supports the .retain directive for saving a symbol
+   from linker garbage collection, define this macro.  */
+#if HAVE_GAS_RETAIN
+#undef TARGET_ASM_MARK_DECL_PRESERVED
+#define TARGET_ASM_MARK_DECL_PRESERVED default_elf_mark_decl_preserved
+#endif
diff --git a/gcc/configure b/gcc/configure
index abff47d30eb..37488eac25d 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -24223,6 +24223,41 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+# Test if the assembler supports the .retain directive for saving a symbol from
+# linker garbage collection.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for retain directive" >&5
+$as_echo_n "checking assembler for retain directive... " >&6; }
+if ${gcc_cv_as_retain_r+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_retain_r=no
+  if test x$gcc_cv_as != x; then
+    $as_echo '.retain retain_sym' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_retain_r=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_retain_r" >&5
+$as_echo "$gcc_cv_as_retain_r" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_GAS_RETAIN `if test $gcc_cv_as_retain_r = yes; then echo 1; else echo 0; fi`
+_ACEOF
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for section merging support" >&5
 $as_echo_n "checking assembler for section merging support... " >&6; }
 if ${gcc_cv_as_shf_merge+:} false; then :
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 26a5d8e3619..08b38d894a3 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -3216,6 +3216,14 @@ AC_DEFINE_UNQUOTED(HAVE_GAS_SECTION_EXCLUDE,
   [`if test $gcc_cv_as_section_exclude_e = yes || test $gcc_cv_as_section_exclude_hash = yes; then echo 1; else echo 0; fi`],
 [Define if your assembler supports specifying the exclude section flag.])
 
+# Test if the assembler supports the .retain directive for saving a symbol from
+# linker garbage collection.
+gcc_GAS_CHECK_FEATURE([retain directive], gcc_cv_as_retain_r,,,
+ [.retain retain_sym])
+AC_DEFINE_UNQUOTED(HAVE_GAS_RETAIN,
+  [`if test $gcc_cv_as_retain_r = yes; then echo 1; else echo 0; fi`],
+[Define if your assembler supports the .retain directive.])
+
 gcc_GAS_CHECK_FEATURE(section merging support, gcc_cv_as_shf_merge,
  [elf,2,12,0], [--fatal-warnings],
  [.section .rodata.str, "aMS", @progbits, 1])
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 62549b02452..4f77a5c0229 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3810,6 +3810,9 @@ When applied to a member function of a C++ class template, the
 attribute also means that the function is instantiated if the
 class itself is instantiated.
 
+As a GNU ELF extension, functions with this attribute will not be
+garbage collected by the linker.
+
 @item visibility ("@var{visibility_type}")
 @cindex @code{visibility} function attribute
 This attribute affects the linkage of the declaration to which it is attached.
@@ -7269,6 +7272,9 @@ When applied to a static data member of a C++ class template, the
 attribute also means that the member is instantiated if the
 class itself is instantiated.
 
+As a GNU ELF extension, variables with this attribute will not be
+garbage collected by the linker.
+
 @item vector_size (@var{bytes})
 @cindex @code{vector_size} variable attribute
 This attribute specifies the vector size for the type of the declared
diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index 49316a5d0ff..7fe77e7f09e 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -2551,6 +2551,9 @@ Target supports @option{-pie}, @option{-fpie} and @option{-fPIE}.
 @item rdynamic
 Target supports @option{-rdynamic}.
 
+@item retain
+Target supports the @code{.retain} assembler directive.
+
 @item scalar_all_fma
 Target supports all four fused multiply-add optabs for both @code{float}
 and @code{double}.  These optabs are: @code{fma_optab}, @code{fms_optab},
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 97437e8274f..b074b2ff75b 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -8773,7 +8773,7 @@ library function is given by @var{symref}, which is a @code{symbol_ref}.
 @deftypefn {Target Hook} void TARGET_ASM_MARK_DECL_PRESERVED (const char *@var{symbol})
 This target hook is a function to output to @var{asm_out_file} an assembler
 directive to annotate @var{symbol} as used.  The Darwin target uses the
-.no_dead_code_strip directive.
+.no_dead_code_strip directive, and ELF targets use the .retain directive.
 @end deftypefn
 
 @defmac ASM_OUTPUT_LABELREF (@var{stream}, @var{name})
diff --git a/gcc/output.h b/gcc/output.h
index eb253c50329..c0eba372c5d 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -609,6 +609,10 @@ extern void default_elf_init_array_asm_out_constructor (rtx, int);
 extern void default_elf_fini_array_asm_out_destructor (rtx, int);
 extern int maybe_assemble_visibility (tree);
 
+#if HAVE_GAS_RETAIN
+void default_elf_mark_decl_preserved (const char *);
+#endif
+
 extern int default_address_cost (rtx, machine_mode, addr_space_t, bool);
 
 /* Stack usage.  */
diff --git a/gcc/target.def b/gcc/target.def
index ed2da154e30..12164792b00 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -737,7 +737,7 @@ DEFHOOK
 (mark_decl_preserved,
  "This target hook is a function to output to @var{asm_out_file} an assembler\n\
 directive to annotate @var{symbol} as used.  The Darwin target uses the\n\
-.no_dead_code_strip directive.",
+.no_dead_code_strip directive, and ELF targets use the .retain directive.",
  void, (const char *symbol),
  hook_void_constcharptr)
 
diff --git a/gcc/testsuite/c-c++-common/attr-used-2.c b/gcc/testsuite/c-c++-common/attr-used-2.c
index f78b94b53a9..cf1d25a5b27 100644
--- a/gcc/testsuite/c-c++-common/attr-used-2.c
+++ b/gcc/testsuite/c-c++-common/attr-used-2.c
@@ -9,3 +9,4 @@ void foo()
 }
 
 /* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler "\\.retain\t\.*xyzzy" { target retain } } } */
diff --git a/gcc/testsuite/c-c++-common/attr-used.c b/gcc/testsuite/c-c++-common/attr-used.c
index ba7705aaa77..65a2f029698 100644
--- a/gcc/testsuite/c-c++-common/attr-used.c
+++ b/gcc/testsuite/c-c++-common/attr-used.c
@@ -11,3 +11,5 @@ static void function_declaration_after(void) __attribute__((__used__));
 
 /* { dg-final { scan-assembler "function_declaration_before" } } */
 /* { dg-final { scan-assembler "function_declaration_after" } } */
+/* { dg-final { scan-assembler "\\.retain\t\.*function_declaration_before" { target retain } } } */
+/* { dg-final { scan-assembler "\\.retain\t\.*function_declaration_after" { target retain } } } */
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 8439720baea..7c0e925f7b4 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -380,6 +380,15 @@ proc check_effective_target_noinit { } {
     return 0
 }
 
+# The .retain assembler directive is only supported by some targets.
+# This proc returns 1 if it's supported, 0 if it's not.
+
+proc check_effective_target_retain { } {
+    return [check_no_compiler_messages retain_available object {
+	__asm__(".retain used_var");
+    }]
+}
+
 ###############################
 # proc check_visibility_available { what_kind }
 ###############################
diff --git a/gcc/varasm.c b/gcc/varasm.c
index ea0b59cf44a..c38640456c4 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -8276,6 +8276,19 @@ default_elf_fini_array_asm_out_destructor (rtx symbol, int priority)
   assemble_addr_to_section (symbol, sec);
 }
 
+
+#if HAVE_GAS_RETAIN
+/* Implement TARGET_ASM_MARK_DECL_PRESERVED for ELF targets that support the
+   .retain assembler directive.  */
+void
+default_elf_mark_decl_preserved (const char *name)
+{
+  fprintf (asm_out_file, "\t.retain\t");
+  assemble_name (asm_out_file, name);
+  fputc ('\n', asm_out_file);
+}
+#endif
+
 /* Default TARGET_ASM_OUTPUT_IDENT hook.
 
    This is a bit of a cheat.  The real default is a no-op, but this
