diff mbox series

[v2,1/3] elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property

Message ID 20240930200822.1669666-2-adhemerval.zanella@linaro.org
State New
Headers show
Series elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property | expand

Commit Message

Adhemerval Zanella Netto Sept. 30, 2024, 8:08 p.m. UTC
Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.

The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
be memory-sealed if the loader supports it. Memory sealing is useful as
a hardening mechanism to avoid either remapping the memory segments or
changing the memory protection segments layout by the dynamic loader
(for instance, the RELRO hardening). The Linux 6.10
(8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
accomplishes it.

A GNU property is used instead of a new dynamic section tag (like the
one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
security features like x86 CET or AArch64 BTI.
---
 bfd/elf-properties.c                  | 72 ++++++++++++++++++++-------
 bfd/elfxx-x86.c                       |  3 +-
 binutils/readelf.c                    |  6 +++
 include/bfdlink.h                     |  3 ++
 include/elf/common.h                  |  1 +
 ld/NEWS                               |  3 ++
 ld/emultempl/elf.em                   |  4 ++
 ld/ld.texi                            |  8 +++
 ld/lexsup.c                           |  4 ++
 ld/testsuite/ld-elf/property-seal-1.d | 15 ++++++
 ld/testsuite/ld-elf/property-seal-2.d | 14 ++++++
 11 files changed, 115 insertions(+), 18 deletions(-)
 create mode 100644 ld/testsuite/ld-elf/property-seal-1.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-2.d

Comments

H.J. Lu Sept. 30, 2024, 8:52 p.m. UTC | #1
On Tue, Oct 1, 2024 at 4:09 AM Adhemerval Zanella
<adhemerval.zanella@linaro.org> wrote:
>
> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>
> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
> be memory-sealed if the loader supports it. Memory sealing is useful as
> a hardening mechanism to avoid either remapping the memory segments or
> changing the memory protection segments layout by the dynamic loader
> (for instance, the RELRO hardening). The Linux 6.10
> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
> accomplishes it.
>
> A GNU property is used instead of a new dynamic section tag (like the
> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
> security features like x86 CET or AArch64 BTI.
> ---
>  bfd/elf-properties.c                  | 72 ++++++++++++++++++++-------
>  bfd/elfxx-x86.c                       |  3 +-
>  binutils/readelf.c                    |  6 +++
>  include/bfdlink.h                     |  3 ++
>  include/elf/common.h                  |  1 +
>  ld/NEWS                               |  3 ++
>  ld/emultempl/elf.em                   |  4 ++
>  ld/ld.texi                            |  8 +++
>  ld/lexsup.c                           |  4 ++
>  ld/testsuite/ld-elf/property-seal-1.d | 15 ++++++
>  ld/testsuite/ld-elf/property-seal-2.d | 14 ++++++
>  11 files changed, 115 insertions(+), 18 deletions(-)
>  create mode 100644 ld/testsuite/ld-elf/property-seal-1.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-2.d
>
> diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
> index ee8bd37f2bd..17c89d9e876 100644
> --- a/bfd/elf-properties.c
> +++ b/bfd/elf-properties.c
> @@ -177,6 +177,9 @@ _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
>               prop->pr_kind = property_number;
>               goto next;
>
> +           case GNU_PROPERTY_MEMORY_SEAL:
> +             goto next;
> +
>             default:
>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
> @@ -258,6 +261,9 @@ elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
>          be added to ABFD.  */
>        return aprop == NULL;
>
> +    case GNU_PROPERTY_MEMORY_SEAL:
> +      return aprop == NULL;
> +
>      default:
>        updated = false;
>        if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
> @@ -607,6 +613,33 @@ elf_write_gnu_properties (struct bfd_link_info *info,
>      }
>  }
>
> +static asection *
> +_bfd_elf_link_create_gnu_property_sec (struct bfd_link_info *info, bfd *elf_bfd,
> +                                      unsigned int elfclass)
> +{
> +  asection *sec;
> +
> +  sec = bfd_make_section_with_flags (elf_bfd,
> +                                    NOTE_GNU_PROPERTY_SECTION_NAME,
> +                                    (SEC_ALLOC
> +                                     | SEC_LOAD
> +                                     | SEC_IN_MEMORY
> +                                     | SEC_READONLY
> +                                     | SEC_HAS_CONTENTS
> +                                     | SEC_DATA));
> +  if (sec == NULL)
> +    info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
> +
> +  if (!bfd_set_section_alignment (sec,
> +                                 elfclass == ELFCLASS64 ? 3 : 2))
> +    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
> +                           sec);
> +
> +  elf_section_type (sec) = SHT_NOTE;
> +  return sec;
> +}
> +
> +
>  /* Set up GNU properties.  Return the first relocatable ELF input with
>     GNU properties if found.  Otherwise, return NULL.  */
>
> @@ -656,23 +689,7 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
>        /* Support -z indirect-extern-access.  */
>        if (first_pbfd == NULL)
>         {
> -         sec = bfd_make_section_with_flags (elf_bfd,
> -                                            NOTE_GNU_PROPERTY_SECTION_NAME,
> -                                            (SEC_ALLOC
> -                                             | SEC_LOAD
> -                                             | SEC_IN_MEMORY
> -                                             | SEC_READONLY
> -                                             | SEC_HAS_CONTENTS
> -                                             | SEC_DATA));
> -         if (sec == NULL)
> -           info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
> -
> -         if (!bfd_set_section_alignment (sec,
> -                                         elfclass == ELFCLASS64 ? 3 : 2))
> -           info->callbacks->einfo (_("%F%pA: failed to align section\n"),
> -                                   sec);
> -
> -         elf_section_type (sec) = SHT_NOTE;
> +         sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
>           first_pbfd = elf_bfd;
>           has_properties = true;
>         }
> @@ -690,6 +707,27 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
>           |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
>      }
>
> +  if (info->memory_seal && elf_bfd != NULL)
> +    {
> +      /* Support -z no-memory-seal.  */
> +      if (first_pbfd == NULL)
> +       {
> +         sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
> +         first_pbfd = elf_bfd;
> +         has_properties = true;
> +       }
> +
> +      p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_MEMORY_SEAL, 0);
> +      if (p->pr_kind == property_unknown)
> +       {
> +         /* Create GNU_PROPERTY_NO_MEMORY_SEAL.  */
> +         p->u.number = GNU_PROPERTY_MEMORY_SEAL;
> +         p->pr_kind = property_number;
> +       }
> +      else
> +       p->u.number |= GNU_PROPERTY_MEMORY_SEAL;
> +    }
> +
>    /* Do nothing if there is no .note.gnu.property section.  */
>    if (!has_properties)
>      return NULL;
> diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
> index dd951b91f50..8a4405c8a79 100644
> --- a/bfd/elfxx-x86.c
> +++ b/bfd/elfxx-x86.c
> @@ -4815,7 +4815,8 @@ _bfd_x86_elf_link_fixup_gnu_properties
>    for (p = *listp; p; p = p->next)
>      {
>        unsigned int type = p->property.pr_type;
> -      if (type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
> +      if (type == GNU_PROPERTY_MEMORY_SEAL
> +         || type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
>           || type == GNU_PROPERTY_X86_COMPAT_ISA_1_NEEDED
>           || (type >= GNU_PROPERTY_X86_UINT32_AND_LO
>               && type <= GNU_PROPERTY_X86_UINT32_AND_HI)
> diff --git a/binutils/readelf.c b/binutils/readelf.c
> index 0f8dc1b9716..bf25425bb8d 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -21464,6 +21464,12 @@ print_gnu_property_note (Filedata * filedata, Elf_Internal_Note * pnote)
>                 printf (_("<corrupt length: %#x> "), datasz);
>               goto next;
>
> +           case GNU_PROPERTY_MEMORY_SEAL:
> +             printf ("memory seal ");
> +             if (datasz)
> +               printf (_("<corrupt length: %#x> "), datasz);
> +             goto next;
> +
>             default:
>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
> diff --git a/include/bfdlink.h b/include/bfdlink.h
> index f802ec627ef..8b9e391e6ff 100644
> --- a/include/bfdlink.h
> +++ b/include/bfdlink.h
> @@ -429,6 +429,9 @@ struct bfd_link_info
>    /* TRUE if only one read-only, non-code segment should be created.  */
>    unsigned int one_rosegment: 1;
>
> +  /* TRUE if GNU_PROPERTY_MEMORY_SEAL should be generated.  */
> +  unsigned int memory_seal: 1;
> +
>    /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
>       should be created.  1 for DWARF2 tables, 2 for compact tables.  */
>    unsigned int eh_frame_hdr_type: 2;
> diff --git a/include/elf/common.h b/include/elf/common.h
> index c9920e7731a..8938e2f4754 100644
> --- a/include/elf/common.h
> +++ b/include/elf/common.h
> @@ -890,6 +890,7 @@
>  /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0).  */
>  #define GNU_PROPERTY_STACK_SIZE                        1
>  #define GNU_PROPERTY_NO_COPY_ON_PROTECTED      2
> +#define GNU_PROPERTY_MEMORY_SEAL               3
>
>  /* A 4-byte unsigned integer property: A bit is set if it is set in all
>     relocatable inputs.  */
> diff --git a/ld/NEWS b/ld/NEWS
> index 1f14dd6bc77..4a28592fa32 100644
> --- a/ld/NEWS
> +++ b/ld/NEWS
> @@ -23,6 +23,9 @@ Changes in 2.43:
>
>  * Add -plugin-save-temps to store plugin intermediate files permanently.
>
> +* Add -z memory-seal/-z nomemory-seal options to ELF linker to mark the
> +  object to memory sealed.
> +
>  Changes in 2.42:
>
>  * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT
> diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
> index 2e865728587..ccd43531237 100644
> --- a/ld/emultempl/elf.em
> +++ b/ld/emultempl/elf.em
> @@ -1075,6 +1075,10 @@ fragment <<EOF
>         link_info.combreloc = false;
>        else if (strcmp (optarg, "nocopyreloc") == 0)
>         link_info.nocopyreloc = true;
> +      else if (strcmp (optarg, "memory-seal") == 0)
> +       link_info.memory_seal = true;
> +      else if (strcmp (optarg, "nomemory-seal") == 0)
> +       link_info.memory_seal = false;
>  EOF
>  if test -n "$COMMONPAGESIZE"; then
>  fragment <<EOF
> diff --git a/ld/ld.texi b/ld/ld.texi
> index 90182c436ec..b8957d3027e 100644
> --- a/ld/ld.texi
> +++ b/ld/ld.texi
> @@ -1591,6 +1591,14 @@ Disable relocation overflow check.  This can be used to disable
>  relocation overflow check if there will be no dynamic relocation
>  overflow at run-time.  Supported for x86_64.
>
> +@item memory-seal
> +@item nomemory-seal
> +Instruct the executable or shared library that the all PT_LOAD segments
> +should be sealed to avoid further manipulation (such as changing the
> +protection flags, the segment size, or remove the mapping).
> +This is a security hardening that requires system support.  This
> +generates GNU_PROPERTY_MEMORY_SEAL in .note.gnu.property section
> +
>  @item now
>  When generating an executable or shared library, mark it to tell the
>  dynamic linker to resolve all symbols when the program is started, or
> diff --git a/ld/lexsup.c b/ld/lexsup.c
> index 8982073bc91..d82d3631a09 100644
> --- a/ld/lexsup.c
> +++ b/ld/lexsup.c
> @@ -2271,6 +2271,10 @@ elf_shlib_list_options (FILE *file)
>        fprintf (file, _("\
>    -z textoff                  Don't treat DT_TEXTREL in output as error\n"));
>      }
> +  fprintf (file, _("\
> +  -z memory-seal              Mark object be memory sealed\n"));
> +  fprintf (file, _("\
> +  -z nomemory-seal            Don't mark oject to be memory sealed (default)\n"));
>  }
>
>  static void
> diff --git a/ld/testsuite/ld-elf/property-seal-1.d b/ld/testsuite/ld-elf/property-seal-1.d
> new file mode 100644
> index 00000000000..0b92a8eb6eb
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-1.d
> @@ -0,0 +1,15 @@
> +#source: empty.s
> +#ld: -shared -z memory-seal
> +#readelf: -n
> +#xfail: ![check_shared_lib_support]
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#...
> +Displaying notes found in: .note.gnu.property
> +[      ]+Owner[        ]+Data size[    ]+Description
> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
> +      Properties: memory seal
> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-2.d b/ld/testsuite/ld-elf/property-seal-2.d
> new file mode 100644
> index 00000000000..12339e83ebd
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-2.d
> @@ -0,0 +1,14 @@
> +#source: empty.s
> +#ld: -z memory-seal
> +#readelf: -n
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#...
> +Displaying notes found in: .note.gnu.property
> +[      ]+Owner[        ]+Data size[    ]+Description
> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
> +      Properties: memory seal
> +#pass
> --
> 2.34.1
>

