Bug 4558 - branch on register condition accepts relocations >= 128K
Summary: branch on register condition accepts relocations >= 128K
Alias: None
Product: binutils
Classification: Unclassified
Component: gas (show other bugs)
Version: 2.18
: P2 normal
Target Milestone: ---
Assignee: unassigned
Depends on:
Reported: 2007-05-28 07:26 UTC by David S. Miller
Modified: 2007-10-09 03:44 UTC (History)
1 user (show)

See Also:
Host: sparc-unknown-linux-gnu
Target: sparc-unknown-liunx-gnu
Build: sparc-unknown-linux-gnu
Last reconfirmed:

Fix for BFD_RELOC_SPARC_WDISP16 overflow checking. (399 bytes, patch)
2007-05-28 07:33 UTC, David S. Miller
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description David S. Miller 2007-05-28 07:26:20 UTC
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:

1: nop
   .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:

00000000 <.text>:
       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:

            case 'k':
              the_insn.reloc = /* RELOC_WDISP2_14 */ BFD_RELOC_SPARC_WDISP16;
              the_insn.pcrel = 1;
              goto immediate;

The comment reference to the mythical "RELOC_WDISP2_14" is quite
curious :-)

Later down in tc-sparc.c we have the handler for BFD_RELOC_SPARC_WDISP16:

        case 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,
                          _("relocation overflow"));
          /* 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.
Comment 1 David S. Miller 2007-05-28 07:33:59 UTC
Created attachment 1868 [details]
Fix for BFD_RELOC_SPARC_WDISP16 overflow checking.