https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71043

            Bug ID: 71043
           Summary: MIPS: Don't generate multiple lo_sums to access
                    unaligned symbols or fields in packed structures
           Product: gcc
           Version: 6.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: andrew.bennett at imgtec dot com
  Target Milestone: ---

In MIPS (and similarly for other RISC architectures) to load an absolute
address of an object requires a two instruction sequence: one instruction to
load the high part of the object's address, and one instruction to load the low
part of the object's address.  Typically the result from the calculation of the
high part of the address will only be used by one instruction to load the low
part of the address.  However, when loading or storing double word values, or
accessing fields in a packed structure the the high part of the address can be
used by multiple instructions to load the low parts of an address at different
offsets.  Lets show this with an example C program.


struct __attribute__((packed))
{
 short s;
 unsigned long long l;
} h;

void foo (void)
{
 h.l = 0;
}


When this is compiled for MIPS it produces the following assembly:

        lui     $2,%hi(h)
        addiu   $4,$2,%lo(h+2)
        addiu   $3,$2,%lo(h+6)
        swl     $0,3($4)
        swr     $0,%lo(h+2)($2)
        swl     $0,3($3)
        jr      $31
        swr     $0,%lo(h+6)($2)

        ...

          .globl  h
        .section        .bss,"aw",@nobits
        .align  2
        .type   h, @object
        .size   h, 10
h:
        .space  10

The high part of the address of object h is loaded into register $2, and this
is then used as part of the low part calculation by two the addiu/swr
instructions which each have different offsets.  In MIPS the value of a low
part calculation is treated as a signed value. It is therefore valid to use the
result of a high part calculation with multiple low part calculations
containing different offsets so long as when adding the result of the high part
to the each of the sign extended low parts we get valid addresses. 
Unfortunately in this testcase we have low part calculations which add an
offset of 6 to the address of the h structure which is greater than its
alignment.  This will cause invalid address to be calculated in certain
situations.

To explain why lets show how this example will be linked if it is built using
the O32 ABI.  Here when the MIPS linker resolves a HI relocation (i.e. %hi(h))
it finds the next LO relocation (i.e. %lo(h+2)) in the relocation table and
using the information from both of these relocations it computes the object's
address and extracts its high part.  Then, when the MIPS linker resolves a LO
relocation it adds the offset to the object's address and then extracts the low
part.

Lets assume that object h has an address of 0x80007ffc.  When the MIPS linker
resolves the value of the HI relocation for object h, it will also use the
value of the LO relocation for object h with an offset of 2.  The high part
value is therefore:

HIGH (0x80007ffc + 2) = HIGH (0x80007ffe) = 0x8000


Then the MIPS linker resolves the value of LO relocation for object h with an
offset of 2:

LO (0x80007ffc + 2) = LO (0x80007ffe) = 0x7ffe


Finally the MIPS linker resolves the value of the LO relocation for object h
with an offset of 6:

LO (0x80007ffc + 6) = LO (0x80008002) = 0x8002

In MIPS the value of a LO relocation is treated as a signed value, so when the
program is run the address of h+6 will be 0x7fff8002 when it should be
0x80008002.

Reply via email to