Is GNU_PROPERTY_MEMORY_SEAL only generated by linker?
What happens when an input relocatable object file contains
GNU_PROPERTY_MEMORY_SEAL?
Jan Beulich Oct. 1, 2024, 6:45 a.m. UTC | #2
On 30.09.2024 22:08, Adhemerval Zanella wrote:
> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
> 
> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
> be memory-sealed if the loader supports it. Memory sealing is useful as
> a hardening mechanism to avoid either remapping the memory segments or
> changing the memory protection segments layout by the dynamic loader
> (for instance, the RELRO hardening). The Linux 6.10
> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
> accomplishes it.
> 
> A GNU property is used instead of a new dynamic section tag (like the
> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
> security features like x86 CET or AArch64 BTI.

What's wrong with DT_* in ET_EXEC type files? The first random executables
I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see
why a note would need using instead. As per H.J.'s question, a note would
also pose a possible issue (as to how to deal with it) when found in an
object file.

Jan
Florian Weimer Oct. 1, 2024, 8:20 a.m. UTC | #3
* Jan Beulich:

> On 30.09.2024 22:08, Adhemerval Zanella wrote:
>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>> 
>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
>> be memory-sealed if the loader supports it. Memory sealing is useful as
>> a hardening mechanism to avoid either remapping the memory segments or
>> changing the memory protection segments layout by the dynamic loader
>> (for instance, the RELRO hardening). The Linux 6.10
>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
>> accomplishes it.
>> 
>> A GNU property is used instead of a new dynamic section tag (like the
>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
>> security features like x86 CET or AArch64 BTI.
>
> What's wrong with DT_* in ET_EXEC type files? The first random executables
> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see
> why a note would need using instead. As per H.J.'s question, a note would
> also pose a possible issue (as to how to deal with it) when found in an
> object file.

Do we even need a new flag?  Could we reuse DF_1_NODELETE?

Thanks,
Florian
Adhemerval Zanella Netto Oct. 3, 2024, 12:22 p.m. UTC | #4
On 30/09/24 17:52, H.J. Lu wrote:
> On Tue, Oct 1, 2024 at 4:09 AM Adhemerval Zanella
> <adhemerval.zanella@linaro.org> wrote:
>>
>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>>
>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
>> be memory-sealed if the loader supports it. Memory sealing is useful as
>> a hardening mechanism to avoid either remapping the memory segments or
>> changing the memory protection segments layout by the dynamic loader
>> (for instance, the RELRO hardening). The Linux 6.10
>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
>> accomplishes it.
>>
>> A GNU property is used instead of a new dynamic section tag (like the
>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
>> security features like x86 CET or AArch64 BTI.
>> ---
>>  bfd/elf-properties.c                  | 72 ++++++++++++++++++++-------
>>  bfd/elfxx-x86.c                       |  3 +-
>>  binutils/readelf.c                    |  6 +++
>>  include/bfdlink.h                     |  3 ++
>>  include/elf/common.h                  |  1 +
>>  ld/NEWS                               |  3 ++
>>  ld/emultempl/elf.em                   |  4 ++
>>  ld/ld.texi                            |  8 +++
>>  ld/lexsup.c                           |  4 ++
>>  ld/testsuite/ld-elf/property-seal-1.d | 15 ++++++
>>  ld/testsuite/ld-elf/property-seal-2.d | 14 ++++++
>>  11 files changed, 115 insertions(+), 18 deletions(-)
>>  create mode 100644 ld/testsuite/ld-elf/property-seal-1.d
>>  create mode 100644 ld/testsuite/ld-elf/property-seal-2.d
>>
>> diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
>> index ee8bd37f2bd..17c89d9e876 100644
>> --- a/bfd/elf-properties.c
>> +++ b/bfd/elf-properties.c
>> @@ -177,6 +177,9 @@ _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
>>               prop->pr_kind = property_number;
>>               goto next;
>>
>> +           case GNU_PROPERTY_MEMORY_SEAL:
>> +             goto next;
>> +
>>             default:
>>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
>> @@ -258,6 +261,9 @@ elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
>>          be added to ABFD.  */
>>        return aprop == NULL;
>>
>> +    case GNU_PROPERTY_MEMORY_SEAL:
>> +      return aprop == NULL;
>> +
>>      default:
>>        updated = false;
>>        if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
>> @@ -607,6 +613,33 @@ elf_write_gnu_properties (struct bfd_link_info *info,
>>      }
>>  }
>>
>> +static asection *
>> +_bfd_elf_link_create_gnu_property_sec (struct bfd_link_info *info, bfd *elf_bfd,
>> +                                      unsigned int elfclass)
>> +{
>> +  asection *sec;
>> +
>> +  sec = bfd_make_section_with_flags (elf_bfd,
>> +                                    NOTE_GNU_PROPERTY_SECTION_NAME,
>> +                                    (SEC_ALLOC
>> +                                     | SEC_LOAD
>> +                                     | SEC_IN_MEMORY
>> +                                     | SEC_READONLY
>> +                                     | SEC_HAS_CONTENTS
>> +                                     | SEC_DATA));
>> +  if (sec == NULL)
>> +    info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
>> +
>> +  if (!bfd_set_section_alignment (sec,
>> +                                 elfclass == ELFCLASS64 ? 3 : 2))
>> +    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
>> +                           sec);
>> +
>> +  elf_section_type (sec) = SHT_NOTE;
>> +  return sec;
>> +}
>> +
>> +
>>  /* Set up GNU properties.  Return the first relocatable ELF input with
>>     GNU properties if found.  Otherwise, return NULL.  */
>>
>> @@ -656,23 +689,7 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
>>        /* Support -z indirect-extern-access.  */
>>        if (first_pbfd == NULL)
>>         {
>> -         sec = bfd_make_section_with_flags (elf_bfd,
>> -                                            NOTE_GNU_PROPERTY_SECTION_NAME,
>> -                                            (SEC_ALLOC
>> -                                             | SEC_LOAD
>> -                                             | SEC_IN_MEMORY
>> -                                             | SEC_READONLY
>> -                                             | SEC_HAS_CONTENTS
>> -                                             | SEC_DATA));
>> -         if (sec == NULL)
>> -           info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
>> -
>> -         if (!bfd_set_section_alignment (sec,
>> -                                         elfclass == ELFCLASS64 ? 3 : 2))
>> -           info->callbacks->einfo (_("%F%pA: failed to align section\n"),
>> -                                   sec);
>> -
>> -         elf_section_type (sec) = SHT_NOTE;
>> +         sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
>>           first_pbfd = elf_bfd;
>>           has_properties = true;
>>         }
>> @@ -690,6 +707,27 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
>>           |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
>>      }
>>
>> +  if (info->memory_seal && elf_bfd != NULL)
>> +    {
>> +      /* Support -z no-memory-seal.  */
>> +      if (first_pbfd == NULL)
>> +       {
>> +         sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
>> +         first_pbfd = elf_bfd;
>> +         has_properties = true;
>> +       }
>> +
>> +      p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_MEMORY_SEAL, 0);
>> +      if (p->pr_kind == property_unknown)
>> +       {
>> +         /* Create GNU_PROPERTY_NO_MEMORY_SEAL.  */
>> +         p->u.number = GNU_PROPERTY_MEMORY_SEAL;
>> +         p->pr_kind = property_number;
>> +       }
>> +      else
>> +       p->u.number |= GNU_PROPERTY_MEMORY_SEAL;
>> +    }
>> +
>>    /* Do nothing if there is no .note.gnu.property section.  */
>>    if (!has_properties)
>>      return NULL;
>> diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
>> index dd951b91f50..8a4405c8a79 100644
>> --- a/bfd/elfxx-x86.c
>> +++ b/bfd/elfxx-x86.c
>> @@ -4815,7 +4815,8 @@ _bfd_x86_elf_link_fixup_gnu_properties
>>    for (p = *listp; p; p = p->next)
>>      {
>>        unsigned int type = p->property.pr_type;
>> -      if (type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
>> +      if (type == GNU_PROPERTY_MEMORY_SEAL
>> +         || type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
>>           || type == GNU_PROPERTY_X86_COMPAT_ISA_1_NEEDED
>>           || (type >= GNU_PROPERTY_X86_UINT32_AND_LO
>>               && type <= GNU_PROPERTY_X86_UINT32_AND_HI)
>> diff --git a/binutils/readelf.c b/binutils/readelf.c
>> index 0f8dc1b9716..bf25425bb8d 100644
>> --- a/binutils/readelf.c
>> +++ b/binutils/readelf.c
>> @@ -21464,6 +21464,12 @@ print_gnu_property_note (Filedata * filedata, Elf_Internal_Note * pnote)
>>                 printf (_("<corrupt length: %#x> "), datasz);
>>               goto next;
>>
>> +           case GNU_PROPERTY_MEMORY_SEAL:
>> +             printf ("memory seal ");
>> +             if (datasz)
>> +               printf (_("<corrupt length: %#x> "), datasz);
>> +             goto next;
>> +
>>             default:
>>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
>> diff --git a/include/bfdlink.h b/include/bfdlink.h
>> index f802ec627ef..8b9e391e6ff 100644
>> --- a/include/bfdlink.h
>> +++ b/include/bfdlink.h
>> @@ -429,6 +429,9 @@ struct bfd_link_info
>>    /* TRUE if only one read-only, non-code segment should be created.  */
>>    unsigned int one_rosegment: 1;
>>
>> +  /* TRUE if GNU_PROPERTY_MEMORY_SEAL should be generated.  */
>> +  unsigned int memory_seal: 1;
>> +
>>    /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
>>       should be created.  1 for DWARF2 tables, 2 for compact tables.  */
>>    unsigned int eh_frame_hdr_type: 2;
>> diff --git a/include/elf/common.h b/include/elf/common.h
>> index c9920e7731a..8938e2f4754 100644
>> --- a/include/elf/common.h
>> +++ b/include/elf/common.h
>> @@ -890,6 +890,7 @@
>>  /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0).  */
>>  #define GNU_PROPERTY_STACK_SIZE                        1
>>  #define GNU_PROPERTY_NO_COPY_ON_PROTECTED      2
>> +#define GNU_PROPERTY_MEMORY_SEAL               3
>>
>>  /* A 4-byte unsigned integer property: A bit is set if it is set in all
>>     relocatable inputs.  */
>> diff --git a/ld/NEWS b/ld/NEWS
>> index 1f14dd6bc77..4a28592fa32 100644
>> --- a/ld/NEWS
>> +++ b/ld/NEWS
>> @@ -23,6 +23,9 @@ Changes in 2.43:
>>
>>  * Add -plugin-save-temps to store plugin intermediate files permanently.
>>
>> +* Add -z memory-seal/-z nomemory-seal options to ELF linker to mark the
>> +  object to memory sealed.
>> +
>>  Changes in 2.42:
>>
>>  * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT
>> diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
>> index 2e865728587..ccd43531237 100644
>> --- a/ld/emultempl/elf.em
>> +++ b/ld/emultempl/elf.em
>> @@ -1075,6 +1075,10 @@ fragment <<EOF
>>         link_info.combreloc = false;
>>        else if (strcmp (optarg, "nocopyreloc") == 0)
>>         link_info.nocopyreloc = true;
>> +      else if (strcmp (optarg, "memory-seal") == 0)
>> +       link_info.memory_seal = true;
>> +      else if (strcmp (optarg, "nomemory-seal") == 0)
>> +       link_info.memory_seal = false;
>>  EOF
>>  if test -n "$COMMONPAGESIZE"; then
>>  fragment <<EOF
>> diff --git a/ld/ld.texi b/ld/ld.texi
>> index 90182c436ec..b8957d3027e 100644
>> --- a/ld/ld.texi
>> +++ b/ld/ld.texi
>> @@ -1591,6 +1591,14 @@ Disable relocation overflow check.  This can be used to disable
>>  relocation overflow check if there will be no dynamic relocation
>>  overflow at run-time.  Supported for x86_64.
>>
>> +@item memory-seal
>> +@item nomemory-seal
>> +Instruct the executable or shared library that the all PT_LOAD segments
>> +should be sealed to avoid further manipulation (such as changing the
>> +protection flags, the segment size, or remove the mapping).
>> +This is a security hardening that requires system support.  This
>> +generates GNU_PROPERTY_MEMORY_SEAL in .note.gnu.property section
>> +
>>  @item now
>>  When generating an executable or shared library, mark it to tell the
>>  dynamic linker to resolve all symbols when the program is started, or
>> diff --git a/ld/lexsup.c b/ld/lexsup.c
>> index 8982073bc91..d82d3631a09 100644
>> --- a/ld/lexsup.c
>> +++ b/ld/lexsup.c
>> @@ -2271,6 +2271,10 @@ elf_shlib_list_options (FILE *file)
>>        fprintf (file, _("\
>>    -z textoff                  Don't treat DT_TEXTREL in output as error\n"));
>>      }
>> +  fprintf (file, _("\
>> +  -z memory-seal              Mark object be memory sealed\n"));
>> +  fprintf (file, _("\
>> +  -z nomemory-seal            Don't mark oject to be memory sealed (default)\n"));
>>  }
>>
>>  static void
>> diff --git a/ld/testsuite/ld-elf/property-seal-1.d b/ld/testsuite/ld-elf/property-seal-1.d
>> new file mode 100644
>> index 00000000000..0b92a8eb6eb
>> --- /dev/null
>> +++ b/ld/testsuite/ld-elf/property-seal-1.d
>> @@ -0,0 +1,15 @@
>> +#source: empty.s
>> +#ld: -shared -z memory-seal
>> +#readelf: -n
>> +#xfail: ![check_shared_lib_support]
>> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
>> +# Assembly source file for the HPPA assembler is renamed and modifed by
>> +# sed.  mn10300 has relocations in .note.gnu.property section which
>> +# elf_parse_notes doesn't support.
>> +
>> +#...
>> +Displaying notes found in: .note.gnu.property
>> +[      ]+Owner[        ]+Data size[    ]+Description
>> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
>> +      Properties: memory seal
>> +#pass
>> diff --git a/ld/testsuite/ld-elf/property-seal-2.d b/ld/testsuite/ld-elf/property-seal-2.d
>> new file mode 100644
>> index 00000000000..12339e83ebd
>> --- /dev/null
>> +++ b/ld/testsuite/ld-elf/property-seal-2.d
>> @@ -0,0 +1,14 @@
>> +#source: empty.s
>> +#ld: -z memory-seal
>> +#readelf: -n
>> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
>> +# Assembly source file for the HPPA assembler is renamed and modifed by
>> +# sed.  mn10300 has relocations in .note.gnu.property section which
>> +# elf_parse_notes doesn't support.
>> +
>> +#...
>> +Displaying notes found in: .note.gnu.property
>> +[      ]+Owner[        ]+Data size[    ]+Description
>> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
>> +      Properties: memory seal
>> +#pass
>> --
>> 2.34.1
>>
> 
> Is GNU_PROPERTY_MEMORY_SEAL only generated by linker?
> What happens when an input relocatable object file contains
> GNU_PROPERTY_MEMORY_SEAL?
> 

