From: Jan Beulich Date: Mon, 8 Sep 2025 09:11:51 +0000 (+0200) Subject: x86: constrain and fix use of the "nojumps" .arch modifier X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5d0ac9ee8fd007e90ba3fef3b95fd756cc191401;p=thirdparty%2Fbinutils-gdb.git 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 --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+ : +[ ]*[a-f0-9]+: 73 03 jae 5 +[ ]*[a-f0-9]+: e9 81 00 jmp 86 +[ ]*[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 +#... +0+86 : +[ ]*[a-f0-9]+: 75 03 jne 8b +[ ]*[a-f0-9]+: e9 75 ff jmp 0 +[ ]*[a-f0-9]+: 74 03 je 90 +[ ]*[a-f0-9]+: e9 (00|fe) (00|ff) jmp (90|8e) <.*> 8e: R_(386|X86_64)_PC16 .* +[ ]*[a-f0-9]+: 75 03 jne 95 +[ ]*[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 +[ ]*[a-f0-9]+: e9 66 ff jmp 0 +[ ]*[a-f0-9]+: c3 ret +#... +Disassembly of section \.text\.other: + +0+ : +[ ]*[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