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