SHF_TLS = 0x400,
   SHF_COMPRESSED = 0x800,
   SHF_MASKOS = 0x0ff00000,
+  SHF_GNU_RETAIN = 0x200000,
   SHF_MASKPROC = 0xf0000000,
 
   // Indicates this section requires ordering in relation to
 
     {
       // Some flags in the input section should not be automatically
       // copied to the output section.
-      elfcpp::Elf_Xword flags = (shdr.get_sh_flags()
-                                & ~ elfcpp::SHF_COMPRESSED);
+      elfcpp::Elf_Xword sh_flags = (shdr.get_sh_flags()
+                                   & ~ elfcpp::SHF_COMPRESSED);
       name = this->namepool_.add(name, true, NULL);
-      os = this->make_output_section(name, sh_type, flags,
-                                    ORDER_INVALID, false);
+      os = this->make_output_section(name, sh_type, sh_flags, ORDER_INVALID,
+                                    false);
     }
   else
     {
+      // Get the section flags and mask out any flags that do not
+      // take part in section matching.
+      elfcpp::Elf_Xword sh_flags
+         = (this->get_output_section_flags(shdr.get_sh_flags())
+            & ~object->osabi().ignored_sh_flags());
+
       // All ".text.unlikely.*" sections can be moved to a unique
       // segment with --text-unlikely-segment option.
       bool text_unlikely_segment
                             object->section_name(shndx).c_str()));
       if (text_unlikely_segment)
        {
-         elfcpp::Elf_Xword flags
-           = this->get_output_section_flags(shdr.get_sh_flags());
-
          Stringpool::Key name_key;
          const char* os_name = this->namepool_.add(".text.unlikely", true,
                                                    &name_key);
-         os = this->get_output_section(os_name, name_key, sh_type, flags,
+         os = this->get_output_section(os_name, name_key, sh_type, sh_flags,
                                        ORDER_INVALID, false);
          // Map this output section to a unique segment.  This is done to
          // separate "text" that is not likely to be executed from "text"
          if (it == this->section_segment_map_.end())
            {
              os = this->choose_output_section(object, name, sh_type,
-                                              shdr.get_sh_flags(), true,
-                                              ORDER_INVALID, false, false,
-                                              true);
+                                              sh_flags, true, ORDER_INVALID,
+                                              false, false, true);
            }
          else
            {
              // We know the name of the output section, directly call
              // get_output_section here by-passing choose_output_section.
-             elfcpp::Elf_Xword flags
-               = this->get_output_section_flags(shdr.get_sh_flags());
-
              const char* os_name = it->second->name;
              Stringpool::Key name_key;
              os_name = this->namepool_.add(os_name, true, &name_key);
-             os = this->get_output_section(os_name, name_key, sh_type, flags,
-                                       ORDER_INVALID, false);
+             os = this->get_output_section(os_name, name_key, sh_type,
+                                           sh_flags, ORDER_INVALID, false);
              if (!os->is_unique_segment())
                {
                  os->set_is_unique_segment();
 
     const elfcpp::Ehdr<size, big_endian>& ehdr)
   : Sized_relobj<size, big_endian>(name, input_file, offset),
     elf_file_(this, ehdr),
+    osabi_(ehdr),
     symtab_shndx_(-1U),
     local_symbol_count_(0),
     output_local_symbol_count_(0),
          if (this->is_section_name_included(name)
              || layout->keep_input_section (this, name)
              || sh_type == elfcpp::SHT_INIT_ARRAY
-             || sh_type == elfcpp::SHT_FINI_ARRAY)
+             || sh_type == elfcpp::SHT_FINI_ARRAY
+             || this->osabi().has_shf_retain(shdr.get_sh_flags()))
            {
              symtab->gc()->worklist().push_back(Section_id(this, i));
            }
 
                             const char* names, section_size_type names_size,
                             Object* obj, bool decompress_if_needed);
 
+// Osabi represents the EI_OSABI field from the ELF header.
+
+template <int size, bool big_endian>
+class Osabi
+{
+ public:
+  Osabi(const elfcpp::Ehdr<size, big_endian>& ehdr)
+    : ei_osabi_(static_cast<elfcpp::ELFOSABI>(
+                   ehdr.get_e_ident()[elfcpp::EI_OSABI]))
+  { }
+
+  bool
+  has_shf_retain(elfcpp::Elf_Xword sh_flags) const
+  {
+    switch (this->ei_osabi_)
+      {
+      case elfcpp::ELFOSABI_GNU:
+      case elfcpp::ELFOSABI_FREEBSD:
+       return (sh_flags & elfcpp::SHF_GNU_RETAIN) != 0;
+      default:
+        break;
+      }
+    return false;
+  }
+
+  elfcpp::Elf_Xword
+  ignored_sh_flags() const
+  {
+    switch (this->ei_osabi_)
+      {
+      case elfcpp::ELFOSABI_GNU:
+      case elfcpp::ELFOSABI_FREEBSD:
+       return elfcpp::SHF_GNU_RETAIN;
+      default:
+        break;
+      }
+    return 0;
+  }
+
+ private:
+  elfcpp::ELFOSABI ei_osabi_;
+};
+
 // Object is an abstract base class which represents either a 32-bit
 // or a 64-bit input object.  This can be a regular object file
 // (ET_REL) or a shared object (ET_DYN).
   e_type() const
   { return this->e_type_; }
 
