[patch] MIPS: Follow the ABI rules for ordering HI16_S/LO16 relocs

Maciej W. Rozycki macro@linux-mips.org
Mon Jun 28 16:22:00 GMT 2004


Hello,

 This is a follow-on patch to the "mips-merge-lo16" one, that is required 
for gas to handle the case when multiple LO16 relocations are associated 
with a preceding HI16_S (or GOT16) one.

 This is one of the fortunate areas the MIPS ABI supplement is clear
about.  On page 4-18 of the spec, there is the following statement:  
"R_MIPS_LO16 entries without an R_MIPS_HI16 entry immediately preceding
are orphaned and the previously defined R_MIPS_HI16 is used for computing
the addend."  The implication is for a correct calculation of the addend 
for a LO16 relocation, the corresponding HI16 relocation has to precede it 
with no other HI16 relocations inbetween.

 Here's a patch that extends reordering of HI16_S/GOT16 and LO16 
relocations.  After pairing all HI16_S/GOT16 relocations with a 
corresponding LO16 one, all sections are scanned for any orphaned LO16 
relocations and if ones are found, they are grouped following the closest 
matching HI16_S/GOT16 one.  If no matching HI16_S/GOT16 is found a warning 
is issued as a broken binary results.

 The included test case illustrates operation -- orphaned LO16 relocations
are actually quite a common case with current gcc if explicit relocs are
used.  Building the Linux kernel with the "mips-merge-lo16" patch applied
to binutils yields several linker warnings about an "access beyond end of
merged section", which is a sign of using a wrong HI16_S relocation for
calculating the addend of a LO16 one.

gas/:
2004-06-28  Maciej W. Rozycki  <macro@linux-mips.org>

	* config/tc-mips.c (mips_frob_file): Rename to...
	(adjust_hi_relocs): ... this.
	(adjust_lo_relocs): New function to attach orphaned LO16 relocs to 
	their corresponding HI16_S/GOT16 ones.
	(mips_frob_file): New function wrapping adjust_hi_relocs() and 
	adjust_lo_relocs().

gas/testsuite/:
2004-06-28  Maciej W. Rozycki  <macro@linux-mips.org>

	* gas/mips/elf-rel20.d: New test for reordering of orphaned LO16 
	relocs.
	* gas/mips/elf-rel20.l: Stderr output for the new test.
	* gas/mips/elf-rel20.s: Source for the new test.
        * gas/mips/mips.exp: Run the new test.

 This has been tested for the mipsel-linux-gnu target with a lot of new 
regressions being results of genuine gas bugs.  I'll address each of them 
in following mails.

 OK to apply?

  Maciej

binutils-2.15.91-20040625-mips-hilo-reloc-sort.patch
diff -up --recursive --new-file binutils-2.15.91-20040625.macro/gas/config/tc-mips.c binutils-2.15.91-20040625/gas/config/tc-mips.c
--- binutils-2.15.91-20040625.macro/gas/config/tc-mips.c	2004-06-15 03:25:28.000000000 +0000
+++ binutils-2.15.91-20040625/gas/config/tc-mips.c	2004-06-27 18:24:07.000000000 +0000
@@ -872,6 +872,8 @@ static void mips16_immed
 static size_t my_getSmallExpression
   (expressionS *, bfd_reloc_code_real_type *, char *);
 static void my_getExpression (expressionS *, char *);
+static void adjust_hi_relocs (void);
+static void adjust_lo_relocs (bfd *, asection *, void *);
 static void s_align (int);
 static void s_change_sec (int);
 static void s_change_section (int);
@@ -10670,14 +10672,23 @@ mips_frob_file_before_adjust (void)
 #endif
 }
 
