237 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp ----------------===//
 | ||
| //
 | ||
| //                             The LLVM Linker
 | ||
| //
 | ||
| // This file is distributed under the University of Illinois Open Source
 | ||
| // License. See LICENSE.TXT for details.
 | ||
| //
 | ||
| //===----------------------------------------------------------------------===//
 | ||
| 
 | ||
| #include "MipsTargetHandler.h"
 | ||
| #include "MipsLinkingContext.h"
 | ||
| #include "MipsRelocationHandler.h"
 | ||
| 
 | ||
| #include "lld/ReaderWriter/RelocationHelperFunctions.h"
 | ||
| 
 | ||
| using namespace lld;
 | ||
| using namespace elf;
 | ||
| using namespace llvm::ELF;
 | ||
| 
 | ||
| namespace {
 | ||
| 
 | ||
| inline void applyReloc(uint8_t *location, uint32_t result) {
 | ||
|   auto target = reinterpret_cast<llvm::support::ulittle32_t *>(location);
 | ||
|   *target = result | uint32_t(*target);
 | ||
| }
 | ||
| 
 | ||
| /// \brief Calculate AHL value combines addends from 'hi' and 'lo' relocations.
 | ||
| inline int64_t calcAHL(int64_t AHI, int64_t ALO) {
 | ||
|   AHI &= 0xffff;
 | ||
|   ALO &= 0xffff;
 | ||
|   return (AHI << 16) + (int16_t)ALO;
 | ||
| }
 | ||
| 
 | ||
| template <size_t BITS, class T> inline T signExtend(T val) {
 | ||
|   if (val & (T(1) << (BITS - 1)))
 | ||
|     val |= T(-1) << BITS;
 | ||
|   return val;
 | ||
| }
 | ||
| 
 | ||
| /// \brief R_MIPS_32
 | ||
| /// local/external: word32 S + A (truncate)
 | ||
| void reloc32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
 | ||
|   applyReloc(location, (S + A) & 0xffffffff);
 | ||
| }
 | ||
| 
 | ||
| /// \brief R_MIPS_26
 | ||
| /// local   : ((A | ((P + 4) & 0x3F000000)) + S) >> 2
 | ||
| /// external: (sign–extend(A) + S) >> 2
 | ||
| void reloc26(uint8_t *location, uint64_t P, uint64_t S, bool isLocal) {
 | ||
|   int32_t A = (*(uint32_t*)location & 0x03FFFFFFU) << 2;
 | ||
|   uint32_t result = isLocal ? (A | ((P + 4) & 0x3F000000)) : signExtend<28>(A);
 | ||
|   result = (result + S) >> 2;
 | ||
|   applyReloc(location, result);
 | ||
| }
 | ||
| 
 | ||
| /// \brief R_MIPS_HI16
 | ||
| /// local/external: hi16 (AHL + S) - (short)(AHL + S) (truncate)
 | ||
| /// _gp_disp      : hi16 (AHL + GP - P) - (short)(AHL + GP - P) (verify)
 | ||
| void relocHi16(uint8_t *location, uint64_t P, uint64_t S, int64_t AHL,
 | ||
|                uint64_t GP, bool isGPDisp) {
 | ||
|   int32_t result = 0;
 | ||
| 
 | ||
|   if (isGPDisp)
 | ||
|     result = (AHL + GP - P) - (int16_t)(AHL + GP - P);
 | ||
|   else
 | ||
|     result = (AHL + S) - (int16_t)(AHL + S);
 | ||
| 
 | ||
|   applyReloc(location, (result >> 16) & 0xffff);
 | ||
| }
 | ||
| 
 | ||
| /// \brief R_MIPS_LO16
 | ||
| /// local/external: lo16 AHL + S (truncate)
 | ||
| /// _gp_disp      : lo16 AHL + GP - P + 4 (verify)
 | ||
