From e53bd91b0fd8f8e7c0b0f32d2080c60873b51404 Mon Sep 17 00:00:00 2001 From: Mark Mitchell Date: Tue, 13 Jul 1999 23:55:08 +0000 Subject: * elf32-mips.c (mips_elf_calculate_relocation): Handle R_MIPS16_26. (mips_elf_relocate_section): Adjust calling sequence for mips_elf_perform_relocation. (mips_elf_perform_relocation): Take additional argument. Handle R_MIPS16_26. Use bfd_put for convenience. --- bfd/elf32-mips.c | 135 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 35 deletions(-) (limited to 'bfd/elf32-mips.c') diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index 5f04726..281f485 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -170,7 +170,8 @@ static bfd_reloc_status_type mips_elf_calculate_relocation static bfd_vma mips_elf_obtain_contents PARAMS ((reloc_howto_type *, const Elf_Internal_Rela *, bfd *, bfd_byte *)); static void mips_elf_perform_relocation - PARAMS ((reloc_howto_type *, const Elf_Internal_Rela *, bfd_vma, + PARAMS ((struct bfd_link_info *, reloc_howto_type *, + const Elf_Internal_Rela *, bfd_vma, bfd *, bfd_byte *)); static boolean mips_elf_assign_gp PARAMS ((bfd *, bfd_vma *)); static boolean mips_elf_sort_hash_table_f @@ -5883,6 +5884,12 @@ mips_elf_calculate_relocation (abfd, value &= howto->dst_mask; break; + case R_MIPS16_26: + /* The calculation for R_MIPS_26 is just the same as for an + R_MIPS_26. It's only the storage of the relocated field into + the output file that's different. That's handle in + mips_elf_perform_relocation. So, we just fall through to the + R_MIPS_26 case here. */ case R_MIPS_26: if (local_p) value = (((addend << 2) | (p & 0xf0000000)) + symbol) >> 2; @@ -5922,7 +5929,7 @@ mips_elf_calculate_relocation (abfd, Here $t9 holds the address of the function being called, as required by the MIPS ELF ABI. The R_MIPS_LO16 - relocation can easily overlfow in this situation, but the + relocation can easily overflow in this situation, but the R_MIPS_HI16 relocation will handle the overflow. Therefore, we consider this a bug in the MIPS ABI, and do not check for overflow here. */ @@ -6037,7 +6044,6 @@ mips_elf_calculate_relocation (abfd, /* We don't do anything with these at present. */ return bfd_reloc_continue; - case R_MIPS16_26: case R_MIPS16_GPREL: /* These relocations, used for MIPS16, are not clearly documented anywhere. What do they do? */ @@ -6107,7 +6113,9 @@ mips_elf_obtain_contents (howto, relocation, input_bfd, contents) Returns false if anything goes wrong. */ static void -mips_elf_perform_relocation (howto, relocation, value, input_bfd, contents) +mips_elf_perform_relocation (info, howto, relocation, value, + input_bfd, contents) + struct bfd_link_info *info; reloc_howto_type *howto; const Elf_Internal_Rela *relocation; bfd_vma value; @@ -6115,7 +6123,10 @@ mips_elf_perform_relocation (howto, relocation, value, input_bfd, contents) bfd_byte *contents; { bfd_vma x; - bfd_byte *location = contents + relocation->r_offset; + bfd_byte *location; + + /* Figure out where the relocation is occurring. */ + location = contents + relocation->r_offset; /* Obtain the current value. */ x = mips_elf_obtain_contents (howto, relocation, input_bfd, contents); @@ -6123,40 +6134,94 @@ mips_elf_perform_relocation (howto, relocation, value, input_bfd, contents) /* Clear the field we are setting. */ x &= ~howto->dst_mask; - /* Set the field. */ - x |= (value & howto->dst_mask); - - /* Put the value into the output. */ - switch (bfd_get_reloc_size (howto)) - { - case 0: - x = 0; - break; - - case 1: - bfd_put_8 (input_bfd, x, location); - break; + /* If this is the R_MIPS16_26 relocation, we must store the + value in a funny way. */ + if (ELF32_R_TYPE (relocation->r_info) == R_MIPS16_26) + { + /* 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, R_MIPS16_26 is + handled mostly like R_MIPS_26. In particular, the addend is + stored 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, R_MIPS16_26 is treated 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) */ + + if (!info->relocateable) + /* Shuffle the bits according to the formula above. */ + value = (((value & 0x1f0000) << 5) + | ((value & 0x3e00000) >> 5) + | (value & 0xffff)); + + /* Perform the relocation. */ + x |= (value & howto->dst_mask); - case 2: - bfd_put_16 (input_bfd, x, location); - break; + /* Swap the high- and low-order 16 bits on little-endian + systems. */ + if (bfd_little_endian (input_bfd)) + x = (((x & 0xffff) << 16) + | (((x & 0xffff0000) >> 16) & 0xffff)); - case 4: + /* Store the value. */ bfd_put_32 (input_bfd, x, location); - break; + return; + } - case 8: -#ifdef BFD64 - bfd_put_64 (input_bfd, x, location); -#else - abort (); -#endif - break; + /* Set the field. */ + x |= (value & howto->dst_mask); - default: - abort (); - break; - } + /* Put the value into the output. */ + bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location); } /* Relocate a MIPS ELF section. */ @@ -6376,7 +6441,7 @@ _bfd_mips_elf_relocate_section (output_bfd, info, input_bfd, input_section, } /* Actually perform the relocation. */ - mips_elf_perform_relocation (howto, rel, value, input_bfd, + mips_elf_perform_relocation (info, howto, rel, value, input_bfd, contents); } -- cgit v1.1