-/* Sort any unmatched HI16_S relocs so that they immediately precede
-   the corresponding LO reloc.  This is called before md_apply_fix3 and
-   tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
-   explicit use of the %hi modifier.  */
+/* Reorder HI16_S and LO16 relocs to match ABI rules.  This is called
+   before md_apply_fix3 and tc_gen_reloc.  */
 
 void
 mips_frob_file (void)
 {
+  adjust_hi_relocs ();
+  bfd_map_over_sections (stdoutput, adjust_lo_relocs, NULL);
+}
+
+/* Sort any unmatched HI16_S relocs so that they immediately precede
+   the corresponding LO reloc.  Unmatched HI16_S relocs can only be
+   generated by explicit use of the %hi modifier.  */
+
+static void
+adjust_hi_relocs (void)
+{
   struct mips_hi_fixup *l;
 
   for (l = mips_hi_fixup_list; l != NULL; l = l->next)
@@ -10754,6 +10765,85 @@ mips_frob_file (void)
     }
 }
 
+/* Find any orphaned LO16 relocs and reorder them so that they follow
+   their corresponding HI16_S relocs with no other intervening HI16_S relocs.
+   Unmatched LO16 relocs can only be generated by explicit use of the %lo
+   modifier.  */
+
+static void
+adjust_lo_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *seg,
+		  void *ptr ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (seg);
+  fixS *f, *pf = NULL;
+
+  for (f = seginfo->fix_root; f != NULL; pf = f, f = f->fx_next)
+    if (f->fx_r_type == BFD_RELOC_LO16)
+      {
+	fixS *g, *prec_hi = seginfo->fix_root, *match_hi = NULL;
+
+	/* Find the last preceding HI16_S reloc and the last preceding
+	   matching HI16_S reloc.  */
+	for (g = seginfo->fix_root; g != f; g = g->fx_next)
+	  {
+	    if (g->fx_r_type == BFD_RELOC_HI16_S
+		|| (g->fx_r_type == BFD_RELOC_MIPS_GOT16
+		    && pic_need_relax (g->fx_addsy, seg)))
+	      prec_hi = g;
+	    /* Permit matches against GOT16 relocs even if against global
+	       symbols to handle variant frags.  */
+	    if ((g->fx_r_type == BFD_RELOC_HI16_S
+		 || g->fx_r_type == BFD_RELOC_MIPS_GOT16)
+		&& g->fx_addsy == f->fx_addsy && g->fx_offset == f->fx_offset)
+	      {
+	        prec_hi = g;
+		match_hi = g;
+	      }
+	  }
+
+	/* If they're the same, there's nothing to do.  */
+	if (prec_hi == match_hi)
+	  continue;
+
+	/* If there's no preceding matching HI16_S reloc, then try
+	   to match with any succeeding one.  */
+	if (!match_hi)
+	  for (g = f; g != NULL; g = g->fx_next)
+	    if ((g->fx_r_type == BFD_RELOC_HI16_S
+		 || g->fx_r_type == BFD_RELOC_MIPS_GOT16)
+		&& g->fx_addsy == f->fx_addsy && g->fx_offset == f->fx_offset)
+		  match_hi = g;
+
+	/* If there's still, then it's a true orphan.  The source is
+	   broken; can't do much about it.  */
+	if (!match_hi)
+	  {
+	    as_warn_where (f->fx_file, f->fx_line,
+			   _("orphaned %%lo relocation"));
+	    continue;
+	  }
+
+	/* Order orphaned siblings by the increasing section offset.  */
+	for (g = match_hi->fx_next; g != NULL; g = g->fx_next)
+	  {
+	    if (g->fx_r_type != BFD_RELOC_LO16
+		|| g->fx_addsy != f->fx_addsy || g->fx_offset != f->fx_offset
+		|| g->fx_where > f->fx_where)
+	      break;
+	    match_hi = g;
+	  }
+
+	/* Finally place the orphan where appropriate.  */
+	g = f->fx_next;
+	f->fx_next = match_hi->fx_next;
+	match_hi->fx_next = f;
+	if (pf == NULL)
+	  seginfo->fix_root = g;
+	else
+	  pf->fx_next = g;
+      }
+}
+
 /* We may have combined relocations without symbols in the N32/N64 ABI.
    We have to prevent gas from dropping them.  */
 
