macho section symbol handling

Message ID ahQYN3DcQuk1vl9t@squeak.grove.modra.org
State New
Headers
Series macho section symbol handling |

Checks

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

Commit Message

Alan Modra May 25, 2026, 9:36 a.m. UTC
  The macho gas support starts a new frag at non-local labels,
identifying the frag with the label symbol as a "subsection".  Relocs
are needed when referencing labels in a different subsection, to
support relaxation.  There is a problem when reloc symbols are reduced
to a section symbol plus offset (see write.c:adjust_reloc_syms), as
this loses the subsection.  Not reducing symbols like this is not a
good option as it results in a large number of symbols, some with
weird internal gas names.  So instead this patch finds the original
frag for any fx_addsy reduced to a section symbol.

Test results are:
+FAIL: .org test 1
-FAIL: i386 opcodes
-FAIL: i386 opcodes (Intel disassembly)
-FAIL: i386 opcodes (w/ suffix)
-FAIL: i386 intel (AT&T disassembly)
-FAIL: i386 intel
-FAIL: Check -madd-bnd-prefix
-FAIL: x86-64 RTM insns
-FAIL: x86-64 RTM insns (Intel disassembly)
-FAIL: Check -madd-bnd-prefix (x86-64)

The reason ".org test 1" fails is the -gdwarf2 .debug_aranges
generates two temp symbols and uses them for the start and size of
each range, the size being calculated by "end" - "beg" (see
out_debug_aranges).  For the test, "beg" is before any source symbol
is emitted so has subsection NULL.  "end" has a subsection but lost
that when the fixup was converted to a section symbol plus offset.
So prior to this change obj_mach_o_in_different_subsection returned
false.  Now that the lost subsection is recovered for "end" it returns
true, and results in "Error: can't resolve .text - L0^A$".

	* config/obj-macho.c (obj_mach_o_in_different_subsection):
	Add parameters.  Get frag containing section sym plus offset.
	(obj_mach_o_force_reloc_sub_same): Adjust to suit
	obj_mach_o_in_different_subsection change.
	(obj_mach_o_force_reloc_sub_local): Likewise.
	(obj_mach_o_force_reloc): Likewise.
	* testsuite/gas/i386/insn-32.d: Don't xfail darwin.
	* write.c (get_frag_for_address): New function, extracted from..
	(get_frag_for_reloc): ..here.
	* write.h (get_frag_for_address): Declare.
  

Patch

diff --git a/gas/config/obj-macho.c b/gas/config/obj-macho.c
index 6de2410e1a7..bf6f2758bc7 100644
--- a/gas/config/obj-macho.c
+++ b/gas/config/obj-macho.c
@@ -1864,7 +1864,8 @@  obj_mach_o_allow_local_subtract (expressionS * left ATTRIBUTE_UNUSED,
 }
 
 static bool
-obj_mach_o_in_different_subsection (symbolS *a, symbolS *b)
+obj_mach_o_in_different_subsection (symbolS *a, segT aseg, valueT offset,
+				    symbolS *b)
 {
   fragS *fa;
   fragS *fb;
@@ -1877,7 +1878,10 @@  obj_mach_o_in_different_subsection (symbolS *a, symbolS *b)
       return true;
     }
 
-  fa = symbol_get_frag (a);
+  if (symbol_section_p (a) && aseg != NULL)
+    fa = get_frag_for_address (NULL, seg_info (aseg), offset);
+  else
+    fa = symbol_get_frag (a);
   fb = symbol_get_frag (b);
   if (fa == NULL || fb == NULL)
     {
@@ -1893,7 +1897,8 @@  obj_mach_o_force_reloc_sub_same (fixS *fix, segT seg)
 {
   if (! SEG_NORMAL (seg))
     return true;
-  return obj_mach_o_in_different_subsection (fix->fx_addsy, fix->fx_subsy);
+  return obj_mach_o_in_different_subsection (fix->fx_addsy, seg,
+					     fix->fx_offset, fix->fx_subsy);
 }
 
 bool
@@ -1902,7 +1907,7 @@  obj_mach_o_force_reloc_sub_local (fixS *fix, segT seg ATTRIBUTE_UNUSED)
   symbolS *fragsym = fix->fx_frag->obj_frag_data.subsection;
   if (fragsym == NULL)
     return false;
-  return obj_mach_o_in_different_subsection (fix->fx_subsy, fragsym);
+  return obj_mach_o_in_different_subsection (fix->fx_subsy, NULL, 0, fragsym);
 }
 
 bool
