ld: Handle special ELF readonly output sections

Message ID 20260331181527.884535-1-hjl.tools@gmail.com
State New
Headers
Series ld: Handle special ELF readonly output sections |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

H.J. Lu March 31, 2026, 6:15 p.m. UTC
  There are many special ELF sections which should be readonly.  But output
sections created in linker script won't set the SEC_READONLY if they
aren't marked with READONLY.  Add bfd_is_special_readonly_section to
return true if a section name is a special ELF readonly section and use
it when creating output sections.

bfd/

	PR ld/34024
	* bfd-in2.h: Regenerated.
	* bfd.c (bfd_is_special_readonly_section): New function.
	* elf-bfd.h (elf_backend_data): Add is_special_readonly_section.
	(_bfd_elf_is_special_readonly_section): New prototype.
	* elf.c (elf_get_special_section): New function.
	(_bfd_elf_get_sec_type_attr): Call elf_get_special_section.
	(_bfd_elf_is_special_readonly_section): New function.
	* elfxx-target.h (elf_backend_is_special_readonly_section): New.
	(elfNN_bed): Add elf_backend_is_special_readonly_section.

ld/

	PR ld/34024
	* ldlang.c (get_os_init_flag): Return SEC_READONLY if the output
	section is a special readonly section.
	* testsuite/ld-elf/flags2.d: New file.
	* testsuite/ld-elf/flags2.ld: Likewise.
	* testsuite/ld-elf/flags2.s: Likewise.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 bfd/bfd-in2.h                 |  2 ++
 bfd/bfd.c                     | 24 ++++++++++++++++++++++++
 bfd/elf-bfd.h                 |  7 +++++++
 bfd/elf.c                     | 35 ++++++++++++++++++++++++++---------
 bfd/elfxx-target.h            |  5 +++++
 ld/ldlang.c                   |  5 ++++-
 ld/testsuite/ld-elf/flags2.d  |  9 +++++++++
 ld/testsuite/ld-elf/flags2.ld |  1 +
 ld/testsuite/ld-elf/flags2.s  |  6 ++++++
 9 files changed, 84 insertions(+), 10 deletions(-)
 create mode 100644 ld/testsuite/ld-elf/flags2.d
 create mode 100644 ld/testsuite/ld-elf/flags2.ld
 create mode 100644 ld/testsuite/ld-elf/flags2.s
  

Patch

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 0b934005f9d..70659dfe824 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2818,6 +2818,8 @@  char *bfd_demangle (bfd *, const char *, int);
 
 asymbol *bfd_group_signature (asection *group, asymbol **isympp);
 
+bool bfd_is_special_readonly_section (bfd *abfd, const char *name);
+
 /* Extracted from bfdio.c.  */
 bfd_size_type bfd_read (void *, bfd_size_type, bfd *)
 ATTRIBUTE_WARN_UNUSED_RESULT;
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 31c70890ad9..a198eb8002f 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -3133,3 +3133,27 @@  bfd_group_signature (asection *group, asymbol **isympp)
     }
   return NULL;
 }
+
+/*
+FUNCTION
+	bfd_is_special_readonly_section
+
+SYNOPSIS
+	bool bfd_is_special_readonly_section (bfd *abfd, const char *name);
+
+DESCRIPTION
+	Return true if the BFD section of NAME is a special readonly
+	section.
+*/
+
+bool
+bfd_is_special_readonly_section (bfd *abfd, const char *name)
+{
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+    {
+      elf_backend_data *bed = get_elf_backend_data (abfd);
+      return bed->is_special_readonly_section (abfd, name);
+    }
+
+  return false;
+}
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 3d2fad49aa4..cb4745166de 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1127,6 +1127,11 @@  struct elf_backend_data
   const struct bfd_elf_special_section * (*get_sec_type_attr)
     (bfd *, asection *);
 
+  /* A function that returns true if the given BFD section of name
+     is a special readonly section.  */
+  bool (*is_special_readonly_section)
+    (bfd *, const char*);
+
   /* A function to handle unusual program segment types when creating BFD
      sections from ELF program segments.  */
   bool (*elf_backend_section_from_phdr)
@@ -2542,6 +2547,8 @@  extern const struct bfd_elf_special_section *_bfd_elf_get_special_section
   ATTRIBUTE_HIDDEN;
 extern const struct bfd_elf_special_section *_bfd_elf_get_sec_type_attr
   (bfd *, asection *) ATTRIBUTE_HIDDEN;
+extern bool _bfd_elf_is_special_readonly_section
+  (bfd *, const char *) ATTRIBUTE_HIDDEN;
 
 extern bool _bfd_elf_link_hide_sym_by_version
   (struct bfd_link_info *, struct elf_link_hash_entry *) ATTRIBUTE_HIDDEN;
diff --git a/bfd/elf.c b/bfd/elf.c
index 5f02188b4b5..db7c5620337 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -3275,32 +3275,31 @@  _bfd_elf_get_special_section (const char *name,
   return NULL;
 }
 
