[PATCH 2/2] x86: constrain and fix use of the "nojumps" .arch modifier

Jan Beulich jbeulich@suse.com
Fri Aug 29 11:47:51 GMT 2025


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.
---
Using output_jump() also for Jcc when "nojumps" is in effect would yield
an overall more consistent result, but would violate what's documented for
local targets.

--- 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_
   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 *fr
 	  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 *fr
 	  /* 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_UNU
     }
   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"));
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -331,6 +331,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
--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -1463,10 +1463,10 @@ instruction with the @samp{data16} instr
 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 wid
 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.
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -850,6 +850,7 @@ if {![is_aout_format]} then {
     run_list_test pr32721 ""
 }
 if {[is_elf_format] || [istarget "*-*-vxworks*"]} then {
+    run_dump_test nojumps
     run_list_test_stdin "list-1" "-al"
     run_list_test_stdin "list-2" "-al"
     run_list_test_stdin "list-3" "-al"
--- /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                	jo     7 <.*>	6: R_X86_64_PC8	undef.*
+[ 	]*[a-f0-9]+:	7a 00                	jp     9 <.*>	8: R_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 00             	jmp    90 <.*>	8e: R_X86_64_PC16	.*
+[ 	]*[a-f0-9]+:	75 03                	jne    95 <late\+0xf>
+[ 	]*[a-f0-9]+:	e9 00 00             	jmp    95 <.*>	93: R_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                	js     2 <.*>	1: R_X86_64_PC8	.*
+#pass
--- /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
--- /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 mailing list