+  // Return the EI_OSABI.
+  const Osabi<size, big_endian>&
+  osabi() const
+  { return this->osabi_; }
+
   // Return the number of symbols.  This is only valid after
   // Object::add_symbols has been called.
   unsigned int
 
   // General access to the ELF file.
   elfcpp::Elf_file<size, big_endian, Object> elf_file_;
+  // The EI_OSABI.
+  const Osabi<size, big_endian> osabi_;
   // Type of ELF file (ET_REL or ET_EXEC).  ET_EXEC files are allowed
   // as input files only for the --just-symbols option.
   int e_type_;
 
 pr26936d.o: pr26936d.s
        $(TEST_AS) --gen-debug -mx86-used-note=yes -o $@ $<
 
+check_SCRIPTS += retain.sh
+check_DATA += retain_1.out retain_2.out
+MOSTLYCLEANFILES += retain_1 retain_2
+retain_1.out: retain_1
+       $(TEST_NM) $< >$@
+retain_1: retain_1.o ../ld-new
+       ../ld-new  -e _start --gc-sections -o $@  retain_1.o
+retain_1.o: retain_1.s
+       $(TEST_AS) -o $@ $<
+retain_2.out: retain_2
+       $(TEST_READELF) -d $< >$@
+retain_2: retain_2.o ../ld-new
+       ../ld-new -pie -e _start --gc-sections -o $@  retain_2.o
+retain_2.o: retain_2.s
+       $(TEST_AS) -o $@ $<
+
 endif DEFAULT_TARGET_X86_64
 
 @DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@        split_s390x_z4_ns split_s390x_n1_ns split_s390x_n2_ns split_s390x_r
 
 @DEFAULT_TARGET_X86_64_TRUE@am__append_114 = *.dwo *.dwp pr26936a \
-@DEFAULT_TARGET_X86_64_TRUE@   pr26936b
+@DEFAULT_TARGET_X86_64_TRUE@   pr26936b retain_1 retain_2
 @DEFAULT_TARGET_X86_64_TRUE@am__append_115 = dwp_test_1.sh \
-@DEFAULT_TARGET_X86_64_TRUE@   dwp_test_2.sh pr26936.sh
+@DEFAULT_TARGET_X86_64_TRUE@   dwp_test_2.sh pr26936.sh retain.sh
 @DEFAULT_TARGET_X86_64_TRUE@am__append_116 = dwp_test_1.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@   dwp_test_2.stdout pr26936a.stdout \
-@DEFAULT_TARGET_X86_64_TRUE@   pr26936b.stdout
+@DEFAULT_TARGET_X86_64_TRUE@   pr26936b.stdout retain_1.out \
+@DEFAULT_TARGET_X86_64_TRUE@   retain_2.out
 subdir = testsuite
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/ax_pthread.m4 \
        --log-file $$b.log --trs-file $$b.trs \
        $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
        "$$tst" $(AM_TESTS_FD_REDIRECT)
+retain.sh.log: retain.sh
+       @p='retain.sh'; \
+       b='retain.sh'; \
+       $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+       --log-file $$b.log --trs-file $$b.trs \
+       $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+       "$$tst" $(AM_TESTS_FD_REDIRECT)
 object_unittest.log: object_unittest$(EXEEXT)
        @p='object_unittest$(EXEEXT)'; \
        b='object_unittest'; \
 @DEFAULT_TARGET_X86_64_TRUE@   $(TEST_AS) --gen-debug -mx86-used-note=yes -o $@ $<
 @DEFAULT_TARGET_X86_64_TRUE@pr26936d.o: pr26936d.s
 @DEFAULT_TARGET_X86_64_TRUE@   $(TEST_AS) --gen-debug -mx86-used-note=yes -o $@ $<
