blob: 17cbe612fb43eb0a4affccc51c0cee7ea34418be [file] [log] [blame]
//===-- RuntimeDyldELFMips.cpp ---- ELF/Mips specific code. -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "RuntimeDyldELFMips.h"
#include "llvm/BinaryFormat/ELF.h"
#define DEBUG_TYPE "dyld"
void RuntimeDyldELFMips::resolveRelocation(const RelocationEntry &RE,
uint64_t Value) {
const SectionEntry &Section = Sections[RE.SectionID];
if (IsMipsO32ABI)
resolveMIPSO32Relocation(Section, RE.Offset, Value, RE.RelType, RE.Addend);
else if (IsMipsN32ABI) {
resolveMIPSN32Relocation(Section, RE.Offset, Value, RE.RelType, RE.Addend,
RE.SymOffset, RE.SectionID);
} else if (IsMipsN64ABI)
resolveMIPSN64Relocation(Section, RE.Offset, Value, RE.RelType, RE.Addend,
RE.SymOffset, RE.SectionID);
else
llvm_unreachable("Mips ABI not handled");
}
uint64_t RuntimeDyldELFMips::evaluateRelocation(const RelocationEntry &RE,
uint64_t Value,
uint64_t Addend) {
if (IsMipsN32ABI) {
const SectionEntry &Section = Sections[RE.SectionID];
Value = evaluateMIPS64Relocation(Section, RE.Offset, Value, RE.RelType,
Addend, RE.SymOffset, RE.SectionID);
return Value;
}
llvm_unreachable("Not reachable");
}
void RuntimeDyldELFMips::applyRelocation(const RelocationEntry &RE,
uint64_t Value) {
if (IsMipsN32ABI) {
const SectionEntry &Section = Sections[RE.SectionID];
applyMIPSRelocation(Section.getAddressWithOffset(RE.Offset), Value,
RE.RelType);
return;
}
llvm_unreachable("Not reachable");
}
int64_t
RuntimeDyldELFMips::evaluateMIPS32Relocation(const SectionEntry &Section,
uint64_t Offset, uint64_t Value,
uint32_t Type) {
LLVM_DEBUG(dbgs() << "evaluateMIPS32Relocation, LocalAddress: 0x"
<< format("%llx", Section.getAddressWithOffset(Offset))
<< " FinalAddress: 0x"
<< format("%llx", Section.getLoadAddressWithOffset(Offset))
<< " Value: 0x" << format("%llx", Value) << " Type: 0x"
<< format("%x", Type) << "\n");
switch (Type) {
default:
llvm_unreachable("Unknown relocation type!");
return Value;
case ELF::R_MIPS_32:
return Value;
case ELF::R_MIPS_26:
return Value >> 2;
case ELF::R_MIPS_HI16:
// Get the higher 16-bits. Also add 1 if bit 15 is 1.
return (Value + 0x8000) >> 16;
case ELF::R_MIPS_LO16:
return Value;
case ELF::R_MIPS_PC32: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return Value - FinalAddress;
}
case ELF::R_MIPS_PC16: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return (Value - FinalAddress) >> 2;
}
case ELF::R_MIPS_PC19_S2: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return (Value - (FinalAddress & ~0x3)) >> 2;
}
case ELF::R_MIPS_PC21_S2: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return (Value - FinalAddress) >> 2;
}
case ELF::R_MIPS_PC26_S2: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return (Value - FinalAddress) >> 2;
}
case ELF::R_MIPS_PCHI16: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return (Value - FinalAddress + 0x8000) >> 16;
}
case ELF::R_MIPS_PCLO16: {
uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return Value - FinalAddress;
}
}
}
int64_t RuntimeDyldELFMips::evaluateMIPS64Relocation(
const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type,
int64_t Addend, uint64_t SymOffset, SID SectionID) {
LLVM_DEBUG(dbgs() << "evaluateMIPS64Relocation, LocalAddress: 0x"
<< format("%llx", Section.getAddressWithOffset(Offset))
<< " FinalAddress: 0x"
<< format("%llx", Section.getLoadAddressWithOffset(Offset))
<< " Value: 0x" << format("%llx", Value) << " Type: 0x"
<< format("%x", Type) << " Addend: 0x"
<< format("%llx", Addend)
<< " Offset: " << format("%llx" PRIx64, Offset)
<< " SID: " << format("%d", SectionID)
<< " SymOffset: " << format("%x", SymOffset) << "\n");
switch (Type) {
default:
llvm_unreachable("Not implemented relocation type!");
break;
case ELF::R_MIPS_JALR:
case ELF::R_MIPS_NONE:
break;
case ELF::R_MIPS_32:
case ELF::R_MIPS_64:
return Value + Addend;
case ELF::R_MIPS_26:
return ((Value + Addend) >> 2) & 0x3ffffff;
case ELF::R_MIPS_GPREL16: {
uint64_t GOTAddr = getSectionLoadAddress(SectionToGOTMap[SectionID]);
return Value + Addend - (GOTAddr + 0x7ff0);
}
case ELF::R_MIPS_SUB:
return Value - Addend;
case ELF::R_MIPS_HI16:
// Get the higher 16-bits. Also add 1 if bit 15 is 1.
return ((Value + Addend + 0x8000) >> 16) & 0xffff;
case ELF::R_MIPS_LO16:
return (Value + Addend) & 0xffff;
case ELF::R_MIPS_HIGHER:
return ((Value + Addend + 0x80008000) >> 32) & 0xffff;
case ELF::R_MIPS_HIGHEST:
return ((Value + Addend + 0x800080008000) >> 48) & 0xffff;
case ELF::R_MIPS_CALL16:
case ELF::R_MIPS_GOT_DISP:
case ELF::R_MIPS_GOT_PAGE: {
uint8_t *LocalGOTAddr =
getSectionAddress(SectionToGOTMap[SectionID]) + SymOffset;
uint64_t GOTEntry = readBytesUnaligned(LocalGOTAddr, getGOTEntrySize());
Value += Addend;
if (Type == ELF::R_MIPS_GOT_PAGE)
Value = (Value + 0x8000) & ~0xffff;
if (GOTEntry)
assert(GOTEntry == Value &&
"GOT entry has two different addresses.");
else
writeBytesUnaligned(Value, LocalGOTAddr, getGOTEntrySize());
return (SymOffset - 0x7ff0) & 0xffff;
}
case ELF::R_MIPS_GOT_OFST: {
int64_t page = (Value + Addend + 0x8000) & ~0xffff;
return (Value + Addend - page) & 0xffff;
}
case ELF::R_MIPS_GPREL32: {
uint64_t GOTAddr = getSectionLoadAddress(SectionToGOTMap[SectionID]);
return Value + Addend - (GOTAddr + 0x7ff0);
}
case ELF::R_MIPS_PC16: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return ((Value + Addend - FinalAddress) >> 2) & 0xffff;
}
case ELF::R_MIPS_PC32: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return Value + Addend - FinalAddress;
}
case ELF::R_MIPS_PC18_S3: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return ((Value + Addend - (FinalAddress & ~0x7)) >> 3) & 0x3ffff;
}
case ELF::R_MIPS_PC19_S2: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return ((Value + Addend - (FinalAddress & ~0x3)) >> 2) & 0x7ffff;
}
case ELF::R_MIPS_PC21_S2: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return ((Value + Addend - FinalAddress) >> 2) & 0x1fffff;
}
case ELF::R_MIPS_PC26_S2: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return ((Value + Addend - FinalAddress) >> 2) & 0x3ffffff;
}
case ELF::R_MIPS_PCHI16: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return ((Value + Addend - FinalAddress + 0x8000) >> 16) & 0xffff;
}
case ELF::R_MIPS_PCLO16: {
uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
return (Value + Addend - FinalAddress) & 0xffff;
}
}
return 0;
}
void RuntimeDyldELFMips::applyMIPSRelocation(uint8_t *TargetPtr, int64_t Value,
uint32_t Type) {
uint32_t Insn = readBytesUnaligned(TargetPtr, 4);
switch (Type) {
default:
llvm_unreachable("Unknown relocation type!");
break;
case ELF::R_MIPS_GPREL16:
case ELF::R_MIPS_HI16:
case ELF::R_MIPS_LO16:
case ELF::R_MIPS_HIGHER:
case ELF::R_MIPS_HIGHEST:
case ELF::R_MIPS_PC16:
case ELF::R_MIPS_PCHI16:
case ELF::R_MIPS_PCLO16:
case ELF::R_MIPS_CALL16:
case ELF::R_MIPS_GOT_DISP:
case ELF::R_MIPS_GOT_PAGE:
case ELF::R_MIPS_GOT_OFST:
Insn = (Insn & 0xffff0000) | (Value & 0x0000ffff);
writeBytesUnaligned(Insn, TargetPtr, 4);
break;
case ELF::R_MIPS_PC18_S3:
Insn = (Insn & 0xfffc0000) | (Value & 0x0003ffff);
writeBytesUnaligned(Insn, TargetPtr, 4);
break;
case ELF::R_MIPS_PC19_S2:
Insn = (Insn & 0xfff80000) | (Value & 0x0007ffff);
writeBytesUnaligned(Insn, TargetPtr, 4);
break;
case ELF::R_MIPS_PC21_S2:
Insn = (Insn & 0xffe00000) | (Value & 0x001fffff);
writeBytesUnaligned(Insn, TargetPtr, 4);
break;
case ELF::R_MIPS_26:
case ELF::R_MIPS_PC26_S2:
Insn = (Insn & 0xfc000000) | (Value & 0x03ffffff);
writeBytesUnaligned(Insn, TargetPtr, 4);
break;
case ELF::R_MIPS_32:
case ELF::R_MIPS_GPREL32:
case ELF::R_MIPS_PC32:
writeBytesUnaligned(Value & 0xffffffff, TargetPtr, 4);
break;
case ELF::R_MIPS_64:
case ELF::R_MIPS_SUB:
writeBytesUnaligned(Value, TargetPtr, 8);
break;
}
}
void RuntimeDyldELFMips::resolveMIPSN32Relocation(
const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type,
int64_t Addend, uint64_t SymOffset, SID SectionID) {
int64_t CalculatedValue = evaluateMIPS64Relocation(
Section, Offset, Value, Type, Addend, SymOffset, SectionID);
applyMIPSRelocation(Section.getAddressWithOffset(Offset), CalculatedValue,
Type);
}
void RuntimeDyldELFMips::resolveMIPSN64Relocation(
const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type,
int64_t Addend, uint64_t SymOffset, SID SectionID) {
uint32_t r_type = Type & 0xff;
uint32_t r_type2 = (Type >> 8) & 0xff;
uint32_t r_type3 = (Type >> 16) & 0xff;
// RelType is used to keep information for which relocation type we are
// applying relocation.
uint32_t RelType = r_type;
int64_t CalculatedValue = evaluateMIPS64Relocation(Section, Offset, Value,
RelType, Addend,
SymOffset, SectionID);
if (r_type2 != ELF::R_MIPS_NONE) {
RelType = r_type2;
CalculatedValue = evaluateMIPS64Relocation(Section, Offset, 0, RelType,
CalculatedValue, SymOffset,
SectionID);
}
if (r_type3 != ELF::R_MIPS_NONE) {
RelType = r_type3;
CalculatedValue = evaluateMIPS64Relocation(Section, Offset, 0, RelType,
CalculatedValue, SymOffset,
SectionID);
}
applyMIPSRelocation(Section.getAddressWithOffset(Offset), CalculatedValue,
RelType);
}
void RuntimeDyldELFMips::resolveMIPSO32Relocation(const SectionEntry &Section,
uint64_t Offset,
uint32_t Value, uint32_t Type,
int32_t Addend) {
uint8_t *TargetPtr = Section.getAddressWithOffset(Offset);
Value += Addend;
LLVM_DEBUG(dbgs() << "resolveMIPSO32Relocation, LocalAddress: "
<< Section.getAddressWithOffset(Offset) << " FinalAddress: "
<< format("%p", Section.getLoadAddressWithOffset(Offset))
<< " Value: " << format("%x", Value) << " Type: "
<< format("%x", Type) << " Addend: " << format("%x", Addend)
<< " SymOffset: " << format("%x", Offset) << "\n");
Value = evaluateMIPS32Relocation(Section, Offset, Value, Type);
applyMIPSRelocation(TargetPtr, Value, Type);
}