@@ -1916,16 +1921,18 @@  obj_mach_o_force_reloc (fixS *fix)
   if (fix->fx_addsy != NULL)
     {
       symbolS *subsec = fix->fx_frag->obj_frag_data.subsection;
-      symbolS *targ = fix->fx_addsy;
 
       /* There might be no subsections at all.  */
       if (subsec == NULL)
 	return false;
 
-      if (S_GET_SEGMENT (targ) == absolute_section)
+      symbolS *targ = fix->fx_addsy;
+      segT targseg = S_GET_SEGMENT (targ);
+      if (targseg == absolute_section)
 	return false;
 
-      return obj_mach_o_in_different_subsection (targ, subsec);
+      return obj_mach_o_in_different_subsection (targ, targseg,
+						 fix->fx_offset, subsec);
     }
   return false;
 }
diff --git a/gas/testsuite/gas/i386/insn-32.d b/gas/testsuite/gas/i386/insn-32.d
index c2d8202bd2e..2649fdcf94e 100644
--- a/gas/testsuite/gas/i386/insn-32.d
+++ b/gas/testsuite/gas/i386/insn-32.d
@@ -1,7 +1,6 @@ 
 #as: --divide
 #objdump: -dw
 #name: .insn (32-bit code)
-#xfail: *-*-darwin*
 
 .*: +file format .*
 
diff --git a/gas/write.c b/gas/write.c
index 9bb47857936..9514c3df42e 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -1228,33 +1228,41 @@  install_reloc (asection *sec, arelent *reloc, fragS *fragp,
     }
 }
 
-static fragS *
-get_frag_for_reloc (fragS *last_frag,
-		    const segment_info_type *seginfo,
-		    const struct reloc_list *r)
+fragS *
+get_frag_for_address (fragS *last_frag,
+		      const segment_info_type *seginfo,
+		      addressT addr)
 {
   fragS *f;
 
   for (f = last_frag; f != NULL; f = f->fr_next)
-    if (f->fr_address <= r->u.b.r.address
-	&& r->u.b.r.address < f->fr_address + f->fr_fix)
+    if (f->fr_address <= addr && addr < f->fr_address + f->fr_fix)
       return f;
 
   for (f = seginfo->frchainP->frch_root; f != NULL; f = f->fr_next)
-    if (f->fr_address <= r->u.b.r.address
-	&& r->u.b.r.address < f->fr_address + f->fr_fix)
+    if (f->fr_address <= addr && addr < f->fr_address + f->fr_fix)
       return f;
 
   for (f = seginfo->frchainP->frch_root; f != NULL; f = f->fr_next)
-    if (f->fr_address <= r->u.b.r.address
-	&& r->u.b.r.address <= f->fr_address + f->fr_fix)
+    if (f->fr_address <= addr && addr <= f->fr_address + f->fr_fix)
       return f;
 
-  as_bad_where (r->file, r->line,
-		_("reloc not within (fixed part of) section"));
   return NULL;
 }
 
+static fragS *
+get_frag_for_reloc (fragS *last_frag,
+		    const segment_info_type *seginfo,
+		    const struct reloc_list *r)
+{
+  fragS *f = get_frag_for_address (last_frag, seginfo, r->u.b.r.address);
+
+  if (f == NULL)
+    as_bad_where (r->file, r->line,
+		  _("reloc not within (fixed part of) section"));
+  return f;
+}
+
 static void
 write_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
 	      void *xxx ATTRIBUTE_UNUSED)
diff --git a/gas/write.h b/gas/write.h
index 7281930c2d6..bc5260a2efd 100644
--- a/gas/write.h
+++ b/gas/write.h
@@ -188,5 +188,8 @@  extern fixS *fix_new_exp (fragS *, unsigned long, unsigned long,
 			  const expressionS *, int, bfd_reloc_code_real_type);
 extern void write_print_statistics (FILE *);
 extern void as_bad_subtract (fixS *);
+struct segment_info_struct;
+extern fragS *get_frag_for_address (fragS *, const struct segment_info_struct *,
+				    addressT);
 
 #endif /* __write_h__ */