#include "InputFiles.h"
#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/LEB128.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
namespace {
class LoongArch final : public TargetInfo {
public:
LoongArch();
uint32_t calcEFlags() const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
RelType getDynRel(RelType type) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
bool usesOnlyLowPageBits(RelType type) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
bool relaxOnce(int pass) const override;
void finalizeRelax(int passes) const override;
};
}
namespace {
enum Op {
SUB_W = 0x00110000,
SUB_D = 0x00118000,
BREAK = 0x002a0000,
SRLI_W = 0x00448000,
SRLI_D = 0x00450000,
ADDI_W = 0x02800000,
ADDI_D = 0x02c00000,
ANDI = 0x03400000,
PCADDU12I = 0x1c000000,
LD_W = 0x28800000,
LD_D = 0x28c00000,
JIRL = 0x4c000000,
};
enum Reg {
R_ZERO = 0,
R_RA = 1,
R_TP = 2,
R_T0 = 12,
R_T1 = 13,
R_T2 = 14,
R_T3 = 15,
};
}
static uint64_t getLoongArchPage(uint64_t p) {
return p & ~static_cast<uint64_t>(0xfff);
}
static uint32_t lo12(uint32_t val) { return val & 0xfff; }
uint64_t elf::getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type) {
uint64_t pcalau12i_pc;
switch (type) {
case R_LARCH_PCALA64_LO20:
case R_LARCH_GOT64_PC_LO20:
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_DESC64_PC_LO20:
pcalau12i_pc = pc - 8;
break;
case R_LARCH_PCALA64_HI12:
case R_LARCH_GOT64_PC_HI12:
case R_LARCH_TLS_IE64_PC_HI12:
case R_LARCH_TLS_DESC64_PC_HI12:
pcalau12i_pc = pc - 12;
break;
default:
pcalau12i_pc = pc;
break;
}
uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pcalau12i_pc);
if (dest & 0x800)
result += 0x1000 - 0x1'0000'0000;
if (result & 0x8000'0000)
result += 0x1'0000'0000;
return result;
}
static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
static uint32_t insn(uint32_t op, uint32_t d, uint32_t j, uint32_t k) {
return op | d | (j << 5) | (k << 10);
}
static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
return begin == 63 ? v >> end : (v & ((1ULL << (begin + 1)) - 1)) >> end;
}
static uint32_t setD5k16(uint32_t insn, uint32_t imm) {
uint32_t immLo = extractBits(imm, 15, 0);
uint32_t immHi = extractBits(imm, 20, 16);
return (insn & 0xfc0003e0) | (immLo << 10) | immHi;
}
static uint32_t setD10k16(uint32_t insn, uint32_t imm) {
uint32_t immLo = extractBits(imm, 15, 0);
uint32_t immHi = extractBits(imm, 25, 16);
return (insn & 0xfc000000) | (immLo << 10) | immHi;
}
static uint32_t setJ20(uint32_t insn, uint32_t imm) {
return (insn & 0xfe00001f) | (extractBits(imm, 19, 0) << 5);
}
static uint32_t setK12(uint32_t insn, uint32_t imm) {
return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
}
static uint32_t setK16(uint32_t insn, uint32_t imm) {
return (insn & 0xfc0003ff) | (extractBits(imm, 15, 0) << 10);
}
static bool isJirl(uint32_t insn) {
return (insn & 0xfc000000) == JIRL;
}
static void handleUleb128(uint8_t *loc, uint64_t val) {
const uint32_t maxcount = 1 + 64 / 7;
uint32_t count;
const char *error = nullptr;
uint64_t orig = decodeULEB128(loc, &count, nullptr, &error);
if (count > maxcount || (count == maxcount && error))
errorOrWarn(getErrorLocation(loc) + "extra space for uleb128");
uint64_t mask = count < maxcount ? (1ULL << 7 * count) - 1 : -1ULL;
encodeULEB128((orig + val) & mask, loc, count);
}
LoongArch::LoongArch() {
defaultCommonPageSize = 16384;
defaultMaxPageSize = 65536;
write32le(trapInstr.data(), BREAK);
copyRel = R_LARCH_COPY;
pltRel = R_LARCH_JUMP_SLOT;
relativeRel = R_LARCH_RELATIVE;
iRelativeRel = R_LARCH_IRELATIVE;
if (config->is64) {
symbolicRel = R_LARCH_64;
tlsModuleIndexRel = R_LARCH_TLS_DTPMOD64;
tlsOffsetRel = R_LARCH_TLS_DTPREL64;
tlsGotRel = R_LARCH_TLS_TPREL64;
tlsDescRel = R_LARCH_TLS_DESC64;
} else {
symbolicRel = R_LARCH_32;
tlsModuleIndexRel = R_LARCH_TLS_DTPMOD32;
tlsOffsetRel = R_LARCH_TLS_DTPREL32;
tlsGotRel = R_LARCH_TLS_TPREL32;
tlsDescRel = R_LARCH_TLS_DESC32;
}
gotRel = symbolicRel;
gotPltHeaderEntriesNum = 2;
pltHeaderSize = 32;
pltEntrySize = 16;
ipltEntrySize = 16;
}
static uint32_t getEFlags(const InputFile *f) {
if (config->is64)
return cast<ObjFile<ELF64LE>>(f)->getObj().getHeader().e_flags;
return cast<ObjFile<ELF32LE>>(f)->getObj().getHeader().e_flags;
}
static bool inputFileHasCode(const InputFile *f) {
for (const auto *sec : f->getSections())
if (sec && sec->flags & SHF_EXECINSTR)
return true;
return false;
}
uint32_t LoongArch::calcEFlags() const {
if (ctx.objectFiles.empty())
return 0;
uint32_t target = 0;
const InputFile *targetFile;
for (const InputFile *f : ctx.objectFiles) {
if (!inputFileHasCode(f))
continue;
uint32_t flags = getEFlags(f);
if (target == 0 && flags != 0) {
target = flags;
targetFile = f;
}
if ((flags & EF_LOONGARCH_ABI_MODIFIER_MASK) !=
(target & EF_LOONGARCH_ABI_MODIFIER_MASK))
error(toString(f) +
": cannot link object files with different ABI from " +
toString(targetFile));
if ((flags & EF_LOONGARCH_OBJABI_MASK) != EF_LOONGARCH_OBJABI_V1)
error(toString(f) + ": unsupported object file ABI version");
}
return target;
}
int64_t LoongArch::getImplicitAddend(const uint8_t *buf, RelType type) const {
switch (type) {
default:
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
case R_LARCH_32:
case R_LARCH_TLS_DTPMOD32:
case R_LARCH_TLS_DTPREL32:
case R_LARCH_TLS_TPREL32:
return SignExtend64<32>(read32le(buf));
case R_LARCH_64:
case R_LARCH_TLS_DTPMOD64:
case R_LARCH_TLS_DTPREL64:
case R_LARCH_TLS_TPREL64:
return read64le(buf);
case R_LARCH_RELATIVE:
case R_LARCH_IRELATIVE:
return config->is64 ? read64le(buf) : read32le(buf);
case R_LARCH_NONE:
case R_LARCH_JUMP_SLOT:
return 0;
case R_LARCH_TLS_DESC32:
return read32le(buf + 4);
case R_LARCH_TLS_DESC64:
return read64le(buf + 8);
}
}
void LoongArch::writeGotPlt(uint8_t *buf, const Symbol &s) const {
if (config->is64)
write64le(buf, in.plt->getVA());
else
write32le(buf, in.plt->getVA());
}
void LoongArch::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
if (config->writeAddends) {
if (config->is64)
write64le(buf, s.getVA());
else
write32le(buf, s.getVA());
}
}
void LoongArch::writePltHeader(uint8_t *buf) const {
uint32_t offset = in.gotPlt->getVA() - in.plt->getVA();
uint32_t sub = config->is64 ? SUB_D : SUB_W;
uint32_t ld = config->is64 ? LD_D : LD_W;
uint32_t addi = config->is64 ? ADDI_D : ADDI_W;
uint32_t srli = config->is64 ? SRLI_D : SRLI_W;
write32le(buf + 0, insn(PCADDU12I, R_T2, hi20(offset), 0));
write32le(buf + 4, insn(sub, R_T1, R_T1, R_T3));
write32le(buf + 8, insn(ld, R_T3, R_T2, lo12(offset)));
write32le(buf + 12, insn(addi, R_T1, R_T1, lo12(-target->pltHeaderSize - 12)));
write32le(buf + 16, insn(addi, R_T0, R_T2, lo12(offset)));
write32le(buf + 20, insn(srli, R_T1, R_T1, config->is64 ? 1 : 2));
write32le(buf + 24, insn(ld, R_T0, R_T0, config->wordsize));
write32le(buf + 28, insn(JIRL, R_ZERO, R_T3, 0));
}
void LoongArch::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const {
uint32_t offset = sym.getGotPltVA() - pltEntryAddr;
write32le(buf + 0, insn(PCADDU12I, R_T3, hi20(offset), 0));
write32le(buf + 4,
insn(config->is64 ? LD_D : LD_W, R_T3, R_T3, lo12(offset)));
write32le(buf + 8, insn(JIRL, R_T1, R_T3, 0));
write32le(buf + 12, insn(ANDI, R_ZERO, R_ZERO, 0));
}
RelType LoongArch::getDynRel(RelType type) const {
return type == target->symbolicRel ? type
: static_cast<RelType>(R_LARCH_NONE);
}
RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
const uint8_t *loc) const {
switch (type) {
case R_LARCH_NONE:
case R_LARCH_MARK_LA:
case R_LARCH_MARK_PCREL:
return R_NONE;
case R_LARCH_32:
case R_LARCH_64:
case R_LARCH_ABS_HI20:
case R_LARCH_ABS_LO12:
case R_LARCH_ABS64_LO20:
case R_LARCH_ABS64_HI12:
return R_ABS;
case R_LARCH_PCALA_LO12:
return isJirl(read32le(loc)) ? R_PLT : R_ABS;
case R_LARCH_TLS_DTPREL32:
case R_LARCH_TLS_DTPREL64:
return R_DTPREL;
case R_LARCH_TLS_TPREL32:
case R_LARCH_TLS_TPREL64:
case R_LARCH_TLS_LE_HI20:
case R_LARCH_TLS_LE_HI20_R:
case R_LARCH_TLS_LE_LO12:
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_LE64_LO20:
case R_LARCH_TLS_LE64_HI12:
return R_TPREL;
case R_LARCH_ADD6:
case R_LARCH_ADD8:
case R_LARCH_ADD16:
case R_LARCH_ADD32:
case R_LARCH_ADD64:
case R_LARCH_ADD_ULEB128:
case R_LARCH_SUB6:
case R_LARCH_SUB8:
case R_LARCH_SUB16:
case R_LARCH_SUB32:
case R_LARCH_SUB64:
case R_LARCH_SUB_ULEB128:
return R_RISCV_ADD;
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
case R_LARCH_PCREL20_S2:
return R_PC;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_B26:
case R_LARCH_CALL36:
return R_PLT_PC;
case R_LARCH_GOT_PC_HI20:
case R_LARCH_GOT64_PC_LO20:
case R_LARCH_GOT64_PC_HI12:
case R_LARCH_TLS_IE_PC_HI20:
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
return R_LOONGARCH_GOT_PAGE_PC;
case R_LARCH_GOT_PC_LO12:
case R_LARCH_TLS_IE_PC_LO12:
return R_LOONGARCH_GOT;
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_GD_PC_HI20:
return R_LOONGARCH_TLSGD_PAGE_PC;
case R_LARCH_PCALA_HI20:
return R_LOONGARCH_PLT_PAGE_PC;
case R_LARCH_PCALA64_LO20:
case R_LARCH_PCALA64_HI12:
return R_LOONGARCH_PAGE_PC;
case R_LARCH_GOT_HI20:
case R_LARCH_GOT_LO12:
case R_LARCH_GOT64_LO20:
case R_LARCH_GOT64_HI12:
case R_LARCH_TLS_IE_HI20:
case R_LARCH_TLS_IE_LO12:
case R_LARCH_TLS_IE64_LO20:
case R_LARCH_TLS_IE64_HI12:
return R_GOT;
case R_LARCH_TLS_LD_HI20:
return R_TLSLD_GOT;
case R_LARCH_TLS_GD_HI20:
return R_TLSGD_GOT;
case R_LARCH_TLS_LE_ADD_R:
case R_LARCH_RELAX:
return config->relax ? R_RELAX_HINT : R_NONE;
case R_LARCH_ALIGN:
return R_RELAX_HINT;
case R_LARCH_TLS_DESC_PC_HI20:
case R_LARCH_TLS_DESC64_PC_LO20:
case R_LARCH_TLS_DESC64_PC_HI12:
return R_LOONGARCH_TLSDESC_PAGE_PC;
case R_LARCH_TLS_DESC_PC_LO12:
case R_LARCH_TLS_DESC_LD:
case R_LARCH_TLS_DESC_HI20:
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_TLS_DESC64_LO20:
case R_LARCH_TLS_DESC64_HI12:
return R_TLSDESC;
case R_LARCH_TLS_DESC_CALL:
return R_TLSDESC_CALL;
case R_LARCH_TLS_LD_PCREL20_S2:
return R_TLSLD_PC;
case R_LARCH_TLS_GD_PCREL20_S2:
return R_TLSGD_PC;
case R_LARCH_TLS_DESC_PCREL20_S2:
return R_TLSDESC_PC;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
return R_NONE;
}
}
bool LoongArch::usesOnlyLowPageBits(RelType type) const {
switch (type) {
default:
return false;
case R_LARCH_PCALA_LO12:
case R_LARCH_GOT_LO12:
case R_LARCH_GOT_PC_LO12:
case R_LARCH_TLS_IE_PC_LO12:
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_TLS_DESC_PC_LO12:
return true;
}
}
void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
switch (rel.type) {
case R_LARCH_32_PCREL:
checkInt(loc, val, 32, rel);
[[fallthrough]];
case R_LARCH_32:
case R_LARCH_TLS_DTPREL32:
write32le(loc, val);
return;
case R_LARCH_64:
case R_LARCH_TLS_DTPREL64:
case R_LARCH_64_PCREL:
write64le(loc, val);
return;
case R_LARCH_PCREL20_S2:
case R_LARCH_TLS_LD_PCREL20_S2:
case R_LARCH_TLS_GD_PCREL20_S2:
case R_LARCH_TLS_DESC_PCREL20_S2:
checkInt(loc, val, 22, rel);
checkAlignment(loc, val, 4, rel);
write32le(loc, setJ20(read32le(loc), val >> 2));
return;
case R_LARCH_B16:
checkInt(loc, val, 18, rel);
checkAlignment(loc, val, 4, rel);
write32le(loc, setK16(read32le(loc), val >> 2));
return;
case R_LARCH_B21:
checkInt(loc, val, 23, rel);
checkAlignment(loc, val, 4, rel);
write32le(loc, setD5k16(read32le(loc), val >> 2));
return;
case R_LARCH_B26:
checkInt(loc, val, 28, rel);
checkAlignment(loc, val, 4, rel);
write32le(loc, setD10k16(read32le(loc), val >> 2));
return;
case R_LARCH_CALL36: {
if (((int64_t)val + 0x20000) != llvm::SignExtend64(val + 0x20000, 38))
reportRangeError(loc, rel, Twine(val), llvm::minIntN(38) - 0x20000,
llvm::maxIntN(38) - 0x20000);
checkAlignment(loc, val, 4, rel);
uint32_t hi20 = extractBits(val + (1 << 17), 37, 18);
uint32_t lo16 = extractBits(val, 17, 2);
write32le(loc, setJ20(read32le(loc), hi20));
write32le(loc + 4, setK16(read32le(loc + 4), lo16));
return;
}
case R_LARCH_PCALA_LO12:
if (isJirl(read32le(loc))) {
checkAlignment(loc, val, 4, rel);
val = SignExtend64<12>(val);
write32le(loc, setK16(read32le(loc), val >> 2));
return;
}
[[fallthrough]];
case R_LARCH_ABS_LO12:
case R_LARCH_GOT_PC_LO12:
case R_LARCH_GOT_LO12:
case R_LARCH_TLS_LE_LO12:
case R_LARCH_TLS_IE_PC_LO12:
case R_LARCH_TLS_IE_LO12:
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_DESC_PC_LO12:
case R_LARCH_TLS_DESC_LO12:
write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0)));
return;
case R_LARCH_ABS_HI20:
case R_LARCH_PCALA_HI20:
case R_LARCH_GOT_PC_HI20:
case R_LARCH_GOT_HI20:
case R_LARCH_TLS_LE_HI20:
case R_LARCH_TLS_IE_PC_HI20:
case R_LARCH_TLS_IE_HI20:
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_PC_HI20:
case R_LARCH_TLS_GD_HI20:
case R_LARCH_TLS_DESC_PC_HI20:
case R_LARCH_TLS_DESC_HI20:
write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12)));
return;
case R_LARCH_TLS_LE_HI20_R:
write32le(loc, setJ20(read32le(loc), extractBits(val + 0x800, 31, 12)));
return;
case R_LARCH_ABS64_LO20:
case R_LARCH_PCALA64_LO20:
case R_LARCH_GOT64_PC_LO20:
case R_LARCH_GOT64_LO20:
case R_LARCH_TLS_LE64_LO20:
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_LO20:
case R_LARCH_TLS_DESC64_PC_LO20:
case R_LARCH_TLS_DESC64_LO20:
write32le(loc, setJ20(read32le(loc), extractBits(val, 51, 32)));
return;
case R_LARCH_ABS64_HI12:
case R_LARCH_PCALA64_HI12:
case R_LARCH_GOT64_PC_HI12:
case R_LARCH_GOT64_HI12:
case R_LARCH_TLS_LE64_HI12:
case R_LARCH_TLS_IE64_PC_HI12:
case R_LARCH_TLS_IE64_HI12:
case R_LARCH_TLS_DESC64_PC_HI12:
case R_LARCH_TLS_DESC64_HI12:
write32le(loc, setK12(read32le(loc), extractBits(val, 63, 52)));
return;
case R_LARCH_ADD6:
*loc = (*loc & 0xc0) | ((*loc + val) & 0x3f);
return;
case R_LARCH_ADD8:
*loc += val;
return;
case R_LARCH_ADD16:
write16le(loc, read16le(loc) + val);
return;
case R_LARCH_ADD32:
write32le(loc, read32le(loc) + val);
return;
case R_LARCH_ADD64:
write64le(loc, read64le(loc) + val);
return;
case R_LARCH_ADD_ULEB128:
handleUleb128(loc, val);
return;
case R_LARCH_SUB6:
*loc = (*loc & 0xc0) | ((*loc - val) & 0x3f);
return;
case R_LARCH_SUB8:
*loc -= val;
return;
case R_LARCH_SUB16:
write16le(loc, read16le(loc) - val);
return;
case R_LARCH_SUB32:
write32le(loc, read32le(loc) - val);
return;
case R_LARCH_SUB64:
write64le(loc, read64le(loc) - val);
return;
case R_LARCH_SUB_ULEB128:
handleUleb128(loc, -val);
return;
case R_LARCH_MARK_LA:
case R_LARCH_MARK_PCREL:
return;
case R_LARCH_TLS_LE_ADD_R:
case R_LARCH_RELAX:
return;
case R_LARCH_TLS_DESC_LD:
return;
case R_LARCH_TLS_DESC32:
write32le(loc + 4, val);
return;
case R_LARCH_TLS_DESC64:
write64le(loc + 8, val);
return;
default:
llvm_unreachable("unknown relocation");
}
}
static bool relax(InputSection &sec) {
const uint64_t secAddr = sec.getVA();
const MutableArrayRef<Relocation> relocs = sec.relocs();
auto &aux = *sec.relaxAux;
bool changed = false;
ArrayRef<SymbolAnchor> sa = ArrayRef(aux.anchors);
uint64_t delta = 0;
std::fill_n(aux.relocTypes.get(), relocs.size(), R_LARCH_NONE);
aux.writes.clear();
for (auto [i, r] : llvm::enumerate(relocs)) {
const uint64_t loc = secAddr + r.offset - delta;
uint32_t &cur = aux.relocDeltas[i], remove = 0;
switch (r.type) {
case R_LARCH_ALIGN: {
const uint64_t addend =
r.sym->isUndefined() ? Log2_64(r.addend) + 1 : r.addend;
const uint64_t allBytes = (1ULL << (addend & 0xff)) - 4;
const uint64_t align = 1ULL << (addend & 0xff);
const uint64_t maxBytes = addend >> 8;
const uint64_t off = loc & (align - 1);
const uint64_t curBytes = off == 0 ? 0 : align - off;
if (maxBytes != 0 && curBytes > maxBytes)
remove = allBytes;
else
remove = allBytes - curBytes;
if (LLVM_UNLIKELY(static_cast<int32_t>(remove) < 0)) {
errorOrWarn(getErrorLocation((const uint8_t *)loc) +
"insufficient padding bytes for " + lld::toString(r.type) +
": " + Twine(allBytes) + " bytes available for " +
"requested alignment of " + Twine(align) + " bytes");
remove = 0;
}
break;
}
}
for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) {
if (sa[0].end)
sa[0].d->size = sa[0].offset - delta - sa[0].d->value;
else
sa[0].d->value = sa[0].offset - delta;
}
delta += remove;
if (delta != cur) {
cur = delta;
changed = true;
}
}
for (const SymbolAnchor &a : sa) {
if (a.end)
a.d->size = a.offset - delta - a.d->value;
else
a.d->value = a.offset - delta;
}
if (!isUInt<32>(delta))
fatal("section size decrease is too large: " + Twine(delta));
sec.bytesDropped = delta;
return changed;
}
bool LoongArch::relaxOnce(int pass) const {
if (config->relocatable)
return false;
if (pass == 0)
initSymbolAnchors();
SmallVector<InputSection *, 0> storage;
bool changed = false;
for (OutputSection *osec : outputSections) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
for (InputSection *sec : getInputSections(*osec, storage))
changed |= relax(*sec);
}
return changed;
}
void LoongArch::finalizeRelax(int passes) const {
log("relaxation passes: " + Twine(passes));
SmallVector<InputSection *, 0> storage;
for (OutputSection *osec : outputSections) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
for (InputSection *sec : getInputSections(*osec, storage)) {
RelaxAux &aux = *sec->relaxAux;
if (!aux.relocDeltas)
continue;
MutableArrayRef<Relocation> rels = sec->relocs();
ArrayRef<uint8_t> old = sec->content();
size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1];
uint8_t *p = context().bAlloc.Allocate<uint8_t>(newSize);
uint64_t offset = 0;
int64_t delta = 0;
sec->content_ = p;
sec->size = newSize;
sec->bytesDropped = 0;
for (size_t i = 0, e = rels.size(); i != e; ++i) {
uint32_t remove = aux.relocDeltas[i] - delta;
delta = aux.relocDeltas[i];
if (remove == 0 && aux.relocTypes[i] == R_LARCH_NONE)
continue;
const Relocation &r = rels[i];
uint64_t size = r.offset - offset;
memcpy(p, old.data() + offset, size);
p += size;
offset = r.offset + remove;
}
memcpy(p, old.data() + offset, old.size() - offset);
delta = 0;
for (size_t i = 0, e = rels.size(); i != e;) {
uint64_t cur = rels[i].offset;
do {
rels[i].offset -= delta;
if (aux.relocTypes[i] != R_LARCH_NONE)
rels[i].type = aux.relocTypes[i];
} while (++i != e && rels[i].offset == cur);
delta = aux.relocDeltas[i - 1];
}
}
}
}
TargetInfo *elf::getLoongArchTargetInfo() {
static LoongArch target;
return ⌖
}