diff --git a/lld/ELF/Arch/NanoMips.cpp b/lld/ELF/Arch/NanoMips.cpp index 38f862326591d..fecc098ff0999 100644 --- a/lld/ELF/Arch/NanoMips.cpp +++ b/lld/ELF/Arch/NanoMips.cpp @@ -178,6 +178,9 @@ template class NanoMips final : public TargetInfo { bool relaxOnce(int pass) const override { return this->transformController.relaxOnce(pass); } + + void scatterNops() const override { this->transformController.scatterNops(); } + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; uint32_t calcEFlags() const override; diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 9a57baf2a4a77..fe2085dccbaa8 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -256,6 +256,8 @@ struct Config { bool relrGlibc = false; bool relrPackDynRelocs = false; llvm::DenseSet saveTempsArgs; + uint32_t scatterNopsDensity; + uint32_t scatterNopsSeed; llvm::SmallVector, 0> shuffleSections; bool singleRoRx; bool shared; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index c6a7f40602217..8c6810bb1f960 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1236,6 +1236,11 @@ static void readConfigs(opt::InputArgList &args) { } config->searchPaths = args::getStrings(args, OPT_library_path); + config->scatterNopsDensity = + args.hasArg(OPT_relocatable) + ? 0 + : args::getInteger(args, OPT_scatter_nops_density, 0); + config->scatterNopsSeed = args::getInteger(args, OPT_scatter_nops_seed, 0); config->sectionStartMap = getSectionStartMap(args); config->shared = args.hasArg(OPT_shared); config->singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true); diff --git a/lld/ELF/NanoMipsTransformations.cpp b/lld/ELF/NanoMipsTransformations.cpp index 5806e0fcf1c2b..7710bcfb520d7 100644 --- a/lld/ELF/NanoMipsTransformations.cpp +++ b/lld/ELF/NanoMipsTransformations.cpp @@ -21,6 +21,7 @@ #include "llvm/Object/ELFTypes.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" #define DEBUG_TYPE "lld-nanomips" using namespace lld; @@ -362,19 +363,16 @@ void NanoMipsTransform::changeBytes(InputSection *isec, uint64_t location, } } -void NanoMipsTransform::updateSectionContent(InputSection *isec, - uint64_t location, int32_t delta, - bool align) { - +bool NanoMipsTransform::updateSectionContentInner(InputSection *isec, + uint64_t location, + int32_t delta, bool align) { // Other than increasing/decreasing byte size of isec, it also // allocates new section content if delta is 0 and section // hasn't yet been changed, as those content would be readonly - changeBytes(isec, location, delta); + NanoMipsTransform::changeBytes(isec, location, delta); if (delta == 0) - return; + return false; - this->changed = true; - this->changedThisIteration = true; LLVM_DEBUG(llvm::dbgs() << "Changed size of input section " << (isec->file ? isec->file->getName() : "nofile") << "(" << isec->name << ") by " << delta @@ -395,7 +393,7 @@ void NanoMipsTransform::updateSectionContent(InputSection *isec, // TODO: Check whether sections without a corresponding file // may have symbols that should be changed during transformations if (!isec->file) - return; + return true; for (auto &symAnchor : isec->nanoMipsRelaxAux->anchors) { Defined *dSym = symAnchor.d; @@ -413,6 +411,19 @@ void NanoMipsTransform::updateSectionContent(InputSection *isec, } } } + + return true; +} + +void NanoMipsTransform::updateSectionContent(InputSection *isec, + uint64_t location, int32_t delta, + bool align) { + bool changed = NanoMipsTransform::updateSectionContentInner(isec, location, + delta, align); + if (changed) { + this->changed = true; + this->changedThisIteration = true; + } } SmallVector NanoMipsTransform::getTransformInsns( @@ -1140,6 +1151,37 @@ void NanoMipsTransformController::changeState(int pass) { LLVM_DEBUG(llvm::dbgs() << "Changed transform state to None\n";); } +template +void NanoMipsTransformController::scatterNops() const { + if (!this->mayRelax()) + return; + + NanoMipsTransformController::initTransformAuxInfo(); + + for (OutputSection *osec : outputSections) { + if (!isOutputSecTransformable(osec)) + continue; + + SmallVector storage; + for (InputSection *sec : getInputSections(*osec, storage)) { + if (!NanoMipsTransformController::safeToModify(sec) || + !sec->relocs().size() || !(sec->flags & SHF_EXECINSTR)) + continue; + + uint64_t hash = llvm::xxHash64(sec->name) + config->scatterNopsSeed; + if ((hash % 100) >= config->scatterNopsDensity) + continue; + + const uint32_t nop16 = 0x9008; + const uint32_t nop16Size = 2; + NanoMipsTransform::updateSectionContentInner(sec, 0, 2, false); + writeInsn(nop16, sec->content(), 0, nop16Size); + LLVM_DEBUG(llvm::dbgs() << sec->name + << " updated with a nop in the beginning!\n";); + } + } +} + // relaxOnce is used for both relaxations and expansions template bool NanoMipsTransformController::relaxOnce(int pass) const { @@ -1148,10 +1190,10 @@ bool NanoMipsTransformController::relaxOnce(int pass) const { LLVM_DEBUG(llvm::dbgs() << "Transformation Pass num: " << pass << "\n";); bool shouldRunAgain = false; if (this->mayRelax()) { - if (pass == 0) { + if (pass == 0 && config->scatterNopsDensity == 0) { // Initialization of additional info that are needed for // relaxations/expansions - initTransformAuxInfo(); + NanoMipsTransformController::initTransformAuxInfo(); } for (OutputSection *osec : outputSections) { if (!isOutputSecTransformable(osec)) @@ -1159,7 +1201,7 @@ bool NanoMipsTransformController::relaxOnce(int pass) const { SmallVector storage; for (InputSection *sec : getInputSections(*osec, storage)) { - if (!this->safeToModify(sec)) + if (!NanoMipsTransformController::safeToModify(sec)) continue; if (sec->relocs().size()) this->scanAndTransform(sec); @@ -1175,8 +1217,8 @@ bool NanoMipsTransformController::relaxOnce(int pass) const { } template -inline bool lld::elf::NanoMipsTransformController::safeToModify( - InputSection *sec) const { +inline bool +lld::elf::NanoMipsTransformController::safeToModify(InputSection *sec) { bool modifiable = false; if (auto *obj = sec->getFile()) { modifiable = @@ -1186,14 +1228,15 @@ inline bool lld::elf::NanoMipsTransformController::safeToModify( } template -void NanoMipsTransformController::initTransformAuxInfo() const { +void NanoMipsTransformController::initTransformAuxInfo() { SmallVector storage; for (OutputSection *osec : outputSections) { if (!isOutputSecTransformable(osec)) continue; for (InputSection *sec : getInputSections(*osec, storage)) { - if (!this->safeToModify(sec) || sec->relocs().size() == 0) + if (!NanoMipsTransformController::safeToModify(sec) || + sec->relocs().size() == 0) continue; sec->nanoMipsRelaxAux = make(); sec->nanoMipsRelaxAux->isAlreadyTransformed = false; diff --git a/lld/ELF/NanoMipsTransformations.h b/lld/ELF/NanoMipsTransformations.h index 524e4d0fb7862..2b5d0c4099f83 100644 --- a/lld/ELF/NanoMipsTransformations.h +++ b/lld/ELF/NanoMipsTransformations.h @@ -352,6 +352,8 @@ class NanoMipsTransform { getTransformTemplate(const NanoMipsInsProperty *insProperty, uint32_t relNum, uint64_t valueToRelocate, uint64_t insn, const InputSection *isec) const = 0; + static bool updateSectionContentInner(InputSection *isec, uint64_t location, + int32_t delta, bool aligned); virtual void updateSectionContent(InputSection *isec, uint64_t location, int32_t delta, bool align = false); bool getChanged() { return changed; } @@ -375,7 +377,7 @@ class NanoMipsTransform { static uint32_t newSkipBcSymCount; private: - void changeBytes(InputSection *isec, uint64_t location, int32_t count); + static void changeBytes(InputSection *isec, uint64_t location, int32_t count); }; class NanoMipsTransformExpand : public NanoMipsTransform { @@ -450,6 +452,7 @@ template class NanoMipsTransformController { void initState(); bool relaxOnce(int pass) const; + void scatterNops() const; // should be called before change state bool shouldRunAgain() const { return this->currentState->getChanged(); } @@ -467,8 +470,8 @@ template class NanoMipsTransformController { NanoMipsTransform::TransformKind getType() const { return this->currentState->getType(); } - bool safeToModify(InputSection *sec) const; - void initTransformAuxInfo() const; + static bool safeToModify(InputSection *sec); + static void initTransformAuxInfo(); void scanAndTransform(InputSection *sec) const; void align(InputSection *sec, Relocation &reloc, uint32_t relNum) const; void changeState(int pass); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 42fb6eb7642c2..d5f161d2d00b9 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -371,6 +371,20 @@ defm retain_symbols_file: Eq<"retain-symbols-file", "Retain only the symbols listed in the file">, MetaVarName<"">; +defm scatter_nops_density: EEq<"scatter-nops-density", + "Approximate percentage of instruction sections that will have nops " + "inserted at their beginnings. If 0, scatter nops doesn't do anything. " + "Doesn't do anything for relocatable outputs (default=0)(nanoMIPS only)">, + Flags<[HelpHidden]>; + + +defm scatter_nops_seed: EEq<"scatter-nops-seed", + "Use a different seed for the pseudo-random determination of " + "section that will be inserted with a nop. If scatter-nops-density is " + "0 or the requested output is relocatable, than this option doesn't have " + "effect (default=0)(nanoMIPS only)">, + Flags<[HelpHidden]>; + defm script: Eq<"script", "Read linker script">; defm section_start: Eq<"section-start", "Set address of section">, diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index d1c4cf63d2662..4b071d4d956cc 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -94,6 +94,10 @@ class TargetInfo { // Do a linker relaxation pass and return true if we changed something. virtual bool relaxOnce(int pass) const { return false; } + // Insert nops randomly on the beginning of every instruction section. + // Currently only implemented on nanoMIPS. + virtual void scatterNops() const {} + virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 1373734b65b7f..2cb0656c71298 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1657,6 +1657,14 @@ template void Writer::finalizeAddressDependentContent() { ThunkCreator tc; AArch64Err843419Patcher a64p; ARMErr657417Patcher a32p; + + // Put scatter nops here, everything is finished with input sections + // till this point, except some instruction transformations. Putting + // scatter nops may trigger those transformations, so we have to put + // it before these transformations. + if (config->scatterNopsDensity != 0) + target->scatterNops(); + script->assignAddresses(); // .ARM.exidx and SHF_LINK_ORDER do not require precise addresses, but they // do require the relative addresses of OutputSections because linker scripts