Yes, I didn't see a strong reason to add a compiler flag to enable it.  Similar
to RELR, it makes mode sense to be a linker decision instead of a per object
one. And the flag is just ignored if present in a ET_REL.

I am not sure if this is be best approach, I am thinking in revising to use a
DT_FLAGS_1 instead.
Adhemerval Zanella Netto Oct. 3, 2024, 12:43 p.m. UTC | #5
On 01/10/24 03:45, Jan Beulich wrote:
> On 30.09.2024 22:08, Adhemerval Zanella wrote:
>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>>
>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
>> be memory-sealed if the loader supports it. Memory sealing is useful as
>> a hardening mechanism to avoid either remapping the memory segments or
>> changing the memory protection segments layout by the dynamic loader
>> (for instance, the RELRO hardening). The Linux 6.10
>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
>> accomplishes it.
>>
>> A GNU property is used instead of a new dynamic section tag (like the
>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
>> security features like x86 CET or AArch64 BTI.
> 
> What's wrong with DT_* in ET_EXEC type files? The first random executables
> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see
> why a note would need using instead. As per H.J.'s question, a note would
> also pose a possible issue (as to how to deal with it) when found in an
> object file.

I had the impression that this was not really required for ET_EXEC, but it
seems I am wrong.  I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE.



> 
> Do we even need a new flag?  Could we reuse DF_1_NODELETE?

I would prefer to use a new flag, I don't want to mix different runtime support
with the same flag (it might be a source of confusion).
Adhemerval Zanella Netto Oct. 3, 2024, 2 p.m. UTC | #6
On 03/10/24 09:43, Adhemerval Zanella Netto wrote:
> 
> 
> On 01/10/24 03:45, Jan Beulich wrote:
>> On 30.09.2024 22:08, Adhemerval Zanella wrote:
>>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>>>
>>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
>>> be memory-sealed if the loader supports it. Memory sealing is useful as
>>> a hardening mechanism to avoid either remapping the memory segments or
>>> changing the memory protection segments layout by the dynamic loader
>>> (for instance, the RELRO hardening). The Linux 6.10
>>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
>>> accomplishes it.
>>>
>>> A GNU property is used instead of a new dynamic section tag (like the
>>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
>>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
>>> security features like x86 CET or AArch64 BTI.
>>
>> What's wrong with DT_* in ET_EXEC type files? The first random executables
>> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see
>> why a note would need using instead. As per H.J.'s question, a note would
>> also pose a possible issue (as to how to deal with it) when found in an
>> object file.
> 
> I had the impression that this was not really required for ET_EXEC, but it
> seems I am wrong.  I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE.
> 
> 

In fact the main problem with DT_FLAGS_1 is it requires a dynamic section and
it might be tricky to get this with for non-pie static binaries (since a lot
of fields are mandatory).  We can just make it supported for PT_DYNAMIC, although
with GNU attribute it is easier to avoid this constraint.
Adhemerval Zanella Netto Oct. 9, 2024, 1:47 p.m. UTC | #7
On 03/10/24 11:00, Adhemerval Zanella Netto wrote:
> 
> 
> On 03/10/24 09:43, Adhemerval Zanella Netto wrote:
>>
>>
>> On 01/10/24 03:45, Jan Beulich wrote:
>>> On 30.09.2024 22:08, Adhemerval Zanella wrote:
>>>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>>>>
>>>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
>>>> be memory-sealed if the loader supports it. Memory sealing is useful as
>>>> a hardening mechanism to avoid either remapping the memory segments or
>>>> changing the memory protection segments layout by the dynamic loader
>>>> (for instance, the RELRO hardening). The Linux 6.10
>>>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
>>>> accomplishes it.
>>>>
>>>> A GNU property is used instead of a new dynamic section tag (like the
>>>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
>>>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
>>>> security features like x86 CET or AArch64 BTI.
>>>
>>> What's wrong with DT_* in ET_EXEC type files? The first random executables
>>> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see
>>> why a note would need using instead. As per H.J.'s question, a note would
>>> also pose a possible issue (as to how to deal with it) when found in an
>>> object file.
>>
>> I had the impression that this was not really required for ET_EXEC, but it
>> seems I am wrong.  I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE.
>>
>>
> 
> In fact the main problem with DT_FLAGS_1 is it requires a dynamic section and
> it might be tricky to get this with for non-pie static binaries (since a lot
> of fields are mandatory).  We can just make it supported for PT_DYNAMIC, although
> with GNU attribute it is easier to avoid this constraint.


So I am more inclined to keep using the current strategy of a gnu property
to avoid the PT_DYNAMIC constraint (specially because some glibc ports 
still do not have static-pie support).  Do you see any problem with this
approach?
H.J. Lu Oct. 9, 2024, 9:24 p.m. UTC | #8
On Wed, Oct 9, 2024 at 9:48 PM Adhemerval Zanella Netto
<adhemerval.zanella@linaro.org> wrote:
>
>
>
> On 03/10/24 11:00, Adhemerval Zanella Netto wrote:
> >
> >
> > On 03/10/24 09:43, Adhemerval Zanella Netto wrote:
> >>
> >>
> >> On 01/10/24 03:45, Jan Beulich wrote:
> >>> On 30.09.2024 22:08, Adhemerval Zanella wrote:
> >>>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
> >>>>
> >>>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
> >>>> be memory-sealed if the loader supports it. Memory sealing is useful as
> >>>> a hardening mechanism to avoid either remapping the memory segments or
> >>>> changing the memory protection segments layout by the dynamic loader
> >>>> (for instance, the RELRO hardening). The Linux 6.10
> >>>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
> >>>> accomplishes it.
> >>>>
> >>>> A GNU property is used instead of a new dynamic section tag (like the
> >>>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
> >>>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
> >>>> security features like x86 CET or AArch64 BTI.
> >>>
> >>> What's wrong with DT_* in ET_EXEC type files? The first random executables
> >>> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see
> >>> why a note would need using instead. As per H.J.'s question, a note would
> >>> also pose a possible issue (as to how to deal with it) when found in an
> >>> object file.
> >>
> >> I had the impression that this was not really required for ET_EXEC, but it
> >> seems I am wrong.  I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE.
> >>
> >>
> >
> > In fact the main problem with DT_FLAGS_1 is it requires a dynamic section and
> > it might be tricky to get this with for non-pie static binaries (since a lot
> > of fields are mandatory).  We can just make it supported for PT_DYNAMIC, although
> > with GNU attribute it is easier to avoid this constraint.
>
>
> So I am more inclined to keep using the current strategy of a gnu property
> to avoid the PT_DYNAMIC constraint (specially because some glibc ports
> still do not have static-pie support).  Do you see any problem with this
> approach?

Please add linker tests to verify that GNU_PROPERTY_MEMORY_SEAL
in input objects, .o and .so, are ignored.   Tests should cover all input
objects with GNU_PROPERTY_MEMORY_SEAL and some have
GNU_PROPERTY_MEMORY_SEAL.  You should check the behaviors
of old and non-bfd linkers.
diff mbox series

Patch

diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
index ee8bd37f2bd..17c89d9e876 100644
--- a/bfd/elf-properties.c
+++ b/bfd/elf-properties.c
@@ -177,6 +177,9 @@  _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
 	      prop->pr_kind = property_number;
 	      goto next;
 
+	    case GNU_PROPERTY_MEMORY_SEAL:
+	      goto next;
+
 	    default:
 	      if ((type >= GNU_PROPERTY_UINT32_AND_LO
 		   && type <= GNU_PROPERTY_UINT32_AND_HI)
@@ -258,6 +261,9 @@  elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
 	 be added to ABFD.  */
       return aprop == NULL;
 
+    case GNU_PROPERTY_MEMORY_SEAL:
+      return aprop == NULL;
+
     default:
       updated = false;
       if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
@@ -607,6 +613,33 @@  elf_write_gnu_properties (struct bfd_link_info *info,
     }
 }
 
+static asection *
+_bfd_elf_link_create_gnu_property_sec (struct bfd_link_info *info, bfd *elf_bfd,
+				       unsigned int elfclass)
+{
+  asection *sec;
+
+  sec = bfd_make_section_with_flags (elf_bfd,
+				     NOTE_GNU_PROPERTY_SECTION_NAME,
+				     (SEC_ALLOC
+				      | SEC_LOAD
+				      | SEC_IN_MEMORY
+				      | SEC_READONLY
+				      | SEC_HAS_CONTENTS
+				      | SEC_DATA));
+  if (sec == NULL)
+    info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
+
+  if (!bfd_set_section_alignment (sec,
+				  elfclass == ELFCLASS64 ? 3 : 2))
+    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
+			    sec);
+
+  elf_section_type (sec) = SHT_NOTE;
+  return sec;
+}
+
+
 /* Set up GNU properties.  Return the first relocatable ELF input with
    GNU properties if found.  Otherwise, return NULL.  */
 
@@ -656,23 +689,7 @@  _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
       /* Support -z indirect-extern-access.  */
       if (first_pbfd == NULL)
 	{
-	  sec = bfd_make_section_with_flags (elf_bfd,
-					     NOTE_GNU_PROPERTY_SECTION_NAME,
-					     (SEC_ALLOC
-					      | SEC_LOAD
-					      | SEC_IN_MEMORY
-					      | SEC_READONLY
-					      | SEC_HAS_CONTENTS
-					      | SEC_DATA));
-	  if (sec == NULL)
-	    info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
-
-	  if (!bfd_set_section_alignment (sec,
-					  elfclass == ELFCLASS64 ? 3 : 2))
-	    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
-				    sec);
-
-	  elf_section_type (sec) = SHT_NOTE;
+	  sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
 	  first_pbfd = elf_bfd;
 	  has_properties = true;
 	}
@@ -690,6 +707,27 @@  _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
 	  |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
     }
 
+  if (info->memory_seal && elf_bfd != NULL)
+    {
+      /* Support -z no-memory-seal.  */
+      if (first_pbfd == NULL)
+	{
+	  sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
+	  first_pbfd = elf_bfd;
+	  has_properties = true;
+	}
+
+      p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_MEMORY_SEAL, 0);
+      if (p->pr_kind == property_unknown)
+	{
+	  /* Create GNU_PROPERTY_NO_MEMORY_SEAL.  */
+	  p->u.number = GNU_PROPERTY_MEMORY_SEAL;
+	  p->pr_kind = property_number;
+	}
+      else
+	p->u.number |= GNU_PROPERTY_MEMORY_SEAL;
+    }
+
   /* Do nothing if there is no .note.gnu.property section.  */
   if (!has_properties)
     return NULL;
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index dd951b91f50..8a4405c8a79 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -4815,7 +4815,8 @@  _bfd_x86_elf_link_fixup_gnu_properties
   for (p = *listp; p; p = p->next)
     {
       unsigned int type = p->property.pr_type;
-      if (type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
+      if (type == GNU_PROPERTY_MEMORY_SEAL
+	  || type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
 	  || type == GNU_PROPERTY_X86_COMPAT_ISA_1_NEEDED
 	  || (type >= GNU_PROPERTY_X86_UINT32_AND_LO
 	      && type <= GNU_PROPERTY_X86_UINT32_AND_HI)
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 0f8dc1b9716..bf25425bb8d 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -21464,6 +21464,12 @@  print_gnu_property_note (Filedata * filedata, Elf_Internal_Note * pnote)
 		printf (_("<corrupt length: %#x> "), datasz);
 	      goto next;
 
+	    case GNU_PROPERTY_MEMORY_SEAL:
+	      printf ("memory seal ");
+	      if (datasz)
+		printf (_("<corrupt length: %#x> "), datasz);
+	      goto next;
+
 	    default:
 	      if ((type >= GNU_PROPERTY_UINT32_AND_LO
 		   && type <= GNU_PROPERTY_UINT32_AND_HI)
diff --git a/include/bfdlink.h b/include/bfdlink.h
index f802ec627ef..8b9e391e6ff 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -429,6 +429,9 @@  struct bfd_link_info
   /* TRUE if only one read-only, non-code segment should be created.  */
   unsigned int one_rosegment: 1;
 
+  /* TRUE if GNU_PROPERTY_MEMORY_SEAL should be generated.  */
+  unsigned int memory_seal: 1;
+
   /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
      should be created.  1 for DWARF2 tables, 2 for compact tables.  */
   unsigned int eh_frame_hdr_type: 2;
diff --git a/include/elf/common.h b/include/elf/common.h
index c9920e7731a..8938e2f4754 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -890,6 +890,7 @@ 
 /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0).  */
 #define GNU_PROPERTY_STACK_SIZE			1
 #define GNU_PROPERTY_NO_COPY_ON_PROTECTED	2
+#define GNU_PROPERTY_MEMORY_SEAL		3
 
 /* A 4-byte unsigned integer property: A bit is set if it is set in all
    relocatable inputs.  */
diff --git a/ld/NEWS b/ld/NEWS
index 1f14dd6bc77..4a28592fa32 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -23,6 +23,9 @@  Changes in 2.43:
 
 * Add -plugin-save-temps to store plugin intermediate files permanently.
 
+* Add -z memory-seal/-z nomemory-seal options to ELF linker to mark the
+  object to memory sealed.
+
 Changes in 2.42:
 
 * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT
diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
index 2e865728587..ccd43531237 100644
--- a/ld/emultempl/elf.em
+++ b/ld/emultempl/elf.em
@@ -1075,6 +1075,10 @@  fragment <<EOF
 	link_info.combreloc = false;
       else if (strcmp (optarg, "nocopyreloc") == 0)
 	link_info.nocopyreloc = true;
+      else if (strcmp (optarg, "memory-seal") == 0)
+       link_info.memory_seal = true;
+      else if (strcmp (optarg, "nomemory-seal") == 0)
+       link_info.memory_seal = false;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/ld.texi b/ld/ld.texi
index 90182c436ec..b8957d3027e 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1591,6 +1591,14 @@  Disable relocation overflow check.  This can be used to disable
 relocation overflow check if there will be no dynamic relocation
 overflow at run-time.  Supported for x86_64.
 
+@item memory-seal
+@item nomemory-seal
+Instruct the executable or shared library that the all PT_LOAD segments
+should be sealed to avoid further manipulation (such as changing the
+protection flags, the segment size, or remove the mapping).
+This is a security hardening that requires system support.  This
+generates GNU_PROPERTY_MEMORY_SEAL in .note.gnu.property section
+
 @item now
 When generating an executable or shared library, mark it to tell the
 dynamic linker to resolve all symbols when the program is started, or
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 8982073bc91..d82d3631a09 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -2271,6 +2271,10 @@  elf_shlib_list_options (FILE *file)
       fprintf (file, _("\
   -z textoff                  Don't treat DT_TEXTREL in output as error\n"));
     }
+  fprintf (file, _("\
+  -z memory-seal              Mark object be memory sealed\n"));
+  fprintf (file, _("\
+  -z nomemory-seal            Don't mark oject to be memory sealed (default)\n"));
 }
 
 static void
diff --git a/ld/testsuite/ld-elf/property-seal-1.d b/ld/testsuite/ld-elf/property-seal-1.d
new file mode 100644
index 00000000000..0b92a8eb6eb
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-1.d
@@ -0,0 +1,15 @@ 
+#source: empty.s
+#ld: -shared -z memory-seal
+#readelf: -n
+#xfail: ![check_shared_lib_support]
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#...
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x[0-9a-f]+	NT_GNU_PROPERTY_TYPE_0
+      Properties: memory seal 
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-2.d b/ld/testsuite/ld-elf/property-seal-2.d
new file mode 100644
index 00000000000..12339e83ebd
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-2.d
@@ -0,0 +1,14 @@ 
+#source: empty.s
+#ld: -z memory-seal
+#readelf: -n
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#...
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x[0-9a-f]+	NT_GNU_PROPERTY_TYPE_0
+      Properties: memory seal 
+#pass