|
| 1 | +//===- AdvancedLTO.cpp----------------------------------------------------===// |
| 2 | +// Part of the eld Project, under the BSD License |
| 3 | +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. |
| 4 | +// SPDX-License-Identifier: BSD-3-Clause |
| 5 | +//===----------------------------------------------------------------------===// |
| 6 | + |
| 7 | +#include "Defines.h" |
| 8 | +#include "LinkerPlugin.h" |
| 9 | +#include "LinkerWrapper.h" |
| 10 | +#include "PluginADT.h" |
| 11 | +#include "PluginVersion.h" |
| 12 | +#include "llvm/ADT/ArrayRef.h" |
| 13 | +#include "llvm/BinaryFormat/ELF.h" |
| 14 | +#include "llvm/IR/GlobalValue.h" |
| 15 | +#include "llvm/LTO/LTO.h" |
| 16 | +#include <cassert> |
| 17 | +#include <cerrno> |
| 18 | +#include <cstdint> |
| 19 | +#include <cstdlib> |
| 20 | +#include <memory> |
| 21 | +#include <optional> |
| 22 | +#include <string> |
| 23 | +#include <unordered_map> |
| 24 | + |
| 25 | +namespace { |
| 26 | + |
| 27 | +using namespace eld::plugin; |
| 28 | + |
| 29 | +struct ModuleData { |
| 30 | + explicit ModuleData(BitcodeFile BCF) : BCFile(BCF) {} |
| 31 | + |
| 32 | + BitcodeFile BCFile; |
| 33 | + std::unordered_map<std::string, Section> SectionsByName; |
| 34 | +}; |
| 35 | + |
| 36 | +class DLL_A_EXPORT AdvancedLTO : public LinkerPlugin { |
| 37 | +public: |
| 38 | + AdvancedLTO() : LinkerPlugin("AdvancedLTO") {} |
| 39 | + |
| 40 | + void Init(const std::string &Options) override { |
| 41 | + (void)Options; |
| 42 | + EnableLTOLinkerScripts = |
| 43 | + getLinker()->getLinkerConfig().hasLTOLinkerScripts(); |
| 44 | + TraceLTO = getLinker()->getLinkerConfig().shouldTraceLTO(); |
| 45 | + } |
| 46 | + |
| 47 | + LTOModule *CreateLTOModule(BitcodeFile BCF, LTOModuleHash Hash) override { |
| 48 | + LastBitcodeFile = BCF; |
| 49 | + auto Data = std::make_unique<ModuleData>(BCF); |
| 50 | + LTOModule *M = reinterpret_cast<LTOModule *>(Data.get()); |
| 51 | + Modules[M] = std::move(Data); |
| 52 | + FilesByHash.insert_or_assign(Hash, &BCF.getBitcodeFile()); |
| 53 | + return M; |
| 54 | + } |
| 55 | + |
| 56 | + void ReadSymbols(LTOModule &M) override { |
| 57 | + ModuleData *Data = importModule(M); |
| 58 | + if (!Data) |
| 59 | + return; |
| 60 | + |
| 61 | + auto getKind = [](const llvm::lto::InputFile::Symbol &Sym) { |
| 62 | + if (Sym.isUndefined()) |
| 63 | + return Symbol::Undefined; |
| 64 | + if (Sym.isCommon()) |
| 65 | + return Symbol::Common; |
| 66 | + return Symbol::Define; |
| 67 | + }; |
| 68 | + |
| 69 | + auto getBinding = [](const llvm::lto::InputFile::Symbol &Sym) { |
| 70 | + if (!Sym.isGlobal()) |
| 71 | + return Symbol::Local; |
| 72 | + if (Sym.isWeak()) |
| 73 | + return Symbol::Weak; |
| 74 | + return Symbol::Global; |
| 75 | + }; |
| 76 | + |
| 77 | + auto getVisibility = [](const llvm::lto::InputFile::Symbol &Sym) { |
| 78 | + switch (Sym.getVisibility()) { |
| 79 | + case llvm::GlobalValue::DefaultVisibility: |
| 80 | + return Symbol::Default; |
| 81 | + case llvm::GlobalValue::HiddenVisibility: |
| 82 | + return Symbol::Hidden; |
| 83 | + case llvm::GlobalValue::ProtectedVisibility: |
| 84 | + return Symbol::Protected; |
| 85 | + } |
| 86 | + return Symbol::Default; |
| 87 | + }; |
| 88 | + |
| 89 | + auto getType = [](const llvm::lto::InputFile::Symbol &Sym) { |
| 90 | + if (Sym.isExecutable()) |
| 91 | + return static_cast<unsigned>(llvm::ELF::STT_FUNC); |
| 92 | + if (Sym.isTLS()) |
| 93 | + return static_cast<unsigned>(llvm::ELF::STT_TLS); |
| 94 | + return static_cast<unsigned>(llvm::ELF::STT_OBJECT); |
| 95 | + }; |
| 96 | + |
| 97 | + llvm::ArrayRef<llvm::lto::InputFile::Symbol> Symbols = |
| 98 | + Data->BCFile.getInputFile().symbols(); |
| 99 | + |
| 100 | + for (const auto &Sym : llvm::enumerate(Symbols)) { |
| 101 | + unsigned SymbolIndex = Sym.index(); |
| 102 | + const llvm::lto::InputFile::Symbol &LtoSym = Sym.value(); |
| 103 | + bool KeepComdat = Data->BCFile.findIfKeptComdat(LtoSym.getComdatIndex()); |
| 104 | + Symbol::Kind Kind = KeepComdat ? getKind(LtoSym) : Symbol::Undefined; |
| 105 | + |
| 106 | + Section InputSection; |
| 107 | + llvm::StringRef SectionName = LtoSym.getSectionName(); |
| 108 | + if (SectionName.empty() && LtoSym.isCommon()) |
| 109 | + SectionName = "COMMON"; |
| 110 | + |
| 111 | + if (!SectionName.empty()) { |
| 112 | + auto ExpSection = getOrCreateSection(*Data, SectionName); |
| 113 | + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSection); |
| 114 | + InputSection = *ExpSection; |
| 115 | + } |
| 116 | + |
| 117 | + auto ExpSymbol = getLinker()->addSymbol( |
| 118 | + Data->BCFile, LtoSym.getName().str(), getBinding(LtoSym), |
| 119 | + InputSection, Kind, getVisibility(LtoSym), getType(LtoSym), |
| 120 | + LtoSym.isCommon() ? LtoSym.getCommonSize() : 0, SymbolIndex); |
| 121 | + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSymbol); |
| 122 | + |
| 123 | + if (!EnableLTOLinkerScripts || !InputSection || Kind == Symbol::Undefined) |
| 124 | + continue; |
| 125 | + |
| 126 | + auto Rule = InputSection.getLinkerScriptRule(); |
| 127 | + if (!Rule || !Rule.isKeep()) |
| 128 | + continue; |
| 129 | + |
| 130 | + auto ExpSetPreserve = getLinker()->setPreserveSymbol(*ExpSymbol); |
| 131 | + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSetPreserve); |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + void VisitSections(InputFile IF) override { |
| 136 | + if (!EnableLTOLinkerScripts) |
| 137 | + return; |
| 138 | + |
| 139 | + for (Section S : IF.getSections()) { |
| 140 | + std::string Name = S.getName(); |
| 141 | + size_t FirstPos = Name.find("^^"); |
| 142 | + if (FirstPos == std::string::npos) |
| 143 | + continue; |
| 144 | + std::string BaseName = Name.substr(0, FirstPos); |
| 145 | + std::string HashStr = Name.substr(FirstPos + 2); |
| 146 | + size_t SecondPos = HashStr.find("^^"); |
| 147 | + if (SecondPos != std::string::npos) |
| 148 | + HashStr = HashStr.substr(SecondPos + 2); |
| 149 | + if (HashStr.empty()) |
| 150 | + continue; |
| 151 | + |
| 152 | + if (!getLinker()->isPostLTOPhase()) |
| 153 | + continue; |
| 154 | + |
| 155 | + errno = 0; |
| 156 | + char *End = nullptr; |
| 157 | + unsigned long long Parsed = std::strtoull(HashStr.c_str(), &End, 16); |
| 158 | + if (errno != 0 || End == HashStr.c_str() || *End != '\0') |
| 159 | + continue; |
| 160 | + uint64_t Hash = Parsed; |
| 161 | + |
| 162 | + auto It = FilesByHash.find(Hash); |
| 163 | + if (It == FilesByHash.end()) |
| 164 | + continue; |
| 165 | + |
| 166 | + getLinker()->setSectionName(S, BaseName); |
| 167 | + getLinker()->setRuleMatchingInput(S, BitcodeFile(*It->second)); |
| 168 | + if (TraceLTO) { |
| 169 | + (void)getLinker()->reportDiag( |
| 170 | + getLinker()->getNoteDiagID( |
| 171 | + "Applying section namespace override %0"), |
| 172 | + Name); |
| 173 | + } |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + void ActBeforeSectionMerging() override { |
| 178 | + if (!getLinker()->getLinkerScript().hasSectionsCommand() || |
| 179 | + getLinker()->getLinkMode() == LinkerWrapper::PartialLink || |
| 180 | + FilesByHash.empty()) |
| 181 | + return; |
| 182 | + |
| 183 | + auto ExpSort = getLinker()->sortInputSectionsForSectionMerging( |
| 184 | + [&](const Section &A, const Section &B) { |
| 185 | + InputFile RMInputA = A.getRuleMatchingInput(); |
| 186 | + InputFile RMInputB = B.getRuleMatchingInput(); |
| 187 | + assert(RMInputA.getInputFile() && RMInputB.getInputFile() && |
| 188 | + "rule-matching inputs must be valid"); |
| 189 | + |
| 190 | + if (RMInputA.isInternal() && !RMInputB.isInternal()) |
| 191 | + return false; |
| 192 | + if (!RMInputA.isInternal() && RMInputB.isInternal()) |
| 193 | + return true; |
| 194 | + |
| 195 | + auto getEffectiveOrdinal = [&](const Section &S) -> uint32_t { |
| 196 | + InputFile IF = S.getInputFile(); |
| 197 | + if (IF.isLTOGeneratedObject() && !S.hasOldInputFile()) { |
| 198 | + assert(LastBitcodeFile.has_value() && |
| 199 | + "LTO-generated objects require a last bitcode input"); |
| 200 | + return LastBitcodeFile->getOrdinal(); |
| 201 | + } |
| 202 | + return S.getRuleMatchingInput().getOrdinal(); |
| 203 | + }; |
| 204 | + |
| 205 | + return getEffectiveOrdinal(A) < getEffectiveOrdinal(B); |
| 206 | + }, |
| 207 | + "AdvancedLTO: sort input sections by original input ordinal"); |
| 208 | + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSort); |
| 209 | + } |
| 210 | + |
| 211 | +private: |
| 212 | + ModuleData *importModule(LTOModule &M) { |
| 213 | + auto *ID = &M; |
| 214 | + auto It = Modules.find(ID); |
| 215 | + if (It == Modules.end()) |
| 216 | + return nullptr; |
| 217 | + return It->second.get(); |
| 218 | + } |
| 219 | + |
| 220 | + Expected<Section> getOrCreateSection(ModuleData &Data, |
| 221 | + llvm::StringRef SectionName) { |
| 222 | + std::string Key = SectionName.str(); |
| 223 | + auto It = Data.SectionsByName.find(Key); |
| 224 | + if (It != Data.SectionsByName.end()) |
| 225 | + return It->second; |
| 226 | + |
| 227 | + auto ExpSection = getLinker()->createBitcodeSection(Key, Data.BCFile); |
| 228 | + ELDEXP_RETURN_DIAGENTRY_IF_ERROR(ExpSection); |
| 229 | + Data.SectionsByName.emplace(std::move(Key), *ExpSection); |
| 230 | + return *ExpSection; |
| 231 | + } |
| 232 | + |
| 233 | +private: |
| 234 | + bool EnableLTOLinkerScripts = false; |
| 235 | + bool TraceLTO = false; |
| 236 | + std::unordered_map<LTOModule *, std::unique_ptr<ModuleData>> Modules; |
| 237 | + std::unordered_map<uint64_t, eld::BitcodeFile *> FilesByHash; |
| 238 | + std::optional<BitcodeFile> LastBitcodeFile; |
| 239 | +}; |
| 240 | + |
| 241 | +} // namespace |
| 242 | + |
| 243 | +ELD_REGISTER_PLUGIN(AdvancedLTO) |
0 commit comments