-const struct bfd_elf_special_section *
-_bfd_elf_get_sec_type_attr (bfd *abfd, asection *sec)
+static const struct bfd_elf_special_section *
+elf_get_special_section (bfd *abfd, const char *name,
+			 unsigned int use_rela_p)
 {
   int i;
   const struct bfd_elf_special_section *spec;
   elf_backend_data *bed;
 
   /* See if this is one of the special sections.  */
-  if (sec->name == NULL)
+  if (name == NULL)
     return NULL;
 
   bed = get_elf_backend_data (abfd);
   spec = bed->special_sections;
   if (spec)
     {
-      spec = _bfd_elf_get_special_section (sec->name,
-					   bed->special_sections,
-					   sec->use_rela_p);
+      spec = _bfd_elf_get_special_section (name, spec, use_rela_p);
       if (spec != NULL)
 	return spec;
     }
 
-  if (sec->name[0] != '.')
+  if (name[0] != '.')
     return NULL;
 
-  i = sec->name[1] - 'b';
+  i = name[1] - 'b';
   if (i < 0 || i > 'z' - 'b')
     return NULL;
 
@@ -3309,7 +3308,25 @@  _bfd_elf_get_sec_type_attr (bfd *abfd, asection *sec)
   if (spec == NULL)
     return NULL;
 
-  return _bfd_elf_get_special_section (sec->name, spec, sec->use_rela_p);
+  return _bfd_elf_get_special_section (name, spec, use_rela_p);
+}
+
+const struct bfd_elf_special_section *
+_bfd_elf_get_sec_type_attr (bfd *abfd, asection *sec)
+{
+  return elf_get_special_section (abfd, sec->name, sec->use_rela_p);
+}
+
+bool
+_bfd_elf_is_special_readonly_section (bfd *abfd, const char *name)
+{
+  elf_backend_data *bed;
+  const struct bfd_elf_special_section *spec;
+  bed = get_elf_backend_data (abfd);
+  spec = elf_get_special_section (abfd, name, bed->default_use_rela_p);
+  if (spec == NULL)
+    return false;
+  return (spec->attr & SHF_WRITE) == 0;
 }
 
 bool
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 124be5ed0c4..580276a543b 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -443,6 +443,10 @@ 
 #ifndef elf_backend_get_sec_type_attr
 #define elf_backend_get_sec_type_attr	_bfd_elf_get_sec_type_attr
 #endif
+#ifndef elf_backend_is_special_readonly_section
+#define elf_backend_is_special_readonly_section	\
+  _bfd_elf_is_special_readonly_section
+#endif
 #ifndef elf_backend_section_from_phdr
 #define elf_backend_section_from_phdr	_bfd_elf_make_section_from_phdr
 #endif
@@ -872,6 +876,7 @@  static const struct elf_backend_data elfNN_bed =
   elf_backend_section_from_shdr,
   elf_backend_section_flags,
   elf_backend_get_sec_type_attr,
+  elf_backend_is_special_readonly_section,
   elf_backend_section_from_phdr,
   elf_backend_fake_sections,
   elf_backend_section_from_bfd_section,
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 656edeb4981..b6d19e95401 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -2604,7 +2604,10 @@  get_os_init_flag (lang_output_section_statement_type * os)
       {
       case readonly_section: return SEC_READONLY;
       case noload_section:   return SEC_NEVER_LOAD;
-      default: break;
+      default:
+	if (bfd_is_special_readonly_section (link_info.output_bfd,
+					     os->name))
+	  return SEC_READONLY;
       }
 
   return 0;
diff --git a/ld/testsuite/ld-elf/flags2.d b/ld/testsuite/ld-elf/flags2.d
new file mode 100644
index 00000000000..5fc8571d9dc
--- /dev/null
+++ b/ld/testsuite/ld-elf/flags2.d
@@ -0,0 +1,9 @@ 
+#name: .rodata section attributes
+#ld: -Tflags2.ld
+#readelf: -S --wide
+
+#...
+Section Headers:
+#...
+  \[[ 0-9]+\] \.rodata.*[ \t]+PROGBITS[ \t0-9a-f]+ A .*
+#pass
diff --git a/ld/testsuite/ld-elf/flags2.ld b/ld/testsuite/ld-elf/flags2.ld
new file mode 100644
index 00000000000..90eeca713e7
--- /dev/null
+++ b/ld/testsuite/ld-elf/flags2.ld
@@ -0,0 +1 @@ 
+SECTIONS { .rodata : ALIGN(4) { __crc_hello = .; LONG(0x05d25769); } }
diff --git a/ld/testsuite/ld-elf/flags2.s b/ld/testsuite/ld-elf/flags2.s
new file mode 100644
index 00000000000..c5dbfbf0a2f
--- /dev/null
+++ b/ld/testsuite/ld-elf/flags2.s
@@ -0,0 +1,6 @@ 
+	.text
+	.globl	start
+	.type	start, %function
+start:
+	.byte 0
+	.section .note.GNU-stack, "", %progbits