[binutils-gdb] x86: constrain and fix use of the "nojumps" .arch modifier

Jan Beulich jbeulich@sourceware.org
Mon Sep 8 09:13:14 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=5d0ac9ee8fd007e90ba3fef3b95fd756cc191401

commit 5d0ac9ee8fd007e90ba3fef3b95fd756cc191401
Author: Jan Beulich <jbeulich@suse.com>
Date:   Mon Sep 8 11:11:51 2025 +0200

    x86: constrain and fix use of the "nojumps" .arch modifier
    
    As said by the paragraph of the description that isn't modified here (a
    few lines up), this was only ever supposed to be used with 16-bit
    architectures. Actually enforcing this allows code in
    md_estimate_size_before_relax() to move to a less frequently used code
    path. (For backwards compatibility, keep accepting "jumps" also with 32-
    or 64-bit architectures.)
    
    Repeat the constraint also in the 2nd paragraph of the doc on this
    subject. And while there also insert a missing insn in the related
    i386-Jumps section.
    
    Furthermore checking a global variable during late processing is wrong. We
    need to record the state in the fragment, and use that rather than the
    state of the variable at the end of parsing all input.
    
    Seeing that there's no testing of the functionality at all, add a testcase
    as well.

Diff:
---
 gas/config/tc-i386.c             | 36 +++++++++++++++++++++---------------
 gas/config/tc-i386.h             |  1 +
 gas/doc/c-i386.texi              | 12 ++++++------
 gas/testsuite/gas/i386/i386.exp  |  1 +
 gas/testsuite/gas/i386/nojumps.d | 31 +++++++++++++++++++++++++++++++
 gas/testsuite/gas/i386/nojumps.e |  3 +++
 gas/testsuite/gas/i386/nojumps.s | 21 +++++++++++++++++++++
 7 files changed, 84 insertions(+), 21 deletions(-)

diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 6afbd0e2103..7f7f62be8f5 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -3517,7 +3517,12 @@ set_cpu_arch (int dummy ATTRIBUTE_UNUSED)
       string = s;
 
       if (strcmp (string, "nojumps") == 0)
-	no_cond_jump_promotion = 1;
+	{
+	  if (cpu_arch_flags.bitfield.cpui386)
+	    as_bad (_("`%s' only supported with 16-bit architectures"), string);
+	  else
+	    no_cond_jump_promotion = true;
+	}
       else if (strcmp (string, "jumps") != 0)
 	{
 	  as_bad (_("no such architecture modifier: `%s'"), string);
@@ -15681,6 +15686,7 @@ void i386_frag_init (fragS *fragP, size_t max_bytes)
   fragP->tc_frag_data.last_insn_normal
     = (seg_info(now_seg)->tc_segment_info_data.last_insn.kind
        == last_insn_other);
+  fragP->tc_frag_data.no_cond_jump_promotion = no_cond_jump_promotion;
 }
 
 /* Calculate the maximum variable size (i.e., excluding fr_fix)
@@ -16198,8 +16204,19 @@ md_estimate_size_before_relax (fragS *fragP, segT segment)
 	  break;
 
 	case COND_JUMP86:
-	  if (size == 2
-	      && (!no_cond_jump_promotion || fragP->fr_var != NO_RELOC))
+	  if (fragP->tc_frag_data.no_cond_jump_promotion
+	      && fragP->fr_var == NO_RELOC)
+	    {
+	      fragP->fr_fix += 1;
+	      fixP = fix_new (fragP, old_fr_fix, 1,
+			      fragP->fr_symbol,
+			      fragP->fr_offset, 1,
+			      BFD_RELOC_8_PCREL);
+	      fixP->fx_signed = 1;
+	      break;
+	    }
+
+	  if (size == 2)
 	    {
 	      /* Negate the condition, and branch past an
 		 unconditional jump.  */
@@ -16221,17 +16238,6 @@ md_estimate_size_before_relax (fragS *fragP, segT segment)
 	  /* Fall through.  */
 
 	case COND_JUMP:
-	  if (no_cond_jump_promotion && fragP->fr_var == NO_RELOC)
-	    {
-	      fragP->fr_fix += 1;
-	      fixP = fix_new (fragP, old_fr_fix, 1,
-			      fragP->fr_symbol,
-			      fragP->fr_offset, 1,
-			      BFD_RELOC_8_PCREL);
-	      fixP->fx_signed = 1;
-	      break;
-	    }
-
 	  /* This changes the byte-displacement jump 0x7N
 	     to the (d)word-displacement jump 0x0f,0x8N.  */
 	  opcode[1] = opcode[0] + 0x10;