| void relocLo16(uint8_t *location, uint64_t P, uint64_t S, int64_t AHL,
 | ||
|                uint64_t GP, bool isGPDisp) {
 | ||
|   int32_t result = 0;
 | ||
| 
 | ||
|   if (isGPDisp)
 | ||
|     result = AHL + GP - P + 4;
 | ||
|   else
 | ||
|     result = AHL + S;
 | ||
| 
 | ||
|   applyReloc(location, result & 0xffff);
 | ||
| }
 | ||
| 
 | ||
| /// \brief R_MIPS_GOT16
 | ||
| /// local/external: rel16 G (verify)
 | ||
| void relocGOT16(uint8_t *location, uint64_t P, uint64_t S, int64_t AHL,
 | ||
|                 uint64_t GP) {
 | ||
|   // FIXME (simon): for local sym put high 16 bit of AHL to the GOT
 | ||
|   int32_t G = (int32_t)(S - GP);
 | ||
|   applyReloc(location, G & 0xffff);
 | ||
| }
 | ||
| 
 | ||
| /// \brief R_MIPS_CALL16
 | ||
| /// external: rel16 G (verify)
 | ||
| void relocCall16(uint8_t *location, uint64_t P, uint64_t S, int64_t A,
 | ||
|                  uint64_t GP) {
 | ||
|   int32_t G = (int32_t)(S - GP);
 | ||
|   applyReloc(location, G & 0xffff);
 | ||
| }
 | ||
| 
 | ||
| /// \brief LLD_R_MIPS_HI16
 | ||
| void relocLldHi16(uint8_t *location, uint64_t S) {
 | ||
|   applyReloc(location, ((S + 0x8000) >> 16) & 0xffff);
 | ||
| }
 | ||
| 
 | ||
| /// \brief LLD_R_MIPS_LO16
 | ||
| void relocLldLo16(uint8_t *location, uint64_t S) {
 | ||
|   applyReloc(location, S & 0xffff);
 | ||
| }
 | ||
| 
 | ||
| } // end anon namespace
 | ||
| 
 | ||
| MipsTargetRelocationHandler::MipsTargetRelocationHandler(
 | ||
|     const MipsLinkingContext &context, const MipsTargetHandler &handler)
 | ||
|     : _targetHandler(handler) {}
 | ||
| 
 | ||
