blob: 757c0e2c0c51b9c1cbd6ab37ebcbf541d15b630d [file] [log] [blame]
//===- TargetImpl.h ---------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_ARCH_TARGETIMPL_H
#define LLD_ELF_ARCH_TARGETIMPL_H
#include "InputFiles.h"
#include "InputSection.h"
#include "Relocations.h"
#include "Symbols.h"
#include "llvm/BinaryFormat/ELF.h"
namespace lld {
namespace elf {
// getControlTransferAddend: If this relocation is used for control transfer
// instructions (e.g. branch, branch-link or call) or code references (e.g.
// virtual function pointers) and indicates an address-insignificant reference,
// return the effective addend for the relocation, otherwise return
// std::nullopt. The effective addend for a relocation is the addend that is
// used to determine its branch destination.
//
// getBranchInfo: If a control transfer relocation referring to is+offset
// directly transfers control to a relocated branch instruction in the specified
// section, return the relocation for the branch target as well as its effective
// addend (see above). Otherwise return {nullptr, 0}.
//
// mergeControlTransferRelocations: Given r1, a relocation for which
// getControlTransferAddend() returned a value, and r2, a relocation returned by
// getBranchInfo(), modify r1 so that it branches directly to the target of r2.
template <typename GetBranchInfo, typename GetControlTransferAddend,
typename MergeControlTransferRelocations>
inline void applyBranchToBranchOptImpl(
Ctx &ctx, GetBranchInfo getBranchInfo,
GetControlTransferAddend getControlTransferAddend,
MergeControlTransferRelocations mergeControlTransferRelocations) {
// Needs to run serially because it writes to the relocations array as well as
// reading relocations of other sections.
for (ELFFileBase *f : ctx.objectFiles) {
auto getRelocBranchInfo =
[&getBranchInfo](Relocation &r,
uint64_t addend) -> std::pair<Relocation *, uint64_t> {
auto *target = dyn_cast_or_null<Defined>(r.sym);
// We don't allow preemptible symbols (may go somewhere else),
// absolute symbols (runtime behavior unknown), non-executable memory
// (ditto) or non-regular sections (no section data).
if (!target || target->isPreemptible || !target->section ||
!(target->section->flags & llvm::ELF::SHF_EXECINSTR) ||
target->section->kind() != SectionBase::Regular)
return {nullptr, 0};
return getBranchInfo(*cast<InputSection>(target->section),
target->value + addend);
};
for (InputSectionBase *s : f->getSections()) {
if (!s)
continue;
for (Relocation &r : s->relocations) {
if (std::optional<uint64_t> addend =
getControlTransferAddend(*cast<InputSection>(s), r)) {
std::pair<Relocation *, uint64_t> targetAndAddend =
getRelocBranchInfo(r, *addend);
if (targetAndAddend.first) {
while (1) {
std::pair<Relocation *, uint64_t> nextTargetAndAddend =
getRelocBranchInfo(*targetAndAddend.first,
targetAndAddend.second);
if (!nextTargetAndAddend.first)
break;
targetAndAddend = nextTargetAndAddend;
}
mergeControlTransferRelocations(r, *targetAndAddend.first);
}
}
}
}
}
}
} // namespace elf
} // namespace lld
#endif