[v2] x86: Check invalid GOT/PLT/TLS relocations

Message ID CAMe9rOpKY3=DZOqAT8G9hn_mio3Gpf0GGmQ_5gzrN3MzvbnPnQ@mail.gmail.com
State New
Headers
Series [v2] x86: Check invalid GOT/PLT/TLS relocations |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply

Commit Message

H.J. Lu July 30, 2026, 9:54 p.m. UTC
  1. Since non-alloc sections aren't checked for TLS, GOT and PLT usages,
relocate_section should issue error for TLS, GOT and PLT relocations in
non-alloc and non-debugging sections.
2. Since TLS relocations must be against thread local symbols, scan_relocs
should issue an error for TLS relocation against non-thread local symbol.

PR ld/34444
PR ld/34448
* elf32-i386.c (elf_i386_tls_transition): Replace
_bfd_x86_elf_link_report_tls_invalid_section_error with
_bfd_x86_elf_link_report_error.
(elf_i386_scan_relocs): Issue an error for TLS relocation against
non-thread local symbol.
(elf_i386_relocate_section): Issue error for TLS, GOT and PLT
relocations in non-alloc and non-debugging sections.
* elf64-x86-64.c (elf_x86_64_tls_transition): Replace
_bfd_x86_elf_link_report_tls_invalid_section_error with
_bfd_x86_elf_link_report_error.
(elf_x86_64_scan_relocs): Issue an error for TLS relocation
against non-thread local symbol.
* elfxx-x86.c (_bfd_x86_elf_link_report_tls_invalid_section_error):
Renamed to ...
(_bfd_x86_elf_link_report_error): This.  Add an argument for
link error type and handle it.
* elfxx-x86.h (elf_x86_error_type): New enum.
(_bfd_x86_elf_link_report_tls_invalid_section_error): Renamed
to ...
(_bfd_x86_elf_link_report_error): This.  Add an argument of
enum elf_x86_error_type.


--
H.J.
  

Patch

From 3385bc7a3511d1c920a7286010cb66696bce2c35 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Thu, 30 Jul 2026 13:43:56 +0800
Subject: [PATCH v2] x86: Check invalid GOT/PLT/TLS relocations

1. Since non-alloc sections aren't checked for TLS, GOT and PLT usages,
relocate_section should issue error for TLS, GOT and PLT relocations in
non-alloc and non-debugging sections.
2. Since TLS relocations must be against thread local symbols, scan_relocs
should issue an error for TLS relocation against non-thread local symbol.

	PR ld/34444
	PR ld/34448
	* elf32-i386.c (elf_i386_tls_transition): Replace
	_bfd_x86_elf_link_report_tls_invalid_section_error with
	_bfd_x86_elf_link_report_error.
	(elf_i386_scan_relocs): Issue an error for TLS relocation against
	non-thread local symbol.
	(elf_i386_relocate_section): Issue error for TLS, GOT and PLT
	relocations in non-alloc and non-debugging sections.
	* elf64-x86-64.c (elf_x86_64_tls_transition): Replace
	_bfd_x86_elf_link_report_tls_invalid_section_error with
	_bfd_x86_elf_link_report_error.
	(elf_x86_64_scan_relocs): Issue an error for TLS relocation
	against non-thread local symbol.
	* elfxx-x86.c (_bfd_x86_elf_link_report_tls_invalid_section_error):
	Renamed to ...
	(_bfd_x86_elf_link_report_error): This.  Add an argument for
	link error type and handle it.
	* elfxx-x86.h (elf_x86_error_type): New enum.
	(_bfd_x86_elf_link_report_tls_invalid_section_error): Renamed
	to ...
	(_bfd_x86_elf_link_report_error): This.  Add an argument of
	enum elf_x86_error_type.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 bfd/elf32-i386.c   | 50 ++++++++++++++++++++++++++++++++++++++-----
 bfd/elf64-x86-64.c | 53 +++++++++++++++++++++++++++++++++++++++++-----
 bfd/elfxx-x86.c    | 53 ++++++++++++++++++++++++++++++++++++++--------
 bfd/elfxx-x86.h    | 11 ++++++++--
 4 files changed, 146 insertions(+), 21 deletions(-)

diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index ae9276cff06..8ad5cb4e354 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -1175,8 +1175,8 @@  elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
        || (sec->flags & SEC_CODE) == 0))
     {
       reloc_howto_type *howto = elf_i386_rtype_to_howto (from_type);
-      _bfd_x86_elf_link_report_tls_invalid_section_error
-	(abfd, sec, symtab_hdr, h, sym, howto);
+      _bfd_x86_elf_link_report_error (abfd, sec, symtab_hdr, h, sym,
+				      howto, elf_x86_error_tls);
       return false;
     }
 
@@ -1749,11 +1749,15 @@  elf_i386_scan_relocs (bfd *abfd,
 
 	    if (tls_type >= GOT_TLS_GD
 		&& tls_type <= GOT_TLS_GDESC
-		&& (elf_section_type (sec) != SHT_PROGBITS
+		&& ((h != NULL
+		     ? h->type != STT_TLS
+		     : ELF_ST_TYPE (isym->st_info) != STT_TLS)
+		    || elf_section_type (sec) != SHT_PROGBITS
 		    || (sec->flags & SEC_CODE) == 0))
 	      {
-		_bfd_x86_elf_link_report_tls_invalid_section_error
-		  (abfd, sec, symtab_hdr, h, isym, howto);
+		_bfd_x86_elf_link_report_error (abfd, sec, symtab_hdr,
+						h, isym, howto,
+						elf_x86_error_tls);
 		goto error_return;
 	      }
 
@@ -2585,6 +2589,18 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	{
 	case R_386_GOT32X:
 	case R_386_GOT32:
+	  /* Since we don't allow non-alloced sections to create GOT/PLT
+	     entries, issue an error if a non-alloced section references
+	     GOT.  */
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    {
+  non_alloc_error:
+	      _bfd_x86_elf_link_report_error (input_bfd, input_section,
+					      symtab_hdr, h, sym, howto,
+					      elf_x86_error_non_alloc);
+	      return false;
+	    }
+
 	  /* Relocation is to the entry for this symbol in the global
 	     offset table.  */
 	  if (htab->elf.sgot == NULL)
@@ -2717,6 +2733,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_386_GOTOFF:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Relocation is relative to the start of the global offset
 	     table.  */
 
@@ -2781,6 +2800,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_386_GOTPC:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Use global offset table as symbol value.  */
 	  relocation = htab->elf.sgotplt->output_section->vma
 		       + htab->elf.sgotplt->output_offset;
@@ -2788,6 +2810,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_386_PLT32:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Relocation is to the entry for this symbol in the
 	     procedure linkage table.  */
 
@@ -2915,6 +2940,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_386_TLS_IE:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  if (!bfd_link_executable (info))
 	    {
 	      Elf_Internal_Rela outrel;
@@ -2942,6 +2970,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	case R_386_TLS_DESC_CALL:
 	case R_386_TLS_IE_32:
 	case R_386_TLS_GOTIE:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  tls_type = GOT_UNKNOWN;
 	  if (h == NULL && local_got_offsets)
 	    tls_type = elf_x86_local_got_tls_type (input_bfd) [r_symndx];
@@ -3440,6 +3471,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_386_TLS_LDM:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  if (! elf_i386_tls_transition (info, input_bfd,
 					 input_section, contents,
 					 symtab_hdr, sym_hashes,
@@ -3512,6 +3546,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_386_TLS_LDO_32:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  if (!bfd_link_executable (info)
 	      || (input_section->flags & SEC_CODE) == 0)
 	    relocation -= _bfd_x86_elf_dtpoff_base (info);
@@ -3522,6 +3559,9 @@  elf_i386_relocate_section (struct bfd_link_info *info,
 
 	case R_386_TLS_LE_32:
 	case R_386_TLS_LE:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  if (!bfd_link_executable (info))
 	    {
 	      Elf_Internal_Rela outrel;
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 377f76d7aea..3d85886a3d5 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1633,8 +1633,8 @@  elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
     {
       reloc_howto_type *howto = elf_x86_64_rtype_to_howto (abfd,
 							   from_type);
-      _bfd_x86_elf_link_report_tls_invalid_section_error
-	(abfd, sec, symtab_hdr, h, sym, howto);
+      _bfd_x86_elf_link_report_error (abfd, sec, symtab_hdr, h,
+				      sym, howto, elf_x86_error_tls);
       return false;
     }
 
@@ -2772,11 +2772,15 @@  need_got:
 
 	    if (tls_type >= GOT_TLS_GD
 		&& tls_type <= GOT_TLS_GDESC
-		&& (elf_section_type (sec) != SHT_PROGBITS
+		&& ((h != NULL
+		     ? h->type != STT_TLS
+		     : ELF_ST_TYPE (isym->st_info) != STT_TLS)
+		    || elf_section_type (sec) != SHT_PROGBITS
 		    || (sec->flags & SEC_CODE) == 0))
 	      {
-		_bfd_x86_elf_link_report_tls_invalid_section_error
-		  (abfd, sec, symtab_hdr, h, isym, howto);
+		_bfd_x86_elf_link_report_error (abfd, sec, symtab_hdr,
+						h, isym, howto,
+						elf_x86_error_tls);
 		goto error_return;
 	      }
 
@@ -3602,6 +3606,18 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	case R_X86_64_GOTPCREL64:
 	  /* Use global offset table entry as symbol value.  */
 	case R_X86_64_GOTPLT64:
+	  /* Since we don't allow non-alloced sections to create GOT/PLT
+	     entries, issue an error if a non-alloced section references
+	     GOT.  */
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    {
+  non_alloc_error:
+	      _bfd_x86_elf_link_report_error (input_bfd, input_section,
+					      symtab_hdr, h, sym, howto,
+					      elf_x86_error_non_alloc);
+	      return false;
+	    }
+
 	  /* This is obsolete and treated the same as GOT64.  */
 	  base_got = htab->elf.sgot;
 
@@ -3740,6 +3756,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_X86_64_GOTOFF64:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Relocation is relative to the start of the global offset
 	     table.  */
 
@@ -3806,6 +3825,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 
 	case R_X86_64_GOTPC32:
 	case R_X86_64_GOTPC64:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Use global offset table as symbol value.  */
 	  relocation = htab->elf.sgotplt->output_section->vma
 		       + htab->elf.sgotplt->output_offset;
@@ -3813,6 +3835,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_X86_64_PLTOFF64:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Relocation is PLT entry relative to GOT.  For local
 	     symbols it's the symbol itself relative to GOT.  */
 	  if (h != NULL
@@ -3849,6 +3874,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_X86_64_PLT32:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  /* Relocation is to the entry for this symbol in the
 	     procedure linkage table.  */
 
@@ -4157,6 +4185,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	case R_X86_64_CODE_4_GOTTPOFF:
 	case R_X86_64_CODE_5_GOTTPOFF:
 	case R_X86_64_CODE_6_GOTTPOFF:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  tls_type = GOT_UNKNOWN;
 	  if (h == NULL && local_got_offsets)
 	    tls_type = elf_x86_local_got_tls_type (input_bfd) [r_symndx];
@@ -4883,6 +4914,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_X86_64_TLSLD:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  if (! elf_x86_64_tls_transition (info, input_bfd,
 					   input_section, contents,
 					   symtab_hdr, sym_hashes,
@@ -5012,6 +5046,9 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 	  break;
 
 	case R_X86_64_DTPOFF32:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  if (!bfd_link_executable (info)
 	      || (input_section->flags & SEC_CODE) == 0)
 	    relocation -= _bfd_x86_elf_dtpoff_base (info);
@@ -5021,11 +5058,17 @@  elf_x86_64_relocate_section (struct bfd_link_info *info,
 
 	case R_X86_64_TPOFF32:
 	case R_X86_64_TPOFF64:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  BFD_ASSERT (bfd_link_executable (info));
 	  relocation = elf_x86_64_tpoff (info, relocation);
 	  break;
 
 	case R_X86_64_DTPOFF64:
+	  if ((input_section->flags & (SEC_ALLOC | SEC_DEBUGGING)) == 0)
+	    goto non_alloc_error;
+
 	  BFD_ASSERT ((input_section->flags & SEC_CODE) == 0);
 	  relocation -= _bfd_x86_elf_dtpoff_base (info);
 	  break;
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index 0f79ef58f02..e08fec52636 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -3369,23 +3369,58 @@  _bfd_x86_elf_link_report_tls_transition_error
   bfd_set_error (bfd_error_bad_value);
 }
 
-/* Report TLS invalid section error.  */
+/* Report link error.  */
 
 void
-_bfd_x86_elf_link_report_tls_invalid_section_error
+_bfd_x86_elf_link_report_error
   (bfd *abfd, asection *sec, Elf_Internal_Shdr *symtab_hdr,
    struct elf_link_hash_entry *h, Elf_Internal_Sym *sym,
-   reloc_howto_type *howto)
+   reloc_howto_type *howto, enum elf_x86_error_type type)
 {
   const char *name;
+  bool non_thread_local;
   if (h)
-    name = h->root.root.string;
+    {
+      non_thread_local = h->type != STT_TLS;
+      name = h->root.root.string;
+    }
   else
-    name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
-  _bfd_error_handler
-    /* xgettext:c-format */
-    (_("%pB: relocation %s against thread local symbol `%s' in "
-       "invalid section `%pA'"), abfd, howto->name, name, sec);
+    {
+      non_thread_local = ELF_ST_TYPE (sym->st_info) != STT_TLS;
+      name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+      if (name[0] == '\0')
+	name = "*unknown*";
+    }
+
+  switch (type)
+    {
+    case elf_x86_error_tls:
+      if (non_thread_local)
+	_bfd_error_handler
+	  /* xgettext:c-format */
+	  (_("%pB: relocation %s against non-thread local symbol "
+	     "`%s' in section `%pA'"),
+	   abfd, howto->name, name, sec);
+      else
+	_bfd_error_handler
+	  /* xgettext:c-format */
+	  (_("%pB: relocation %s against thread local symbol `%s' in "
+	     "invalid section `%pA'"), abfd, howto->name, name, sec);
+      break;
+
+    case elf_x86_error_non_alloc:
+      _bfd_error_handler
+	/* xgettext:c-format */
+	(_("%pB: relocation %s against symbol `%s' in non-alloc "
+	   "section `%pA'"),
+	 abfd, howto->name, name, sec);
+      break;
+
+    default:
+      abort ();
+      break;
+    }
+
   bfd_set_error (bfd_error_bad_value);
 }
 
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index 4932b2e43c5..430d61d3097 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -794,6 +794,12 @@  enum elf_x86_tls_error_type
   elf_x86_tls_error_yes
 };
 
+enum elf_x86_error_type
+{
+  elf_x86_error_tls,
+  elf_x86_error_non_alloc
+};
+
 /* Set if a relocation is converted from a GOTPCREL relocation.  */
 #define R_X86_64_converted_reloc_bit (1 << 7)
 
@@ -951,9 +957,10 @@  extern void _bfd_x86_elf_link_report_tls_transition_error
    const Elf_Internal_Rela *, const char *, const char *,
    enum elf_x86_tls_error_type) ATTRIBUTE_HIDDEN;
 
-extern void _bfd_x86_elf_link_report_tls_invalid_section_error
+extern void _bfd_x86_elf_link_report_error
   (bfd *, asection *, Elf_Internal_Shdr *, struct elf_link_hash_entry *,
-   Elf_Internal_Sym *, reloc_howto_type *) ATTRIBUTE_HIDDEN;
+   Elf_Internal_Sym *, reloc_howto_type *,
+   enum elf_x86_error_type) ATTRIBUTE_HIDDEN;
 
 extern bool
 _bfd_elf_x86_copy_special_section_fields
-- 
2.55.0