@@ -16409,7 +16415,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
     }
   else
     {
-      if (no_cond_jump_promotion
+      if (fragP->tc_frag_data.no_cond_jump_promotion
 	  && TYPE_FROM_RELAX_STATE (fragP->fr_subtype) != UNCOND_JUMP)
 	as_warn_where (fragP->fr_file, fragP->fr_line,
 		       _("long jump required"));
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 1cdf8fc39e7..0a38f6a017b 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -329,6 +329,7 @@ struct i386_tc_frag_data
   unsigned int cpunop : 1;
   unsigned int isanop : 1;
   unsigned int last_insn_normal : 1;
+  bool no_cond_jump_promotion : 1;
 };
 
 /* We need to emit the right NOP pattern in .align frags.  This is
diff --git a/gas/doc/c-i386.texi b/gas/doc/c-i386.texi
index b1f3b04ff1c..6598a04e6ef 100644
--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -1463,10 +1463,10 @@ instruction with the @samp{data16} instruction prefix), since the 80386
 insists upon masking @samp{%eip} to 16 bits after the word displacement
 is added. (See also @pxref{i386-Arch})
 
-Note that the @samp{jcxz}, @samp{jecxz}, @samp{loop}, @samp{loopz},
-@samp{loope}, @samp{loopnz} and @samp{loopne} instructions only come in byte
-displacements, so that if you use these instructions (@code{@value{GCC}} does
-not use them) you may get an error message (and incorrect code).  The AT&T
+Note that the @samp{jcxz}, @samp{jecxz}, @samp{jrcxz}, @samp{loop},
+@samp{loopz}, @samp{loope}, @samp{loopnz} and @samp{loopne} instructions only
+come with byte displacements, so that if you use these instructions
+(@code{@value{GCC}} does not use them) you may get an error message.  The AT&T
 80386 assembler tries to get around this problem by expanding @samp{jcxz foo}
 to
 
@@ -1760,8 +1760,8 @@ suffixes will disable all insns with wider vector or mask register operands.
 On SVR4-derived platforms, the separator character @samp{/} can be replaced by
 @samp{:}.
 
-Following the CPU architecture (but not a sub-architecture, which are those
-starting with a dot), you may specify @samp{jumps} or @samp{nojumps} to
+Following a 16-bit CPU architecture (but not a sub-architecture, which are
+those starting with a dot), you may specify @samp{jumps} or @samp{nojumps} to
 control automatic promotion of conditional jumps. @samp{jumps} is the
 default, and enables jump promotion;  All external jumps will be of the long
 variety, and file-local jumps will be promoted as necessary.
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index 137c1002073..564ca826ca5 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -861,6 +861,7 @@ if {[is_elf_format] || [istarget "*-*-vxworks*"]} then {
     run_dump_test "dw2-compressed-3b"
 
     if {![istarget "i*86-*-elfiamcu"]} then {
+	run_dump_test nojumps
 	run_dump_test "property-2"
 	run_dump_test "property-3"
 	run_dump_test "property-4"
diff --git a/gas/testsuite/gas/i386/nojumps.d b/gas/testsuite/gas/i386/nojumps.d
new file mode 100644
index 00000000000..7b2f1c1eda5
--- /dev/null
+++ b/gas/testsuite/gas/i386/nojumps.d
@@ -0,0 +1,31 @@
+#name: ix86 nojumps
+#objdump: -drw -Mi8086
+#warning_output: nojumps.e
+
+.*: +file format .*86.*
+
+Disassembly of section \.text:
+
+0+ <early>:
+[ 	]*[a-f0-9]+:	73 03                	jae    5 <early\+0x5>
+[ 	]*[a-f0-9]+:	e9 81 00             	jmp    86 <late>
+[ 	]*[a-f0-9]+:	70 (00|ff)                	jo     [76] <.*>	6: R_(386|X86_64)_PC8	undef.*
+[ 	]*[a-f0-9]+:	7a (00|ff)                	jp     [98] <.*>	8: R_(386|X86_64)_PC8	.*
+[ 	]*[a-f0-9]+:	eb 7b                	jmp    86 <late>
+#...
+0+86 <late>:
+[ 	]*[a-f0-9]+:	75 03                	jne    8b <late\+0x5>
+[ 	]*[a-f0-9]+:	e9 75 ff             	jmp    0 <early>
+[ 	]*[a-f0-9]+:	74 03                	je     90 <late\+0xa>
+[ 	]*[a-f0-9]+:	e9 (00|fe) (00|ff)             	jmp    (90|8e) <.*>	8e: R_(386|X86_64)_PC16	.*
+[ 	]*[a-f0-9]+:	75 03                	jne    95 <late\+0xf>
+[ 	]*[a-f0-9]+:	e9 (00|fe) (00|ff)             	jmp    9[53] <.*>	93: R_(386|X86_64)_PC16	undef.*
+[ 	]*[a-f0-9]+:	79 03                	jns    9a <late\+0x14>
+[ 	]*[a-f0-9]+:	e9 66 ff             	jmp    0 <early>
+[ 	]*[a-f0-9]+:	c3                   	ret
+#...
+Disassembly of section \.text\.other:
+
+0+ <other>:
+[ 	]*[a-f0-9]+:	78 (00|ff)                	js     [21] <.*>	1: R_(386|X86_64)_PC8	.*
+#pass
diff --git a/gas/testsuite/gas/i386/nojumps.e b/gas/testsuite/gas/i386/nojumps.e
new file mode 100644
index 00000000000..b5614c12c5b
--- /dev/null
+++ b/gas/testsuite/gas/i386/nojumps.e
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*:11: Warning: long jump required
+.*:16: Warning: long jump required
diff --git a/gas/testsuite/gas/i386/nojumps.s b/gas/testsuite/gas/i386/nojumps.s
new file mode 100644
index 00000000000..faf088b6aaf
--- /dev/null
+++ b/gas/testsuite/gas/i386/nojumps.s
@@ -0,0 +1,21 @@
+	.code16
+	.arch i8086,nojumps
+
+	.section .text.other, "ax", @progbits
+other:
+	js	early
+	ret
+
+	.text
+early:
+	jc	late
+	jo	undef
+	jp	other
+	.nops 125
+late:
+	jz	early
+	.arch i8086,jumps
+	jnz	other
+	jz	undef
+	js	early
+	ret


More information about the Binutils-cvs mailing list