Files
clang-p2996/lld/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp
Simon Atanasyan 073c2d2974 [Mips] GOT16 relocation against non-local symbol does not require a
paired LO16 relocation.

llvm-svn: 198790
2014-01-08 20:43:03 +00:00

201 lines
6.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;
}
/// \brief R_MIPS_32
/// local/external: word32 S + A (truncate)
void reloc32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
uint32_t result = (uint32_t)(S + A);
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);
result = lld::scatterBits<uint32_t>(result >> 16, 0xffff);
applyReloc(location, result);
}
/// \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;
result = lld::scatterBits<uint32_t>(result, 0xffff);
applyReloc(location, result);
}
/// \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);
int32_t result = lld::scatterBits<uint32_t>(G, 0xffff);
applyReloc(location, result);
}
/// \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);
int32_t result = lld::scatterBits<uint32_t>(G, 0xffff);
applyReloc(location, result);
}
} // 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_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 LLD_R_MIPS_GLOBAL_GOT:
// Do nothing.
break;
case LLD_R_MIPS_GLOBAL_GOT16:
relocGOT16(location, relocVAddress, targetVAddress, ref.addend(),
_targetHandler.getGPDispSymAddr());
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();
}