+@DEFAULT_TARGET_X86_64_TRUE@retain_1.out: retain_1
+@DEFAULT_TARGET_X86_64_TRUE@   $(TEST_NM) $< >$@
+@DEFAULT_TARGET_X86_64_TRUE@retain_1: retain_1.o ../ld-new
+@DEFAULT_TARGET_X86_64_TRUE@   ../ld-new  -e _start --gc-sections -o $@  retain_1.o
+@DEFAULT_TARGET_X86_64_TRUE@retain_1.o: retain_1.s
+@DEFAULT_TARGET_X86_64_TRUE@   $(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_X86_64_TRUE@retain_2.out: retain_2
+@DEFAULT_TARGET_X86_64_TRUE@   $(TEST_READELF) -d $< >$@
+@DEFAULT_TARGET_X86_64_TRUE@retain_2: retain_2.o ../ld-new
+@DEFAULT_TARGET_X86_64_TRUE@   ../ld-new -pie -e _start --gc-sections -o $@  retain_2.o
+@DEFAULT_TARGET_X86_64_TRUE@retain_2.o: retain_2.s
+@DEFAULT_TARGET_X86_64_TRUE@   $(TEST_AS) -o $@ $<
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 
--- /dev/null
+#!/bin/sh
+
+# retain.sh -- Tests for SHF_GNU_RETAIN.
+
+# Copyright (C) 2020 Free Software Foundation, Inc.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+set -e
+
+check()
+{
+    number_of_occurrence=`egrep "$2" ./$1 -o | wc -l`
+    if [ $number_of_occurrence != $3 ]
+    then
+       echo "$1: \"$2\" $3: Failed"
+       status=1
+    fi
+}
+
+status=0
+check retain_1.out " T fnretain1" 1
+check retain_1.out " b lsretain0.2" 1
+check retain_1.out " b lsretain1.1" 1
+check retain_1.out " d lsretain2.0" 1
+check retain_1.out " B retain0" 1
+check retain_1.out " B retain1" 1
+check retain_1.out " D retain2" 1
+check retain_1.out " b sretain0" 1
+check retain_1.out " b sretain1" 1
+check retain_1.out " d sretain2" 1
+if grep discard retain_1.out
+then
+  echo "retain_1.out: Garbage collection failed"
+  status=1
+fi
+
+check retain_2.out " \(PREINIT_ARRAY\)" 1
+check retain_2.out " \(PREINIT_ARRAYSZ\)" 1
+check retain_2.out " \(INIT_ARRAY\)" 1
+check retain_2.out " \(INIT_ARRAYSZ\)" 1
+check retain_2.out " \(FINI_ARRAY\)" 1
+check retain_2.out " \(FINI_ARRAYSZ\)" 1
+
+exit $status
 
--- /dev/null
+       .global discard0
+       .section        .bss.discard0,"aw"
+       .type   discard0, %object
+discard0:
+       .zero   2
+
+       .global discard1
+       .section        .bss.discard1,"aw"
+       .type   discard1, %object
+discard1:
+       .zero   2
+
+       .global discard2
+       .section        .data.discard2,"aw"
+       .type   discard2, %object
+discard2:
+       .word   1
+
+       .section        .bss.sdiscard0,"aw"
+       .type   sdiscard0, %object
+sdiscard0:
+       .zero   2
+
+       .section        .bss.sdiscard1,"aw"
+       .type   sdiscard1, %object
+sdiscard1:
+       .zero   2
+
+       .section        .data.sdiscard2,"aw"
+       .type   sdiscard2, %object
+sdiscard2:
+       .word   1
+
+       .section        .text.fndiscard0,"ax"
+       .global fndiscard0
+       .type   fndiscard0, %function
+fndiscard0:
+       .word 0
+
+       .global retain0
+       .section        .bss.retain0,"awR"
+       .type   retain0, %object
+retain0:
+       .zero   2
+
+       .global retain1
+       .section        .bss.retain1,"awR"
+       .type   retain1, %object
+retain1:
+       .zero   2
+
+       .global retain2
+       .section        .data.retain2,"awR"
+       .type   retain2, %object
+retain2:
+       .word   1
+
+       .section        .bss.sretain0,"awR"
+       .type   sretain0, %object
+sretain0:
+       .zero   2
+
+       .section        .bss.sretain1,"awR"
+       .type   sretain1, %object
+sretain1:
+       .zero   2
+
+       .section        .data.sretain2,"aRw"
+       .type   sretain2, %object
+sretain2:
+       .word   1
+
+       .section        .text.fnretain1,"Rax"
+       .global fnretain1
+       .type   fnretain1, %function
+fnretain1:
+       .word   0
+
+       .section        .text.fndiscard2,"ax"
+       .global fndiscard2
+       .type   fndiscard2, %function
+fndiscard2:
+       .word   0
+
+       .section        .bss.lsretain0,"awR"
+       .type   lsretain0.2, %object
+lsretain0.2:
+       .zero   2
+
+       .section        .bss.lsretain1,"aRw"
+       .type   lsretain1.1, %object
+lsretain1.1:
+       .zero   2
+
+       .section        .data.lsretain2,"aRw"
+       .type   lsretain2.0, %object
+lsretain2.0:
+       .word   1
+
+       .section        .text._start,"ax"
+       .global _start
+       .type   _start, %function
+_start:
+       .word 0
 
--- /dev/null
+       .section .preinit_array.01000,"aw",%preinit_array
+       .dc.a 0
+
+       .section .init_array.01000,"aw",%init_array
+       .dc.a 0
+
+       .section .fini_array.01000,"aw",%fini_array
+       .dc.a 0
+
+       .section .preinit_array.01000,"awR",%preinit_array
+       .dc.a 0
+
+       .section .init_array.01000,"awR",%init_array
+       .dc.a 0
+
+       .section .fini_array.01000,"awR",%fini_array
+       .dc.a 0
+
+       .text
+       .globl _start
+_start:
+       .dc.a 0