diff -up --recursive --new-file binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/elf-rel20.d binutils-2.15.91-20040625/gas/testsuite/gas/mips/elf-rel20.d
--- binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/elf-rel20.d	1970-01-01 00:00:00.000000000 +0000
+++ binutils-2.15.91-20040625/gas/testsuite/gas/mips/elf-rel20.d	2004-06-27 16:50:49.000000000 +0000
@@ -0,0 +1,17 @@
+#readelf: --relocs
+#name: MIPS ELF reloc 20
+#as: -march=mips1 -mabi=32
+#source: elf-rel20.s
+#stderr: elf-rel20.l
+
+# Test reordering of orphaned lo16 relocs.
+
+Relocation section '\.rel\.text' at offset .* contains 7 entries:
+ *Offset * Info * Type * Sym\.Value * Sym\. Name
+0+000004 * 0+000205 * R_MIPS_HI16 * 0+000000 * \.data
+0+000000 * 0+000206 * R_MIPS_LO16 * 0+000000 * \.data
+0+000008 * 0+000206 * R_MIPS_LO16 * 0+000000 * \.data
+0+000014 * 0+000206 * R_MIPS_LO16 * 0+000000 * \.data
+0+00000c * 0+000a05 * R_MIPS_HI16 * 0+000000 * ext
+0+000010 * 0+000a06 * R_MIPS_LO16 * 0+000000 * ext
+0+000018 * 0+000206 * R_MIPS_LO16 * 0+000000 * \.data
diff -up --recursive --new-file binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/elf-rel20.l binutils-2.15.91-20040625/gas/testsuite/gas/mips/elf-rel20.l
--- binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/elf-rel20.l	1970-01-01 00:00:00.000000000 +0000
+++ binutils-2.15.91-20040625/gas/testsuite/gas/mips/elf-rel20.l	2004-06-27 16:45:44.000000000 +0000
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:19: Warning: orphaned %lo relocation
diff -up --recursive --new-file binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/elf-rel20.s binutils-2.15.91-20040625/gas/testsuite/gas/mips/elf-rel20.s
--- binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/elf-rel20.s	1970-01-01 00:00:00.000000000 +0000
+++ binutils-2.15.91-20040625/gas/testsuite/gas/mips/elf-rel20.s	2004-06-27 16:33:39.000000000 +0000
@@ -0,0 +1,21 @@
+# Source file used to test reordering of orphaned lo16 relocs.
+
+	.data
+nil:
+	.asciiz	"nil"
+foo:
+	.asciiz	"foo"
+bar:
+	.asciiz	"bar"
+
+	.text
+fun:
+	addiu	$2, $0, %lo(foo)
+	lui	$4, %hi(foo)
+	addiu	$5, $4, %lo(foo)
+	lui	$8, %hi(ext)
+	addiu	$8, $8, %lo(ext)
+	addiu	$4, $4, %lo(foo)
+	addiu	$3, $0, %lo(bar)
+
+	.space	16
diff -up --recursive --new-file binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/mips.exp binutils-2.15.91-20040625/gas/testsuite/gas/mips/mips.exp
--- binutils-2.15.91-20040625.macro/gas/testsuite/gas/mips/mips.exp	2004-05-12 03:25:38.000000000 +0000
+++ binutils-2.15.91-20040625/gas/testsuite/gas/mips/mips.exp	2004-06-27 16:43:14.000000000 +0000
@@ -660,6 +660,7 @@ if { [istarget mips*-*-*] } then {
 	    run_dump_test "elf-rel18"
 	}
 	run_dump_test "elf-rel19"
+	run_dump_test "elf-rel20"
 
 	if { !$no_mips16 } {
 	    run_dump_test "${tmips}mips${el}16-e"



More information about the Binutils mailing list