]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
x86: constrain and fix use of the "nojumps" .arch modifier
authorJan Beulich <jbeulich@suse.com>
Mon, 8 Sep 2025 09:11:51 +0000 (11:11 +0200)
committerJan Beulich <jbeulich@suse.com>
Mon, 8 Sep 2025 09:11:51 +0000 (11:11 +0200)
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.

gas/config/tc-i386.c
gas/config/tc-i386.h
gas/doc/c-i386.texi
gas/testsuite/gas/i386/i386.exp
gas/testsuite/gas/i386/nojumps.d [new file with mode: 0644]
gas/testsuite/gas/i386/nojumps.e [new file with mode: 0644]
gas/testsuite/gas/i386/nojumps.s [new file with mode: 0644]

index 6afbd0e2103a400993e781b3400fdb31b44331dd..7f7f62be8f592036f063bdf9ab19007abbcfecc1 100644 (file)
@@ -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"));
index 1cdf8fc39e7effcb4dc70ebf31bfa2b5770f9ba8..0a38f6a017bc350118a64926f6b96b689bbb1e69 100644 (file)
@@ -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
index b1f3b04ff1c2cc6adf51c38d5ef0b79cd213d464..6598a04e6ef97a6683a921e1ce633f542e4311e7 100644 (file)
@@ -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.
index 137c10020732c2270fe00a474ff3436f7771f850..564ca826ca559ec893970adc193b03e87e3f453f 100644 (file)
@@ -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 (file)
index 0000000..7b2f1c1
--- /dev/null
@@ -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 (file)
index 0000000..b5614c1
--- /dev/null
@@ -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 (file)
index 0000000..faf088b
--- /dev/null
@@ -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