| MipsTargetRelocationHandler::~MipsTargetRelocationHandler() {
 | ||
|   assert(_pairedRelocations.empty());
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| MipsTargetRelocationHandler::savePairedRelocation(const lld::AtomLayout &atom,
 | ||
|                                                   const Reference &ref) const {
 | ||
|   auto pi = _pairedRelocations.find(&atom);
 | ||
|   if (pi == _pairedRelocations.end())
 | ||
|     pi = _pairedRelocations.emplace(&atom, PairedRelocationsT()).first;
 | ||
| 
 | ||
|   pi->second.push_back(&ref);
 | ||
| }
 | ||
| 
 | ||
| void MipsTargetRelocationHandler::applyPairedRelocations(
 | ||
|     ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom,
 | ||
|     int64_t loAddend) const {
 | ||
|   auto pi = _pairedRelocations.find(&atom);
 | ||
|   if (pi == _pairedRelocations.end())
 | ||
|     return;
 | ||
| 
 | ||
|   for (auto ri : pi->second) {
 | ||
|     uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset;
 | ||
|     uint8_t *location = atomContent + ri->offsetInAtom();
 | ||
|     uint64_t targetVAddress = writer.addressOfAtom(ri->target());
 | ||
|     uint64_t relocVAddress = atom._virtualAddr + ri->offsetInAtom();
 | ||
| 
 | ||
|     int64_t ahl = calcAHL(ri->addend(), loAddend);
 | ||
| 
 | ||
|     if (ri->kindNamespace() != lld::Reference::KindNamespace::ELF)
 | ||
|       continue;
 | ||
|     assert(ri->kindArch() == Reference::KindArch::Mips);
 | ||
|     switch (ri->kindValue()) {
 | ||
|     case R_MIPS_HI16:
 | ||
|       relocHi16(location, relocVAddress, targetVAddress, ahl,
 | ||
|                 _targetHandler.getGPDispSymAddr(),
 | ||
|                 ri->target()->name() == "_gp_disp");
 | ||
|       break;
 | ||
|     case R_MIPS_GOT16:
 | ||
|       relocGOT16(location, relocVAddress, targetVAddress, ahl,
 | ||
|                  _targetHandler.getGPDispSymAddr());
 | ||
|       break;
 | ||
|     default:
 | ||
|       llvm_unreachable("Unknown type of paired relocation.");
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   _pairedRelocations.erase(pi);
 | ||
| }
 | ||
| 
 | ||
| error_code MipsTargetRelocationHandler::applyRelocation(
 | ||
|     ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom,
 | ||
|     const Reference &ref) const {
 | ||
|   uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset;
 | ||
|   uint8_t *location = atomContent + ref.offsetInAtom();
 | ||
|   uint64_t targetVAddress = writer.addressOfAtom(ref.target());
 | ||
|   uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom();
 | ||
| 
 | ||
|   if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF)
 | ||
|     return error_code::success();
 | ||
|   assert(ref.kindArch() == Reference::KindArch::Mips);
 | ||
|   switch (ref.kindValue()) {
 | ||
|   case R_MIPS_NONE:
 | ||
|     break;
 | ||
|   case R_MIPS_32:
 | ||
|     reloc32(location, relocVAddress, targetVAddress, ref.addend());
 | ||
|     break;
 | ||
|   case R_MIPS_26:
 | ||
|     reloc26(location, relocVAddress, targetVAddress, true);
 | ||
|     break;
 | ||
|   case R_MIPS_HI16:
 | ||
|     savePairedRelocation(atom, ref);
 | ||
|     break;
 | ||
|   case R_MIPS_LO16:
 | ||
|     relocLo16(location, relocVAddress, targetVAddress, calcAHL(0, ref.addend()),
 | ||
|               _targetHandler.getGPDispSymAddr(),
 | ||
|               ref.target()->name() == "_gp_disp");
 | ||
|     applyPairedRelocations(writer, buf, atom, ref.addend());
 | ||
|     break;
 | ||
|   case R_MIPS_GOT16:
 | ||
|     savePairedRelocation(atom, ref);
 | ||
|     break;
 | ||
|   case R_MIPS_CALL16:
 | ||
|     relocCall16(location, relocVAddress, targetVAddress, ref.addend(),
 | ||
|                 _targetHandler.getGPDispSymAddr());
 | ||
|     break;
 | ||
|   case R_MIPS_JALR:
 | ||
|     // We do not do JALR optimization now.
 | ||
|     break;
 | ||
|   case R_MIPS_JUMP_SLOT:
 | ||
|     // Ignore runtime relocations.
 | ||
|     break;
 | ||
|   case LLD_R_MIPS_GLOBAL_GOT:
 | ||
|     // Do nothing.
 | ||
|     break;
 | ||
|   case LLD_R_MIPS_GLOBAL_GOT16:
 | ||
|     relocGOT16(location, relocVAddress, targetVAddress, ref.addend(),
 | ||
|                _targetHandler.getGPDispSymAddr());
 | ||
|     break;
 | ||
|   case LLD_R_MIPS_GLOBAL_26:
 | ||
|     reloc26(location, relocVAddress, targetVAddress, false);
 | ||
|     break;
 | ||
|   case LLD_R_MIPS_HI16:
 | ||
|     relocLldHi16(location, targetVAddress);
 | ||
|     break;
 | ||
|   case LLD_R_MIPS_LO16:
 | ||
|     relocLldLo16(location, targetVAddress);
 | ||
|     break;
 | ||
|   default: {
 | ||
|     std::string str;
 | ||
|     llvm::raw_string_ostream s(str);
 | ||
|     s << "Unhandled Mips relocation: " << ref.kindValue();
 | ||
|     llvm_unreachable(s.str().c_str());
 | ||
|   }
 | ||
|   }
 | ||
| 
 | ||
|   return error_code::success();
 | ||
| }
 |