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