This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: PATCH for 64-bit MIPS ELF buglets


   From: Mark Mitchell <mark@codesourcery.com>
   Date: Mon, 12 Jul 1999 17:30:53 -0700

   Can you describe, in the same sort of way used in the "official" MIPS
   ABI documentation what R_MIPS16_26 and R_MIPS16_GPREL do?

I'm going to make this up; if what I say clearly contradicts the code,
follow the code.

R_MIPS16_26 is used for the mips16 jal and jalx instructions.  Most
mips16 instructions are 16 bits, but these instructions are 32 bits.

The format of these instructions is:

    +--------------+--------------------------------+
    !     JALX     ! X!   Imm 20:16  !   Imm 25:21  !
    +--------------+--------------------------------+
    !	  	  Immediate  15:0		    !
    +-----------------------------------------------+

JALX is the 5-bit value 00011.  X is 0 for jal, 1 for jalx.  Note that
the immediate value in the first word is swapped.

When producing a relocateable object file, handle R_MIPS16_26 mostly
like R_MIPS_26.  In particular, store the addend as a straight 26-bit
value in a 32-bit instruction.  (gas makes life simpler for itself by
never adjusting a R_MIPS16_26 reloc to be against a section, so the
addend is always zero).  However, the 32 bit instruction is stored as
2 16-bit values, rather than a single 32-bit value.  In a big-endian
file, the result is the same; in a little-endian file, the two 16-bit
halves of the 32 bit value are swapped.  This is so that a
disassembler can recognize the jal instruction.

When doing a final link, treat it as a 32 bit instruction stored as
two 16-bit values.  The addend A is the contents of the targ26 field.
The calculation is the same as R_MIPS_26.  When storing the calculated
value, reorder the immediate value as shown above, and don't forget to
store the value as two 16-bit values.

To put it in MIPS ABI terms, the relocation field is T-targ26-16,
defined as

big-endian:
    +--------+----------------------+
    |        |                      |
    |        |    targ26-16         |
    |31    26|25                   0|
    +--------+----------------------+

little-endian:
    +----------+------+-------------+
    |          |      |             |
    |  sub1    |      |     sub2    |
    |0        9|10  15|16         31|
    +----------+--------------------+
where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
((sub1 << 16) | sub2)).

When producing a relocateable object file, the calculation is
    (((A < 2) | (P & 0xf0000000) + S) >> 2)
When producing a fully linked file, the calculation is
    let R = (((A < 2) | (P & 0xf0000000) + S) >> 2)
    ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff)


R_MIPS16_GPREL is used for GP-relative addressing in mips16 mode.  A
typical instruction will have a format like this:

    +--------------+--------------------------------+
    !    EXTEND    !     Imm 10:5    !   Imm 15:11  !
    +--------------+--------------------------------+
    !    Major     !   rx   !   ry   !   Imm  4:0   !
    +--------------+--------------------------------+

EXTEND is the five bit value 11110.  Major is the instruction opcode.

This is handled exactly like R_MIPS_GPREL16, except that the addend is
retrieved and stored as shown in this diagram; that is, the Imm fields
above replace the V-rel16 field.

   I have to admit that I find the code that was there before pretty much
   impenetrable.  That goes for the 16/32-bit stub stuff as well.  I
   understand what the point is (16-bit code and 32-bit code needs stubs
   when calling each other), but I don't understand what the code is
   doing.

The mips16 can operate in either 16-bit mode or 32-bit mode.  This is
signalled by the low-order bit of the PC; when it is 1, the chip is in
16-bit mode.  The low-order bit is forced to zero when fetching
instructions.

Thus the mips16 can switch between modes any time it does a jump to
register (jr, jalr).  When doing an absolute jump (j, jal), the low
order bit is not represented, and accordingly the mode does not
change.  A new 32-bit instruction, jalx, does a jump and also changes
the mode.  There is a similar jalx 16-bit instruction.

Define a 16-bit symbol as a text symbol at the start of a sequence of
16-bit instructions.  A 16-bit symbol is marked in an object file
using the st_other field; a value of STO_MIPS16 indicates a 16-bit
symbol.

A R_MIPS_26 relocation implies a 32-bit instruction.  When such a
relocation refers to a 16 bit symbol, the opcode is required to be
jal.  The linker automatically converts it to jalx.

Similarly, a R_MIPS16_26 relocation implies a 16-bit instruction.
When such a relocation refers to a 32-bit symbol, the opcode is
required to be jal.  The linker automatically converts it to jalx.


This is complicated by floating point parameters and return values.
On the mips16, 16-bit code can not refer to the floating point
registers.  Therefore, when calling between 32-bit code and 16-bit
code and passing or returning floating point values, stub code is
needed to adjust the values.  This stub code is automatically
generated by the compiler (see gcc/config/mips/mips.c).  The linker is
responsible for using the stub code when it is needed, and discarding
it when it is not.

I quote elf32-mips.c:

  /* The mips16 compiler uses a couple of special sections to handle
     floating point arguments.

     Section names that look like .mips16.fn.FNNAME contain stubs that
     copy floating point arguments from the fp regs to the gp regs and
     then jump to FNNAME.  If any 32 bit function calls FNNAME, the
     call should be redirected to the stub instead.  If no 32 bit
     function calls FNNAME, the stub should be discarded.  We need to
     consider any reference to the function, not just a call, because
     if the address of the function is taken we will need the stub,
     since the address might be passed to a 32 bit function.

     Section names that look like .mips16.call.FNNAME contain stubs
     that copy floating point arguments from the gp regs to the fp
     regs and then jump to FNNAME.  If FNNAME is a 32 bit function,
     then any 16 bit function that calls FNNAME should be redirected
     to the stub instead.  If FNNAME is not a 32 bit function, the
     stub should be discarded.

     .mips16.call.fp.FNNAME sections are similar, but contain stubs
     which call FNNAME and then copy the return value from the fp regs
     to the gp regs.  These stubs store the return value in $18 while
     calling FNNAME; any function which might call one of these stubs
     must arrange to save $18 around the call.  (This case is not
     needed for 32 bit functions that call 16 bit functions, because
     16 bit functions always return floating point values in both
     $f0/$f1 and $2/$3.)

     Note that in all cases FNNAME might be defined statically.
     Therefore, FNNAME is not used literally.  Instead, the relocation
     information will indicate which symbol the section is for.

     We record any stubs that we find in the symbol table.  */

You can see this in action in various places.  In the relocate_section
function, the code needs to check for calls to a function which has a
stub, and to convert the code to call the stub when switching between
16-bit and 32-bit mode.  This is a matter of changing the target of
the relocation, as though it referred to the stub rather than to the
symbol to which it actually refers.  The version of relocate_section
before your changes makes the right checks.

Ian

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]