Home | History | Annotate | Download | only in Mips
      1 //===- MipsRelocationFactory.cpp  -----------------------------------------===//
      2 //
      3 //                     The MCLinker Project
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include <llvm/ADT/Twine.h>
     11 #include <llvm/Support/ELF.h>
     12 #include <mcld/LD/Layout.h>
     13 #include <mcld/Target/OutputRelocSection.h>
     14 #include <mcld/Support/MsgHandling.h>
     15 
     16 #include "MipsRelocationFactory.h"
     17 #include "MipsRelocationFunctions.h"
     18 
     19 using namespace mcld;
     20 
     21 //===----------------------------------------------------------------------===//
     22 // Relocation Functions and Tables
     23 //===----------------------------------------------------------------------===//
     24 DECL_MIPS_APPLY_RELOC_FUNCS
     25 
     26 /// the prototype of applying function
     27 typedef RelocationFactory::Result (*ApplyFunctionType)(Relocation&,
     28                                                        const MCLDInfo& pLDInfo,
     29                                                        MipsRelocationFactory&);
     30 
     31 // the table entry of applying functions
     32 struct ApplyFunctionTriple
     33 {
     34   ApplyFunctionType func;
     35   unsigned int type;
     36   const char* name;
     37 };
     38 
     39 // declare the table of applying functions
     40 static const ApplyFunctionTriple ApplyFunctions[] = {
     41   DECL_MIPS_APPLY_RELOC_FUNC_PTRS
     42 };
     43 
     44 //===----------------------------------------------------------------------===//
     45 // MipsRelocationFactory
     46 //===----------------------------------------------------------------------===//
     47 MipsRelocationFactory::MipsRelocationFactory(size_t pNum,
     48                                              MipsGNULDBackend& pParent)
     49   : RelocationFactory(pNum),
     50     m_Target(pParent),
     51     m_AHL(0)
     52 {
     53 }
     54 
     55 RelocationFactory::Result
     56 MipsRelocationFactory::applyRelocation(Relocation& pRelocation,
     57                                        const MCLDInfo& pLDInfo)
     58 
     59 {
     60   Relocation::Type type = pRelocation.type();
     61 
     62   if (type >= sizeof(ApplyFunctions) / sizeof(ApplyFunctions[0])) {
     63     fatal(diag::unknown_relocation) << (int)type
     64                                     << pRelocation.symInfo()->name();
     65     return Unknown;
     66   }
     67 
     68   // apply the relocation
     69   return ApplyFunctions[type].func(pRelocation, pLDInfo, *this);
     70 }
     71 
     72 const char* MipsRelocationFactory::getName(Relocation::Type pType) const
     73 {
     74   return ApplyFunctions[pType].name;
     75 }
     76 
     77 //===----------------------------------------------------------------------===//
     78 // Relocation helper function
     79 
     80 //===----------------------------------------------------------------------===//
     81 static const char * const GP_DISP_NAME = "_gp_disp";
     82 
     83 // Find next R_MIPS_LO16 relocation paired to pReloc.
     84 static
     85 Relocation* helper_FindLo16Reloc(Relocation& pReloc)
     86 {
     87   Relocation* reloc = static_cast<Relocation*>(pReloc.getNextNode());
     88   while (NULL != reloc)
     89   {
     90     if (llvm::ELF::R_MIPS_LO16 == reloc->type() &&
     91         reloc->symInfo() == pReloc.symInfo())
     92       return reloc;
     93 
     94     reloc = static_cast<Relocation*>(reloc->getNextNode());
     95   }
     96   return NULL;
     97 }
     98 
     99 // Check the symbol is _gp_disp.
    100 static
    101 bool helper_isGpDisp(const Relocation& pReloc)
    102 {
    103   const ResolveInfo* rsym = pReloc.symInfo();
    104   return 0 == strcmp(GP_DISP_NAME, rsym->name());
    105 }
    106 
    107 static
    108 RelocationFactory::Address helper_GetGP(MipsRelocationFactory& pParent)
    109 {
    110   return pParent.getTarget().getGOT().getSection().addr() + 0x7FF0;
    111 }
    112 
    113 static
    114 GOTEntry& helper_GetGOTEntry(Relocation& pReloc,
    115                              MipsRelocationFactory& pParent,
    116                              bool& pExist, int32_t value)
    117 {
    118   // rsym - The relocation target symbol
    119   ResolveInfo* rsym = pReloc.symInfo();
    120   MipsGNULDBackend& ld_backend = pParent.getTarget();
    121   MipsGOT& got = ld_backend.getGOT();
    122 
    123   GOTEntry& got_entry = *got.getEntry(*rsym, pExist);
    124 
    125   if (pExist)
    126     return got_entry;
    127 
    128   // If we first get this GOT entry, we should initialize it.
    129   if (!(got.isLocal(rsym) && rsym->type() == ResolveInfo::Section)) {
    130     if (rsym->reserved() & MipsGNULDBackend::ReserveGot) {
    131       got_entry.setContent(pReloc.symValue());
    132     }
    133     else {
    134       fatal(diag::reserve_entry_number_mismatch_got);
    135     }
    136   }
    137 
    138   return got_entry;
    139 }
    140 
    141 static
    142 RelocationFactory::Address helper_GetGOTOffset(Relocation& pReloc,
    143                                                MipsRelocationFactory& pParent)
    144 {
    145   bool exist;
    146   GOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent, exist, 0);
    147   return pParent.getLayout().getOutputOffset(got_entry) - 0x7FF0;
    148 }
    149 
    150 static
    151 int32_t helper_CalcAHL(const Relocation& pHiReloc, const Relocation& pLoReloc)
    152 {
    153   assert((pHiReloc.type() == llvm::ELF::R_MIPS_HI16 ||
    154           pHiReloc.type() == llvm::ELF::R_MIPS_GOT16) &&
    155          pLoReloc.type() == llvm::ELF::R_MIPS_LO16 &&
    156          "Incorrect type of relocation for AHL calculation");
    157 
    158   // Note the addend is section symbol offset here
    159   assert (pHiReloc.addend() == pLoReloc.addend());
    160 
    161   int32_t AHI = pHiReloc.target();
    162   int32_t ALO = pLoReloc.target();
    163   int32_t AHL = ((AHI & 0xFFFF) << 16) + (int16_t)(ALO & 0xFFFF) + pLoReloc.addend();
    164   return AHL;
    165 }
    166 
    167 static
    168 void helper_DynRel(Relocation& pReloc,
    169                    MipsRelocationFactory& pParent)
    170 {
    171   ResolveInfo* rsym = pReloc.symInfo();
    172   MipsGNULDBackend& ld_backend = pParent.getTarget();
    173   MipsGOT& got = ld_backend.getGOT();
    174 
    175   bool exist;
    176   Relocation& rel_entry =
    177     *ld_backend.getRelDyn().getEntry(*rsym, false, exist);
    178 
    179   rel_entry.setType(llvm::ELF::R_MIPS_REL32);
    180   rel_entry.targetRef() = pReloc.targetRef();
    181 
    182   RelocationFactory::DWord A = pReloc.target() + pReloc.addend();
    183   RelocationFactory::DWord S = pReloc.symValue();
    184 
    185   if (got.isLocal(rsym)) {
    186     rel_entry.setSymInfo(NULL);
    187     pReloc.target() = A + S;
    188   }
    189   else {
    190     rel_entry.setSymInfo(rsym);
    191     // Don't add symbol value that will be resolved by the dynamic linker
    192     pReloc.target() = A;
    193   }
    194 }
    195 
    196 //=========================================//
    197 // Relocation functions implementation     //
    198 //=========================================//
    199 
    200 // R_MIPS_NONE and those unsupported/deprecated relocation type
    201 static
    202 MipsRelocationFactory::Result none(Relocation& pReloc,
    203                                    const MCLDInfo& pLDInfo,
    204                                    MipsRelocationFactory& pParent)
    205 {
    206   return MipsRelocationFactory::OK;
    207 }
    208 
    209 // R_MIPS_32: S + A
    210 static
    211 MipsRelocationFactory::Result abs32(Relocation& pReloc,
    212                                     const MCLDInfo& pLDInfo,
    213                                     MipsRelocationFactory& pParent)
    214 {
    215   ResolveInfo* rsym = pReloc.symInfo();
    216 
    217   RelocationFactory::DWord A = pReloc.target() + pReloc.addend();
    218   RelocationFactory::DWord S = pReloc.symValue();
    219 
    220   const LDSection* target_sect = pParent.getLayout().getOutputLDSection(
    221                                                   *(pReloc.targetRef().frag()));
    222   assert(NULL != target_sect);
    223   // If the flag of target section is not ALLOC, we will not scan this relocation
    224   // but perform static relocation. (e.g., applying .debug section)
    225   if (0x0 == (llvm::ELF::SHF_ALLOC & target_sect->flag())) {
    226     pReloc.target() = S + A;
    227     return MipsRelocationFactory::OK;
    228   }
    229 
    230   if (rsym->reserved() & MipsGNULDBackend::ReserveRel) {
    231     helper_DynRel(pReloc, pParent);
    232 
    233     return MipsRelocationFactory::OK;
    234   }
    235 
    236   pReloc.target() = (S + A);
    237 
    238   return MipsRelocationFactory::OK;
    239 }
    240 
    241 // R_MIPS_HI16:
    242 //   local/external: ((AHL + S) - (short)(AHL + S)) >> 16
    243 //   _gp_disp      : ((AHL + GP - P) - (short)(AHL + GP - P)) >> 16
    244 static
    245 MipsRelocationFactory::Result hi16(Relocation& pReloc,
    246                                    const MCLDInfo& pLDInfo,
    247                                    MipsRelocationFactory& pParent)
    248 {
    249   Relocation* lo_reloc = helper_FindLo16Reloc(pReloc);
    250   assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_HI16");
    251 
    252   int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc);
    253   int32_t res = 0;
    254 
    255   pParent.setAHL(AHL);
    256 
    257   if (helper_isGpDisp(pReloc)) {
    258     int32_t P = pReloc.place(pParent.getLayout());
    259     int32_t GP = helper_GetGP(pParent);
    260     res = ((AHL + GP - P) - (int16_t)(AHL + GP - P)) >> 16;
    261   }
    262   else {
    263     int32_t S = pReloc.symValue();
    264     res = ((AHL + S) - (int16_t)(AHL + S)) >> 16;
    265   }
    266 
    267   pReloc.target() &= 0xFFFF0000;
    268   pReloc.target() |= (res & 0xFFFF);
    269 
    270   return MipsRelocationFactory::OK;
    271 }
    272 
    273 // R_MIPS_LO16:
    274 //   local/external: AHL + S
    275 //   _gp_disp      : AHL + GP - P + 4
    276 static
    277 MipsRelocationFactory::Result lo16(Relocation& pReloc,
    278                                    const MCLDInfo& pLDInfo,
    279                                    MipsRelocationFactory& pParent)
    280 {
    281   int32_t res = 0;
    282 
    283   if (helper_isGpDisp(pReloc)) {
    284     int32_t P = pReloc.place(pParent.getLayout());
    285     int32_t GP = helper_GetGP(pParent);
    286     int32_t AHL = pParent.getAHL();
    287     res = AHL + GP - P + 4;
    288   }
    289   else {
    290     int32_t S = pReloc.symValue();
    291     // The previous AHL may be for other hi/lo pairs.
    292     // We need to calcuate the lo part now.  It is easy.
    293     // Remember to add the section offset to ALO.
    294     int32_t ALO = (pReloc.target() & 0xFFFF) + pReloc.addend();
    295     res = ALO + S;
    296   }
    297 
    298   pReloc.target() &= 0xFFFF0000;
    299   pReloc.target() |= (res & 0xFFFF);
    300 
    301   return MipsRelocationFactory::OK;
    302 }
    303 
    304 // R_MIPS_GOT16:
    305 //   local   : G (calculate AHL and put high 16 bit to GOT)
    306 //   external: G
    307 static
    308 MipsRelocationFactory::Result got16(Relocation& pReloc,
    309                                     const MCLDInfo& pLDInfo,
    310                                     MipsRelocationFactory& pParent)
    311 {
    312   ResolveInfo* rsym = pReloc.symInfo();
    313   RelocationFactory::Address G = 0;
    314 
    315   if (rsym->isLocal()) {
    316     Relocation* lo_reloc = helper_FindLo16Reloc(pReloc);
    317     assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_GOT16");
    318 
    319     int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc);
    320     int32_t S = pReloc.symValue();
    321 
    322     pParent.setAHL(AHL);
    323 
    324     int32_t res = (AHL + S + 0x8000) & 0xFFFF0000;
    325     bool exist;
    326     GOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent, exist, res);
    327 
    328     got_entry.setContent(res);
    329     G = pParent.getLayout().getOutputOffset(got_entry) - 0x7FF0;
    330   }
    331   else {
    332     G = helper_GetGOTOffset(pReloc, pParent);
    333   }
    334 
    335   pReloc.target() &= 0xFFFF0000;
    336   pReloc.target() |= (G & 0xFFFF);
    337 
    338   return MipsRelocationFactory::OK;
    339 }
    340 
    341 // R_MIPS_CALL16: G
    342 static
    343 MipsRelocationFactory::Result call16(Relocation& pReloc,
    344                                      const MCLDInfo& pLDInfo,
    345                                      MipsRelocationFactory& pParent)
    346 {
    347   RelocationFactory::Address G = helper_GetGOTOffset(pReloc, pParent);
    348 
    349   pReloc.target() &= 0xFFFF0000;
    350   pReloc.target() |= (G & 0xFFFF);
    351 
    352   return MipsRelocationFactory::OK;
    353 }
    354 
    355 // R_MIPS_GPREL32: A + S + GP0 - GP
    356 static
    357 MipsRelocationFactory::Result gprel32(Relocation& pReloc,
    358                                       const MCLDInfo& pLDInfo,
    359                                       MipsRelocationFactory& pParent)
    360 {
    361   // Remember to add the section offset to A.
    362   int32_t A = pReloc.target() + pReloc.addend();
    363   int32_t S = pReloc.symValue();
    364   int32_t GP = helper_GetGP(pParent);
    365 
    366   // llvm does not emits SHT_MIPS_REGINFO section.
    367   // Assume that GP0 is zero.
    368   pReloc.target() = (A + S - GP) & 0xFFFFFFFF;
    369 
    370   return MipsRelocationFactory::OK;
    371 }
    372