If the displacement from a "br*" instruction to the target of
the branch is 128K or more, gas writes garbace into the displacement
field(s) instead of generating a relocation overflow error.
The branch on register condition instructions have a 16-bit signed
displacement field, but it is split into two pieces of the instruction.
The low 14 bits are at bits 0-13 of the instruction, bits 14 and 15
of the displacement are at bits 21-20 of the instruction.
The test case is very simple:
.skip (128 * 1024)
brz,pt %o0, 1b
Build this with "as -Av9a -o test.o test.s". GAS allows this
erroneously, instead of generating a relocation failure.
You can look at the assembler of the object file with
"objdump --disassemble test.o" you will see output like:
0: 01 00 00 00 nop
20004: 02 da 3f ff brz %o0, 0x40000
20008: 01 00 00 00 nop
which is obviously bogus.
sparc-opc.c uses 'k' character class for the relocation, this is
handled in tc-sparc.c with:
the_insn.reloc = /* RELOC_WDISP2_14 */ BFD_RELOC_SPARC_WDISP16;
the_insn.pcrel = 1;
The comment reference to the mythical "RELOC_WDISP2_14" is quite
Later down in tc-sparc.c we have the handler for BFD_RELOC_SPARC_WDISP16:
/* FIXME: simplify. */
if (((val > 0) && (val & ~0x3fffc))
|| ((val < 0) && (~(val - 1) & ~0x3fffc)))
as_bad_where (fixP->fx_file, fixP->fx_line,
/* FIXME: The +1 deserves a comment. */
val = (val >> 2) + 1;
insn |= ((val & 0xc000) << 6) | (val & 0x3fff);
And here we have the bug, the reloc range checking is wrong.
The masks should be 0x1fffc instead of 0x3fffc.
Created attachment 1868 [details]
Fix for BFD_RELOC_SPARC_WDISP16 overflow checking.