From c7a8cdc0efde83c854f7abb0fe5581bed4ea0d5c Mon Sep 17 00:00:00 2001 From: Pavel Kosov Date: Fri, 15 Dec 2023 15:56:45 +0300 Subject: [PATCH] [ADLT] Initial ADLT extension for LLD ADLT - advanced dynamic library layout tool This extension to LLD allows to merge shared libraries into one, performing optimizations on this merged so, thus bringing better performance with reducing code size and RAM consumption Issue: https://gitee.com/openharmony/third_party_llvm-project/issues/I8MNGT Signed-off-by: Pavel Kosov --- .gitignore | 3 + lld/ELF/Arch/AArch64.cpp | 89 ++++++ lld/ELF/Config.h | 2 + lld/ELF/Driver.cpp | 93 +++++- lld/ELF/DriverUtils.cpp | 8 + lld/ELF/InputFiles.cpp | 560 +++++++++++++++++++++++++++++++++- lld/ELF/InputFiles.h | 106 ++++++- lld/ELF/InputSection.cpp | 25 +- lld/ELF/InputSection.h | 11 +- lld/ELF/MapFile.cpp | 6 +- lld/ELF/MarkLive.cpp | 3 +- lld/ELF/Options.td | 2 + lld/ELF/Relocations.cpp | 156 +++++++++- lld/ELF/Symbols.h | 6 +- lld/ELF/SyntheticSections.cpp | 68 ++++- lld/ELF/Target.h | 3 + lld/ELF/Writer.cpp | 36 ++- llvm-build/Makefile | 6 +- llvm-build/build.py | 2 +- 19 files changed, 1112 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index 20c4f52cd378..46544f17502d 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,6 @@ pythonenv* /clang/utils/analyzer/projects/*/RefScanBuildResults # automodapi puts generated documentation files here. /lldb/docs/python_api/ + + +musl_copy_* diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index b23684819a23..7274232d1469 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -44,6 +44,8 @@ public: uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; bool usesOnlyLowPageBits(RelType type) const override; + void deRelocate(uint8_t *loc, const Relocation &rel, + uint64_t *val) const override; // ADLT void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; @@ -335,8 +337,95 @@ static void writeSMovWImm(uint8_t *loc, uint32_t imm) { write32le(loc, inst | ((imm & 0xFFFF) << 5)); } +static void adltRelocateDebugMessage(StringRef title, uint8_t *loc, + const Relocation &rel, uint64_t &val) { + lld::outs() << title + << " loc: " << loc /*<< " loc val: " << Twine::utohexstr(*loc)*/ + << " rel expr: " << rel.expr + << " rel type: " << lld::toString(rel.type) << " rel offset: 0x" + << Twine::utohexstr(rel.offset) << " rel addend: 0x" + << Twine::utohexstr(rel.addend) + << " val: 0x" << Twine::utohexstr(val) + << " rel sym: " << (rel.sym ? rel.sym->getName() : "null!"); + + if (rel.sym && rel.sym->isDefined()) { + auto d = cast(rel.sym); + lld::outs() << ": section " << d->section->name + << ", value 0x" << Twine::utohexstr(d->value); + } + lld::outs() << '\n'; +} + +void AArch64::deRelocate(uint8_t *loc, const Relocation &rel, + uint64_t *val) const { + bool isDebug = false; + if (isDebug) + adltRelocateDebugMessage("[AArch64::deRelocate]: ", loc, rel, *val); + + auto orClear32le = [&]() { + write32le(loc, read32le(loc) & ~((1 << 26) -1)); + }; + + auto orClearAArch64Imm = [&]() { + write32le(loc, read32le(loc) & ~(0xFFF << 10)); + }; + + switch (rel.type) { + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: { + /*if (*val & (1UL << 32)) { + if (isDebug) + debugMessage("found 32 bit turned on! Disabling"); + *val = *val & ~(1UL << 32); + }*/ + orClear32le(); + break; + } + case R_AARCH64_CONDBR19: + case R_AARCH64_LD_PREL_LO19: + case R_AARCH64_MOVW_UABS_G0_NC: + case R_AARCH64_MOVW_UABS_G1_NC: + case R_AARCH64_MOVW_UABS_G2_NC: + case R_AARCH64_MOVW_UABS_G3: + case R_AARCH64_TSTBR14: + orClear32le(); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: { + orClearAArch64Imm(); + break; + } + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: + case R_AARCH64_TLSDESC_LD64_LO12: + case R_AARCH64_LDST128_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: + case R_AARCH64_LD64_GOTPAGE_LO15: + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12: + orClearAArch64Imm(); + break; + } +} + void AArch64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + bool isDebug = false; + if (config->adlt) { + deRelocate(loc, rel, &val); + } + if (config->adlt && isDebug) + adltRelocateDebugMessage("[AArch64::relocate]: ", loc, rel, val); + switch (rel.type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index d6babe9f74c2..94e863502a89 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -220,6 +220,7 @@ struct Configuration { std::vector> shuffleSections; bool singleRoRx; bool shared; + bool adlt = false; bool symbolic; bool isStatic = false; bool sysvHash = false; @@ -384,6 +385,7 @@ struct Ctx { SmallVector> memoryBuffers; SmallVector objectFiles; SmallVector sharedFiles; + SmallVector sharedFilesExtended; SmallVector binaryFiles; SmallVector bitcodeFiles; SmallVector lazyBitcodeFiles; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index abcc8a984a65..d36bdb017fb2 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -274,8 +274,12 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { // the directory part is ignored. Note that path may be a temporary and // cannot be stored into SharedFile::soName. path = mbref.getBufferIdentifier(); - files.push_back( - make(mbref, withLOption ? path::filename(path) : path)); + if (config->adlt) + files.push_back(createSharedFileExtended( + mbref, withLOption ? path::filename(path) : path)); + else + files.push_back( + make(mbref, withLOption ? path::filename(path) : path)); return; case file_magic::bitcode: files.push_back(make(mbref, "", 0, inLib)); @@ -843,7 +847,8 @@ static std::pair getPackDynRelocs(opt::InputArgList &args) { static void readCallGraph(MemoryBufferRef mb) { // Build a map from symbol name to section DenseMap map; - for (ELFFileBase *file : ctx->objectFiles) + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) for (Symbol *sym : file->getSymbols()) map[sym->getName()] = sym; @@ -922,6 +927,7 @@ processCallGraphRelocations(SmallVector &symbolIndices, template static void readCallGraphsFromObjectFiles() { SmallVector symbolIndices; ArrayRef cgProfile; + // ADLT TODO for (auto file : ctx->objectFiles) { auto *obj = cast>(file); if (!processCallGraphRelocations(symbolIndices, cgProfile, obj)) @@ -1086,6 +1092,7 @@ static void readConfigs(opt::InputArgList &args) { else if (arg->getOption().matches(OPT_Bsymbolic)) config->bsymbolic = BsymbolicKind::All; } + config->adlt = args.hasArg(OPT_adlt); config->checkSections = args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); config->chroot = args.getLastArgValue(OPT_chroot); @@ -1101,7 +1108,7 @@ static void readConfigs(opt::InputArgList &args) { config->dwoDir = args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); config->dynamicLinker = getDynamicLinker(args); config->ehFrameHdr = - args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); + args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, config->adlt); config->emitLLVM = args.hasArg(OPT_plugin_opt_emit_llvm, false); config->emitRelocs = args.hasArg(OPT_emit_relocs); config->callGraphProfileSort = args.hasFlag( @@ -1217,7 +1224,7 @@ static void readConfigs(opt::InputArgList &args) { config->searchPaths = args::getStrings(args, OPT_library_path); config->sectionStartMap = getSectionStartMap(args); - config->shared = args.hasArg(OPT_shared); + config->shared = config->adlt ? true : args.hasArg(OPT_shared); config->singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true); config->soName = args.getLastArgValue(OPT_soname); config->sortSection = getSortSection(args); @@ -1818,7 +1825,8 @@ static void excludeLibs(opt::InputArgList &args) { sym->versionId = VER_NDX_LOCAL; }; - for (ELFFileBase *file : ctx->objectFiles) + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) visit(file); for (BitcodeFile *file : ctx->bitcodeFiles) @@ -2014,7 +2022,8 @@ static void writeDependencyFile() { // symbols of type CommonSymbol. static void replaceCommonSymbols() { llvm::TimeTraceScope timeScope("Replace common symbols"); - for (ELFFileBase *file : ctx->objectFiles) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) { if (!file->hasCommonSyms) continue; for (Symbol *sym : file->getGlobalSymbols()) { @@ -2090,7 +2099,8 @@ static void findKeepUniqueSections(opt::InputArgList &args) { // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. - for (InputFile *f : ctx->objectFiles) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (InputFile *f : files) { auto *obj = cast>(f); ArrayRef syms = obj->getSymbols(); if (obj->addrsigSec) { @@ -2344,7 +2354,8 @@ static void redirectSymbols(ArrayRef wrapped) { return; // Update pointers in input files. - parallelForEach(ctx->objectFiles, [&](ELFFileBase *file) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + parallelForEach(files, [&](ELFFileBase *file) { for (Symbol *&sym : file->getMutableGlobalSymbols()) if (Symbol *s = map.lookup(sym)) sym = s; @@ -2379,7 +2390,8 @@ static uint32_t getAndFeatures() { return 0; uint32_t ret = -1; - for (ELFFileBase *f : ctx->objectFiles) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *f : files) { uint32_t features = f->andFeatures; checkAndReportMissingFeature( @@ -2462,6 +2474,46 @@ static void postParseObjectFile(ELFFileBase *file) { } } +static void postParseSharedFileForAdlt(ELFFileBase *file) { + switch (config->ekind) { + case ELF32LEKind: + cast>(file)->postParseForAdlt(); + break; + case ELF32BEKind: + cast>(file)->postParseForAdlt(); + break; + case ELF64LEKind: + cast>(file)->postParseForAdlt(); + break; + case ELF64BEKind: + cast>(file)->postParseForAdlt(); + break; + default: + llvm_unreachable(""); + } +} + +static bool isSectionValidForAdlt(int fileIdx, InputSectionBase *s) { + if (!s || s == &InputSection::discarded) + return false; + uint32_t type = s->type; + StringRef name = s->name; + + bool isBaseType = type == SHT_NOBITS || type == SHT_NOTE || + type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY; + // TODO: fix .debug_info relocation + bool isNeededProgBits = + type == SHT_PROGBITS && + !(name.startswith(".got.plt") || name.startswith(".plt") || name.startswith(".got") || + name.startswith(".eh_frame_hdr") || name.startswith(".debug_")); + bool ret = isBaseType || isNeededProgBits; + + bool isDebug = false; + if (ret && isDebug) + lld::outs() << "[isSectionValidForAdlt]: " << name << "\n"; + return ret; +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. void LinkerDriver::link(opt::InputArgList &args) { @@ -2531,8 +2583,9 @@ void LinkerDriver::link(opt::InputArgList &args) { // producing a shared library. // We also need one if any shared libraries are used and for pie executables // (probably because the dynamic linker needs it). - config->hasDynSymTab = - !ctx->sharedFiles.empty() || config->isPic || config->exportDynamic; + config->hasDynSymTab = (config->adlt ? !ctx->sharedFilesExtended.empty() + : !ctx->sharedFiles.empty()) || + config->isPic || config->exportDynamic; // Some symbols (such as __ehdr_start) are defined lazily only when there // are undefined symbols for them, so we add these to trigger that logic. @@ -2587,6 +2640,9 @@ void LinkerDriver::link(opt::InputArgList &args) { // No more lazy bitcode can be extracted at this point. Do post parse work // like checking duplicate symbols. + if (config->adlt) + parallelForEach(ctx->sharedFilesExtended, postParseSharedFileForAdlt); + parallelForEach(ctx->objectFiles, initializeLocalSymbols); parallelForEach(ctx->objectFiles, postParseObjectFile); parallelForEach(ctx->bitcodeFiles, @@ -2654,6 +2710,7 @@ void LinkerDriver::link(opt::InputArgList &args) { // With this the symbol table should be complete. After this, no new names // except a few linker-synthesized ones will be added to the symbol table. const size_t numObjsBeforeLTO = ctx->objectFiles.size(); + const size_t numSoBeforeLTO = ctx->sharedFilesExtended.size(); invokeELFT(compileBitcodeFiles, skipLinkedOutput); // Symbol resolution finished. Report backward reference problems, @@ -2670,6 +2727,11 @@ void LinkerDriver::link(opt::InputArgList &args) { // compileBitcodeFiles may have produced lto.tmp object files. After this, no // more file will be added. + if (config->adlt) { + auto newSharedFiles = + makeArrayRef(ctx->sharedFilesExtended).slice(numSoBeforeLTO); + parallelForEach(newSharedFiles, postParseSharedFileForAdlt); + } auto newObjectFiles = makeArrayRef(ctx->objectFiles).slice(numObjsBeforeLTO); parallelForEach(newObjectFiles, initializeLocalSymbols); parallelForEach(newObjectFiles, postParseObjectFile); @@ -2693,6 +2755,13 @@ void LinkerDriver::link(opt::InputArgList &args) { // Now that we have a complete list of input files. // Beyond this point, no new files are added. // Aggregate all input sections into one place. + if (config->adlt) + for (auto it : llvm::enumerate(ctx->sharedFilesExtended)) + for (InputSectionBase *s : it.value()->getSections()) + if (isSectionValidForAdlt(it.index(), s)) + inputSections.push_back(s); + + for (InputFile *f : ctx->objectFiles) for (InputSectionBase *s : f->getSections()) if (s && s != &InputSection::discarded) diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp index 51f3dc3a056e..db42b6a36028 100644 --- a/lld/ELF/DriverUtils.cpp +++ b/lld/ELF/DriverUtils.cpp @@ -64,6 +64,13 @@ static void handleColorDiagnostics(opt::InputArgList &args) { error("unknown option: --color-diagnostics=" + s); } +static void handleAdltOption(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_adlt); + if (!arg) + return; + lld::outs() << "adlt is active\n"; +} + static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { StringRef s = arg->getValue(); @@ -120,6 +127,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef argv) { args = this->ParseArgs(vec, missingIndex, missingCount); handleColorDiagnostics(args); + handleAdltOption(args); if (missingCount) error(Twine(args.getArgString(missingIndex)) + ": missing argument"); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 473809b05e9c..ae24890ea57c 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -145,13 +145,13 @@ static bool isCompatible(InputFile *file) { } InputFile *existing = nullptr; - if (!ctx->objectFiles.empty()) - existing = ctx->objectFiles[0]; - else if (!ctx->sharedFiles.empty()) - existing = ctx->sharedFiles[0]; - else if (!ctx->bitcodeFiles.empty()) - existing = ctx->bitcodeFiles[0]; - std::string with; + if (!ctx->objectFiles.empty()) + existing = ctx->objectFiles[0]; + else if (!ctx->sharedFiles.empty()) + existing = ctx->sharedFiles[0]; + else if (!ctx->bitcodeFiles.empty()) + existing = ctx->bitcodeFiles[0]; + std::string with; if (existing) with = " with " + toString(existing); error(toString(file) + " is incompatible" + with); @@ -184,6 +184,13 @@ template static void doParseFile(InputFile *file) { message(toString(file)); // .so file + if (config->adlt) + if (auto *f = dyn_cast>(file)) { + ctx->sharedFilesExtended.push_back(cast(file)); + f->parseForAdlt(); + return; + } + if (auto *f = dyn_cast(file)) { f->parse(); return; @@ -509,6 +516,21 @@ static void handleSectionGroup(ArrayRef sections, prev->nextInSectionGroup = head; } +template +ArrayRef ObjFile::getShndxTable() { + return shndxTable; +} + +static StringRef markItemForAdlt(StringRef input, StringRef filePath) { + if (input.empty()) + return input; + StringRef simpleFile = path::stem(filePath); + std::string mark = "_" + simpleFile.str(); + if (input.endswith(mark)) + return input; + return saver().save(input + mark); +} + template void ObjFile::initializeSections(bool ignoreComdats, const llvm::object::ELFFile &obj) { @@ -548,6 +570,13 @@ void ObjFile::initializeSections(bool ignoreComdats, this->sections[i] = &InputSection::discarded; continue; } + auto secName = check(obj.getSectionName(sec, shstrtab)); + if (config->adlt && sec.sh_type == SHT_NULL) { + auto name = isPatchedSecName ? markItemForAdlt(secName, archiveName) : secName; + this->sections[i] = createInputSection(i, sec, name); + this->sections[i]->address = sec.sh_addr; + this->sections[i]->size = sec.sh_size; + } switch (sec.sh_type) { case SHT_GROUP: { @@ -569,9 +598,16 @@ void ObjFile::initializeSections(bool ignoreComdats, symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this) .second; if (keepGroup) { - if (config->relocatable) - this->sections[i] = createInputSection( - i, sec, check(obj.getSectionName(sec, shstrtab))); + if (config->relocatable) { + auto name = config->adlt && isPatchedSecName + ? markItemForAdlt(secName, archiveName) + : secName; + this->sections[i] = createInputSection(i, sec, name); + if (config->adlt) { + this->sections[i]->address = sec.sh_addr; + this->sections[i]->size = sec.sh_size; + } + } selectedGroups.push_back(entries); continue; } @@ -598,8 +634,14 @@ void ObjFile::initializeSections(bool ignoreComdats, ctx->hasSympart.store(true, std::memory_order_relaxed); LLVM_FALLTHROUGH; default: - this->sections[i] = - createInputSection(i, sec, check(obj.getSectionName(sec, shstrtab))); + auto name = config->adlt && isPatchedSecName + ? markItemForAdlt(secName, archiveName) + : secName; + this->sections[i] = createInputSection(i, sec, name); + if (config->adlt) { + this->sections[i]->address = sec.sh_addr; + this->sections[i]->size = sec.sh_size; + } } } @@ -1028,6 +1070,10 @@ void ObjFile::initializeSymbols(const object::ELFFile &obj) { uint8_t stOther = eSym.st_other; uint8_t type = eSym.getType(); uint64_t value = eSym.st_value; + if (config->adlt) { + const Elf_Shdr *eSec = *obj.getSection(secIdx); + value -= eSec->sh_addr; + } uint64_t size = eSym.st_size; Symbol *sym = symbols[i]; @@ -1095,9 +1141,12 @@ template void ObjFile::initializeLocalSymbols() { if (eSym.st_shndx == SHN_UNDEF || sec == &InputSection::discarded) new (symbols[i]) Undefined(this, name, STB_LOCAL, eSym.st_other, type, /*discardedSecIdx=*/secIdx); - else + else { + const Elf_Shdr *eSec = *this->getObj().getSection(secIdx); + auto value = config->adlt ? eSym.st_value - eSec->sh_addr : eSym.st_value; new (symbols[i]) Defined(this, name, STB_LOCAL, eSym.st_other, type, - eSym.st_value, eSym.st_size, sec); + value, eSym.st_size, sec); + } symbols[i]->isUsedInRegularObj = true; } } @@ -1290,6 +1339,36 @@ std::vector SharedFile::parseVerneed(const ELFFile &obj, return verneeds; } +template +std::vector SharedFileExtended::parseVerneed(const ELFFile &obj, + const typename ELFT::Shdr *sec) { + if (!sec) + return {}; + std::vector verneeds; + ArrayRef data = CHECK(obj.getSectionContents(*sec), this); + const uint8_t *verneedBuf = data.begin(); + for (unsigned i = 0; i != sec->sh_info; ++i) { + if (verneedBuf + sizeof(typename ELFT::Verneed) > data.end()) + fatal(toString(this) + " has an invalid Verneed"); + auto *vn = reinterpret_cast(verneedBuf); + const uint8_t *vernauxBuf = verneedBuf + vn->vn_aux; + for (unsigned j = 0; j != vn->vn_cnt; ++j) { + if (vernauxBuf + sizeof(typename ELFT::Vernaux) > data.end()) + fatal(toString(this) + " has an invalid Vernaux"); + auto *aux = reinterpret_cast(vernauxBuf); + if (aux->vna_name >= this->stringTable.size()) + fatal(toString(this) + " has a Vernaux with an invalid vna_name"); + uint16_t version = aux->vna_other & VERSYM_VERSION; + if (version >= verneeds.size()) + verneeds.resize(version + 1); + verneeds[version] = aux->vna_name; + vernauxBuf += aux->vna_next; + } + verneedBuf += vn->vn_next; + } + return verneeds; +} + // We do not usually care about alignments of data in shared object // files because the loader takes care of it. However, if we promote a // DSO symbol to point to .bss due to copy relocation, we need to keep @@ -1497,6 +1576,433 @@ template void SharedFile::parse() { } } +template +void SharedFileExtended::resolveDuplicatesForAdlt() { + auto pred = [&](DuplicateSymbol dup) { + auto sym = dup.sym; + if (!sym->isDefined()) + return true; + + auto d = cast(sym); + return !d->section && !d->getOutputSection(); + }; + static std::mutex mu; + { + std::lock_guard lock(mu); + llvm::erase_if(ctx->duplicates, pred); + } + + if (ctx->duplicates.empty()) + return; + + { + std::lock_guard lock(mu); + for (DuplicateSymbol &dup : ctx->duplicates) + warn("duplicate: " + dup.file->getName() + ": " + dup.section->name + + ": " + dup.sym->getName()); + ctx->duplicates.clear(); + } +} + +template +SharedFileExtended::SharedFileExtended(MemoryBufferRef mb, + StringRef soName) + : ObjFile(mb, soName), soName(soName), + simpleSoName(path::stem(soName)) { + const_cast(this->fileKind) = InputFile::SharedKind; +} + +template void SharedFileExtended::parseForAdlt() { + this->parse(); + parseDynamics(); + parseElfSymTab(); + + bool isDebug = false; + if (!isDebug) + return; + + const ELFFile obj = this->getObj(); + ArrayRef objSections = this->template getELFShdrs(); + StringRef shstrtab = getShStrTab(objSections); + + lld::outs() << "Parse symbols from .symtab:\n"; + const Elf_Shdr *elfSymTab = *obj.getSection(symTabSecIdx); + StringRef strTable = *obj.getStringTableForSymtab(*elfSymTab); + + auto eSyms = *obj.symbols(elfSymTab); + this->symbols.resize(this->symbols.size() + eSyms.size()); + for (const Elf_Sym &sym : eSyms) { + if (!sym.isDefined()) + continue; + + auto rawSec = obj.getSection(sym.st_shndx); + if (rawSec.takeError()) + continue; + if (isDebug) + traceElfSymbol(sym, strTable); + } + if (isDebug) + lld::outs() << '\n'; + + lld::outs() << "Parse offsets of some sections:\n"; + for (const Elf_Shdr &sec : objSections) { + auto name = check(obj.getSectionName(sec, shstrtab)); + if (name == ".init_array" || name == ".fini_array" || name == ".data") + traceElfSection(sec); + } + lld::outs() << '\n'; +} + +template void SharedFileExtended::postParseForAdlt() { + this->initializeLocalSymbols(); + this->postParse(); + resolveDuplicatesForAdlt(); +} + +template +StringRef SharedFileExtended::addAdltPrefix(StringRef input) { + return markItemForAdlt(input, soName); +} + +template +bool SharedFileExtended::addAdltPrefix(Symbol *s) { + StringRef newName = markItemForAdlt(s->getName(), soName); + if (s->getName() == newName) + return false; + s->setName(newName); + return true; +} + +template +Defined* SharedFileExtended::findSectionSymbol(uint64_t offset) { + auto predRange = [=](Symbol *sym) { + if (!sym || sym->isUndefined() || !sym->isSection()) + return false; + const Defined *d = cast(sym); + uint64_t low = d->section->address; + uint64_t high = low + d->section->size; + return (offset >= low) && (offset < high); + }; + + auto i = this->allSymbols.begin(); + auto e = this->allSymbols.end(); + auto ret = std::find_if(i, e, predRange); + if (ret != e) // item was found + return cast(*ret); + + return nullptr; +} + +template +StringRef SharedFileExtended::getShStrTab(ArrayRef elfSections) { + return CHECK(this->getObj().getSectionStringTable(elfSections), this); +} + +template +void SharedFileExtended::traceElfSymbol(const Elf_Sym &sym, + StringRef strTable) const { + const ELFFile obj = this->getObj(); + auto rawSec = obj.getSection(sym.st_shndx); + auto parsedSec = !rawSec.takeError() ? *obj.getSection(sym.st_shndx) : nullptr; + lld::outs() << "File: " << soName << " symName: " << *sym.getName(strTable) + << " val: 0x" << Twine::utohexstr(sym.st_value) << " sec of sym: " + << (parsedSec ? *obj.getSectionName(*parsedSec) : "unknown!") + << " sym type: 0x" << Twine::utohexstr(sym.getType()) + << " sym binding: 0x" << Twine::utohexstr(sym.getBinding()) + << '\n'; +} + +template +void SharedFileExtended::traceElfSection(const Elf_Shdr &sec) const { + const ELFFile obj = this->getObj(); + + auto secName = *obj.getSectionName(sec); + lld::outs() << "File: " << soName << " sec: " << secName << " sec addr: 0x" + << Twine::utohexstr(sec.sh_addr) << " sec offs: 0x" + << Twine::utohexstr(sec.sh_offset) << " sec ent size: 0x" + << Twine::utohexstr(sec.sh_entsize) << '\n'; +} + +template +void SharedFileExtended::traceSymbol(const Symbol& sym) const { + lld::outs() << "File: " << soName << " symName: " << sym.getName(); + if (!sym.isDefined()) { + lld::outs() << '\n'; + return; + } + auto &d = cast(sym); + lld::outs() << " val: 0x" << Twine::utohexstr(d.value) << " sec of sym: " + << (d.section ? d.section->name : "unknown!") + << " sym type: 0x" << Twine::utohexstr(sym.type) + << " sym binding: 0x" << Twine::utohexstr(sym.binding) + << '\n'; +} + +template +void SharedFileExtended::traceSection(const SectionBase& sec) const { + lld::outs() << "File: " << soName << " sec: " << sec.name << " sec addr: 0x" + << Twine::utohexstr(sec.address) << " sec offs: 0x" + << Twine::utohexstr(sec.getOffset(0)) << " sec ent size: 0x" + << Twine::utohexstr(sec.entsize) << '\n'; +} + +template +Symbol& SharedFileExtended::getSymbol(uint32_t symbolIndex) const { + if (symbolIndex >= this->symbols.size()) + return getSymbolFromElfSymTab(symbolIndex); + return *this->symbols[symbolIndex]; +} + +template +Defined *SharedFileExtended::findSymbolByValue(uint32_t value) const { + bool isDebug = false; + auto predValues = [=](Symbol *sym) { + if (!sym || sym->isUndefined()) + return false; + const Defined *d = cast(sym); + bool goodVal = d->section->address + d->value == value; + bool goodFile = d->file == this; + bool isGood = goodVal && goodFile; + if (isDebug && isGood) + lld::outs() << "good\n"; + return isGood; + }; + + // filter by value for .dynsym, else for .symtab + SmallVector matches; + auto i = this->symbols.begin(); + auto e = this->symbols.end(); + std::copy_if(i, e, std::back_inserter(matches), predValues); + + if (matches.empty()) { + auto i = this->allSymbols.begin(); + auto e = this->allSymbols.end(); + std::copy_if(i, e, std::back_inserter(matches), predValues); + } + + if (matches.empty()) { + // warn("Symbol not found! Value: 0x" + Twine::utohexstr(value)); + return nullptr; + } + + // filter by type else return first item + i = matches.begin(); + e = matches.end(); + auto predTypes = [=](Symbol *sym) { + return sym->type == STT_FUNC || sym->type == STT_OBJECT; + }; + auto ret = std::find_if(i, e, predTypes); + if (ret != e) // item was found + return cast(*ret); + + // todo fix it + return cast(*i); +} + +template void SharedFileExtended::parseDynamics() { + const ELFFile obj = this->getObj(); + ArrayRef sections = this->template getELFShdrs(); + + ArrayRef dynamicTags; + const Elf_Shdr *versymSec = nullptr; + const Elf_Shdr *verdefSec = nullptr; + const Elf_Shdr *verneedSec = nullptr; + + // parse external lib deps + int secIdx = 0; + for (const Elf_Shdr &sec : sections) { + switch (sec.sh_type) { + case SHT_DYNAMIC: + dynamicTags = + CHECK(obj.template getSectionContentsAsArray(sec), this); + break; + case SHT_DYNSYM: + dynSymSecIdx = secIdx; + break; + case SHT_SYMTAB: + symTabSecIdx = secIdx; + break; + case SHT_SYMTAB_SHNDX: + symTabShndxSecIdx = secIdx; + break; + case SHT_GNU_versym: + versymSec = &sec; + break; + case SHT_GNU_verdef: + verdefSec = &sec; + break; + case SHT_GNU_verneed: + verneedSec = &sec; + break; + } + secIdx++; + } + + if (versymSec && this->numELFSyms == 0) { + error("SHT_GNU_versym should be associated with symbol table"); + return; + } + + // Search for a DT_SONAME tag to initialize this->soName. + for (const Elf_Dyn &dyn : dynamicTags) { + if (dyn.d_tag == DT_NEEDED) { + uint64_t val = dyn.getVal(); + if (val >= this->stringTable.size()) + fatal(toString(this) + ": invalid DT_NEEDED entry"); + dtNeeded.push_back(this->stringTable.data() + val); + } else if (dyn.d_tag == DT_SONAME) { + uint64_t val = dyn.getVal(); + if (val >= this->stringTable.size()) + fatal(toString(this) + ": invalid DT_SONAME entry"); + soName = this->stringTable.data() + val; + } + } + + verdefs = parseVerdefs(obj.base(), verdefSec); + std::vector verneeds = parseVerneed(obj, verneedSec); + + // Parse ".gnu.version" section which is a parallel array for the symbol + // table. If a given file doesn't have a ".gnu.version" section, we use + // VER_NDX_GLOBAL. + size_t size = this->numELFSyms - this->firstGlobal; + std::vector versyms(size, VER_NDX_GLOBAL); + if (versymSec) { + ArrayRef versym = + CHECK(obj.template getSectionContentsAsArray(*versymSec), + this) + .slice(this->firstGlobal); + for (size_t i = 0; i < size; ++i) + versyms[i] = versym[i].vs_index; + } + + // System libraries can have a lot of symbols with versions. Using a + // fixed buffer for computing the versions name (foo@ver) can save a + // lot of allocations. + SmallString<0> versionedNameBuffer; + + // Add symbols to the symbol table. + SymbolTable &symtab = *elf::symtab; + ArrayRef syms = this->template getGlobalELFSyms(); + for (size_t i = 0, e = syms.size(); i != e; ++i) { + const Elf_Sym &sym = syms[i]; + + // ELF spec requires that all local symbols precede weak or global + // symbols in each symbol table, and the index of first non-local symbol + // is stored to sh_info. If a local symbol appears after some non-local + // symbol, that's a violation of the spec. + StringRef name = CHECK(sym.getName(this->stringTable), this); + if (sym.getBinding() == STB_LOCAL) { + warn("found local symbol '" + name + + "' in global part of symbol table in file " + toString(this)); + continue; + } + + uint16_t idx = versyms[i] & ~VERSYM_HIDDEN; + if (sym.isUndefined()) { + // For unversioned undefined symbols, VER_NDX_GLOBAL makes more sense but + // as of binutils 2.34, GNU ld produces VER_NDX_LOCAL. + if (idx != VER_NDX_LOCAL && idx != VER_NDX_GLOBAL) { + if (idx >= verneeds.size()) { + error("corrupt input file: version need index " + Twine(idx) + + " for symbol " + name + " is out of bounds\n>>> defined in " + + toString(this)); + continue; + } + StringRef verName = this->stringTable.data() + verneeds[idx]; + versionedNameBuffer.clear(); + name = saver().save( + (name + "@" + verName).toStringRef(versionedNameBuffer)); + } + Symbol *s = symtab.addSymbol( + Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); + s->exportDynamic = true; + if (s->isUndefined() && sym.getBinding() != STB_WEAK && + config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore) + requiredSymbols.push_back(s); + continue; + } + // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly + // assigns VER_NDX_LOCAL to this section global symbol. Here is a + // workaround for this bug. + if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL && + name == "_gp_disp") + continue; + + uint32_t alignment = getAlignment(sections, sym); + if (!(versyms[i] & VERSYM_HIDDEN)) { + auto *s = symtab.addSymbol( + SharedSymbol{*this, name, sym.getBinding(), sym.st_other, + sym.getType(), sym.st_value, sym.st_size, alignment}); + if (s->file == this) + s->verdefIndex = idx; + } + + // Also add the symbol with the versioned name to handle undefined symbols + // with explicit versions. + if (idx == VER_NDX_GLOBAL) + continue; + + if (idx >= verdefs.size() || idx == VER_NDX_LOCAL) { + error("corrupt input file: version definition index " + Twine(idx) + + " for symbol " + name + " is out of bounds\n>>> defined in " + + toString(this)); + continue; + } + + StringRef verName = + this->stringTable.data() + + reinterpret_cast(verdefs[idx])->getAux()->vda_name; + versionedNameBuffer.clear(); + name = (name + "@" + verName).toStringRef(versionedNameBuffer); + auto *s = symtab.addSymbol( + SharedSymbol{*this, saver().save(name), sym.getBinding(), sym.st_other, + sym.getType(), sym.st_value, sym.st_size, alignment}); + if (s->file == this) + s->verdefIndex = idx; + } +} + +template void SharedFileExtended::parseElfSymTab() { + const ELFFile obj = this->getObj(); + ArrayRef eSections = this->template getELFShdrs(); + + // Find a symbol table. + const Elf_Shdr *eSymtabSec = findSection(eSections, SHT_SYMTAB); + if (!eSymtabSec) + return; + + eFirstGlobal = eSymtabSec->sh_info; + + ArrayRef eSyms = CHECK(obj.symbols(eSymtabSec), this); + auto numESyms = uint32_t(eSyms.size()); + auto eStringTable = CHECK(obj.getStringTableForSymtab(*eSymtabSec, eSections), this); + this->allSymbols.resize(numESyms); + + for (size_t i = 0; i < numESyms; i++) { + const Elf_Sym &eSym = eSyms[i]; + StringRef name(eStringTable.data() + eSym.st_name); + auto bind = eSym.getBinding(); + auto type = eSym.getType(); + auto other = eSym.st_other; + if (eSym.isDefined()) { + auto secIdx = eSym.st_shndx; + if (LLVM_UNLIKELY(secIdx == SHN_XINDEX)) + secIdx = check( + getExtendedSymbolTableIndex(eSym, i, this->getShndxTable())); + else if (secIdx >= SHN_LORESERVE) + secIdx = 0; + const Elf_Shdr *eSec = *this->getObj().getSection(secIdx); + InputSectionBase *sec = this->sections[secIdx]; + auto val = eSym.st_value - eSec->sh_addr; + this->allSymbols[i] = + make(this, name, bind, other, type, val, eSym.st_size, sec); + } else { + this->allSymbols[i] = make(this, name, bind, other, type, + /*discardedSecIdx=*/i); + } + } +} + static ELFKind getBitcodeELFKind(const Triple &t) { if (t.isLittleEndian()) return t.isArch64Bit() ? ELF64LEKind : ELF32LEKind; @@ -1733,6 +2239,27 @@ ELFFileBase *elf::createObjFile(MemoryBufferRef mb, StringRef archiveName, return f; } +ELFFileBase *elf::createSharedFileExtended(MemoryBufferRef mb, StringRef soName) { + ELFFileBase *f; + switch (getELFKind(mb, soName)) { + case ELF32LEKind: + f = make>(mb, soName); + break; + case ELF32BEKind: + f = make>(mb, soName); + break; + case ELF64LEKind: + f = make>(mb, soName); + break; + case ELF64BEKind: + f = make>(mb, soName); + break; + default: + llvm_unreachable("getELFKind"); + } + return f; +} + template void ObjFile::parseLazy() { const ArrayRef eSyms = this->getELFSyms(); SymbolTable &symtab = *elf::symtab; @@ -1785,3 +2312,8 @@ template void SharedFile::parse(); template void SharedFile::parse(); template void SharedFile::parse(); template void SharedFile::parse(); + +template class elf::SharedFileExtended; +template class elf::SharedFileExtended; +template class elf::SharedFileExtended; +template class elf::SharedFileExtended; \ No newline at end of file diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index a24e664a7e16..6712c4841d65 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -76,18 +76,31 @@ public: // Returns sections. It is a runtime error to call this function // on files that don't have the notion of sections. ArrayRef getSections() const { - assert(fileKind == ObjKind || fileKind == BinaryKind); + if (config->adlt) + assert(fileKind == SharedKind); + else + assert(fileKind == ObjKind || fileKind == BinaryKind); return sections; } // Returns object file symbols. It is a runtime error to call this // function on files of other types. ArrayRef getSymbols() const { - assert(fileKind == BinaryKind || fileKind == ObjKind || - fileKind == BitcodeKind); + if (config->adlt) + assert(fileKind == SharedKind); + else + assert(fileKind == BinaryKind || fileKind == ObjKind || + fileKind == BitcodeKind); return symbols; } + // ADLT beg + ArrayRef getAllSymbols() const { return allSymbols; } + + SmallVector allSymbols; + // ADLT end + + // Get filename to use for linker script processing. StringRef getNameForScript() const; @@ -161,6 +174,7 @@ private: class ELFFileBase : public InputFile { public: ELFFileBase(Kind k, MemoryBufferRef m); + virtual ~ELFFileBase() {} static bool classof(const InputFile *f) { return f->isElf(); } template llvm::object::ELFFile getObj() const { @@ -169,7 +183,7 @@ public: StringRef getStringTable() const { return stringTable; } - ArrayRef getLocalSymbols() { + virtual ArrayRef getLocalSymbols() { if (symbols.empty()) return {}; return llvm::makeArrayRef(symbols).slice(1, firstGlobal - 1); @@ -224,6 +238,7 @@ public: ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) { this->archiveName = archiveName; } + virtual ~ObjFile() {} void parse(bool ignoreComdats = false); void parseLazy(); @@ -231,7 +246,7 @@ public: StringRef getShtGroupSignature(ArrayRef sections, const Elf_Shdr &sec); - Symbol &getSymbol(uint32_t symbolIndex) const { + virtual Symbol &getSymbol(uint32_t symbolIndex) const { if (symbolIndex >= this->symbols.size()) fatal(toString(this) + ": invalid symbol index"); return *this->symbols[symbolIndex]; @@ -247,6 +262,8 @@ public: llvm::Optional getDILineInfo(InputSectionBase *, uint64_t); llvm::Optional> getVariableLoc(StringRef name); + ArrayRef getShndxTable(); + // Name of source file obtained from STT_FILE symbol value, // or empty string if there is no such symbol in object file // symbol table. @@ -277,6 +294,8 @@ public: void initializeLocalSymbols(); void postParse(); + bool isPatchedSecName = true; // ADLT + private: void initializeSections(bool ignoreComdats, const llvm::object::ELFFile &obj); @@ -364,6 +383,81 @@ private: const typename ELFT::Shdr *sec); }; + +// ADLT +template +class SharedFileExtended : public ObjFile { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + +public: + SharedFileExtended(MemoryBufferRef mb, StringRef soName); + + static bool classof(const InputFile *f) { + return f->kind() == InputFile::SharedKind; + } + + void parseForAdlt(); + void postParseForAdlt(); + + StringRef addAdltPrefix(StringRef input); + bool addAdltPrefix(Symbol *s); + + Defined* findSectionSymbol(uint64_t offset); + + ArrayRef getLocalSymbols() override { + if (this->allSymbols.empty()) + return {}; + return llvm::makeArrayRef(this->allSymbols).slice(1, eFirstGlobal - 1); + } + + Symbol &getSymbol(uint32_t symbolIndex) const override; + Defined *findSymbolByValue(uint32_t value) const; + + Symbol &getSymbolFromElfSymTab(uint32_t symbolIndex) const { + if (symbolIndex >= this->allSymbols.size()) + fatal(toString(this) + ": invalid symbol index"); + return *this->allSymbols[symbolIndex]; + } + + void traceElfSymbol(const Elf_Sym &sym, StringRef strTable) const; + void traceElfSection(const Elf_Shdr &sec) const; + + void traceSymbol(const Symbol& sym) const; + void traceSection(const SectionBase& sec) const; + + int dynSymSecIdx = 0; + int symTabSecIdx = 0; + int symTabShndxSecIdx = 0; + int eFirstGlobal = 0; + + // This is actually a vector of Elf_Verdef pointers. + SmallVector verdefs; + // If the output file needs Elf_Verneed data structures for this file, this is + // a vector of Elf_Vernaux version identifiers that map onto the entries in + // Verdefs, otherwise it is empty. + SmallVector vernauxs; + static unsigned vernauxNum; + SmallVector dtNeeded; + StringRef soName; + StringRef simpleSoName; + // Used for --as-needed + // bool isNeeded; + // Non-weak undefined symbols which are not yet resolved when the SO is + // parsed. Only filled for `--no-allow-shlib-undefined`. + SmallVector requiredSymbols; + +private: + void parseDynamics(); // SharedFile compability + void parseElfSymTab(); // ObjectFile compability + + void resolveDuplicatesForAdlt(); + + StringRef getShStrTab(ArrayRef elfSections); + + std::vector parseVerneed(const llvm::object::ELFFile &obj, + const typename ELFT::Shdr *sec); +}; + class BinaryFile : public InputFile { public: explicit BinaryFile(MemoryBufferRef m) : InputFile(BinaryKind, m) {} @@ -374,6 +468,8 @@ public: ELFFileBase *createObjFile(MemoryBufferRef mb, StringRef archiveName = "", bool lazy = false); +ELFFileBase *createSharedFileExtended(MemoryBufferRef mb, StringRef soName = ""); + std::string replaceThinLTOSuffix(StringRef path); } // namespace elf diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 8fe36eca6a4b..8e772a6a683e 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -347,8 +347,9 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { for (const RelTy &rel : rels) { RelType type = rel.getType(config->isMips64EL); - const ObjFile *file = getFile(); - Symbol &sym = file->getRelocTargetSym(rel); + const ELFFileBase *file = cast_or_null(file); + Symbol &sym = config->adlt ? getSharedFile()->getRelocTargetSym(rel) + : getFile()->getRelocTargetSym(rel); auto *p = reinterpret_cast(buf); buf += sizeof(RelTy); @@ -383,7 +384,7 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { uint32_t secIdx = cast(sym).discardedSecIdx; Elf_Shdr_Impl sec = file->template getELFShdrs()[secIdx]; warn("relocation refers to a discarded section: " + - CHECK(file->getObj().getSectionName(sec), file) + + CHECK(file->getObj().getSectionName(sec), file) + "\n>>> referenced by " + getObjMsg(p->r_offset)); } p->setSymbolAndType(0, 0, false); @@ -844,7 +845,19 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef rels) { if (!RelTy::IsRela) addend += target.getImplicitAddend(bufLoc, type); - Symbol &sym = getFile()->getRelocTargetSym(rel); + Symbol &sym = config->adlt ? getSharedFile()->getSymbol( + rel.getSymbol(config->isMips64EL)) + : getFile()->getRelocTargetSym(rel); + if (config->adlt) { // TODO improve + switch (type) { + case R_AARCH64_RELATIVE: + case R_AARCH64_GLOB_DAT: + case R_AARCH64_JUMP_SLOT: + return; + default: + break; + } + } RelExpr expr = target.getRelExpr(type, sym, bufLoc); if (expr == R_NONE) continue; @@ -948,7 +961,9 @@ static void relocateNonAllocForRelocatable(InputSection *sec, uint8_t *buf) { template void InputSectionBase::relocate(uint8_t *buf, uint8_t *bufEnd) { - if ((flags & SHF_EXECINSTR) && LLVM_UNLIKELY(getFile()->splitStack)) + if (flags & SHF_EXECINSTR && + (config->adlt ? LLVM_UNLIKELY(getSharedFile()->splitStack) + : LLVM_UNLIKELY(getFile()->splitStack))) adjustSplitStackFunctionPrologues(buf, bufEnd); if (flags & SHF_ALLOC) { diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index d1b889750bbd..b9f4541e77a9 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -28,6 +28,7 @@ class Defined; struct Partition; class SyntheticSection; template class ObjFile; +template class SharedFileExtended; class OutputSection; extern std::vector partitions; @@ -74,6 +75,9 @@ public: uint32_t type; uint32_t link; uint32_t info; + uint64_t address = 0; // store input sec addr for ADLT + uint64_t size = 0; // store input sec size for ADLT + OutputSection *getOutputSection(); const OutputSection *getOutputSection() const { @@ -133,6 +137,11 @@ public: return cast_or_null>(file); } + template + SharedFileExtended *getSharedFile() const { + return cast_or_null>(file); + } + // Used by --optimize-bb-jumps and RISC-V linker relaxation temporarily to // indicate the number of bytes which is not counted in the size. This should // be reset to zero after uses. @@ -392,7 +401,7 @@ private: template void copyShtGroup(uint8_t *buf); }; -static_assert(sizeof(InputSection) <= 160, "InputSection is too big"); +static_assert(sizeof(InputSection) <= 180, "InputSection is too big"); inline bool isDebugSection(const InputSectionBase &sec) { return (sec.flags & llvm::ELF::SHF_ALLOC) == 0 && diff --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp index 893511929a3a..51353287c90e 100644 --- a/lld/ELF/MapFile.cpp +++ b/lld/ELF/MapFile.cpp @@ -55,7 +55,8 @@ static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma, // Returns a list of all symbols that we want to print out. static std::vector getSymbols() { std::vector v; - for (ELFFileBase *file : ctx->objectFiles) + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) for (Symbol *b : file->getSymbols()) if (auto *dr = dyn_cast(b)) if (!dr->isSection() && dr->section && dr->section->isLive() && @@ -224,7 +225,8 @@ static void writeMapFile(raw_fd_ostream &os) { static void writeCref(raw_fd_ostream &os) { // Collect symbols and files. MapVector> map; - for (ELFFileBase *file : ctx->objectFiles) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) { for (Symbol *sym : file->getSymbols()) { if (isa(sym)) map[sym].insert(file); diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp index 12a23390e663..61be1e13287f 100644 --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -345,7 +345,8 @@ template void MarkLive::mark() { // to from __start_/__stop_ symbols because there will only be one set of // symbols for the whole program. template void MarkLive::moveToMain() { - for (ELFFileBase *file : ctx->objectFiles) + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) for (Symbol *s : file->getSymbols()) if (auto *d = dyn_cast(s)) if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section && diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 4e6c20f5c7f6..46943047df31 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -375,6 +375,8 @@ defm section_start: Eq<"section-start", "Set address of section">, def shared: F<"shared">, HelpText<"Build a shared object">; +def adlt: F<"adlt">, HelpText<"Build adlt library">; + defm soname: Eq<"soname", "Set DT_SONAME">; defm sort_section: diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 277c57505bb2..3f79aea7ba7e 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -455,6 +455,9 @@ private: void processAux(RelExpr expr, RelType type, uint64_t offset, Symbol &sym, int64_t addend) const; template void scanOne(RelTy *&i); + + template + void handleRelativeReloc(const RelTy &rel, const Symbol& sym, uint64_t offset); }; } // namespace @@ -1294,7 +1297,8 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, template void RelocationScanner::scanOne(RelTy *&i) { const RelTy &rel = *i; uint32_t symIndex = rel.getSymbol(config->isMips64EL); - Symbol &sym = sec.getFile()->getSymbol(symIndex); + auto file = config->adlt ? sec.getSharedFile() : sec.getFile(); + Symbol &sym = file->getSymbol(symIndex); RelType type; // Deal with MIPS oddity. @@ -1312,11 +1316,84 @@ template void RelocationScanner::scanOne(RelTy *&i) { // Error if the target symbol is undefined. Symbol index 0 may be used by // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them. - if (sym.isUndefined() && symIndex != 0 && + if (symIndex != 0 && sym.isUndefined() && maybeReportUndefined(cast(sym), sec, offset)) return; const uint8_t *relocatedAddr = sec.rawData.begin() + offset; + if (config->adlt) { + auto file = sec.getSharedFile(); + bool isDebug = false; + + auto saveSymIfNeeded = [](const Defined& d) { + auto found = elf::symtab->find(d.getName()); + if (!found) + in.symTab->addSymbol(elf::symtab->addSymbol(d)); + }; + + auto handleRelativeReloc = [&]() { + RelType type = R_AARCH64_ABS64; + RelExpr expr = target.getRelExpr(type, sym, relocatedAddr); + uint64_t addend = computeAddend(rel, expr, sym.isLocal()); + StringRef title = "handle relative reloc: "; + if (isDebug) + lld::outs() << title << "offset: 0x" + Twine::utohexstr(offset) + << " addend: 0x" + Twine::utohexstr(addend) + "\n" ; + + // parse offset (where) + Defined *foundSymbol = file->findSymbolByValue(offset); + if (!foundSymbol) { + fatal(title + "sym not found! offset: 0x" + Twine::utohexstr(offset) + "\n"); + return; + } + if (!foundSymbol->exportDynamic) + file->addAdltPrefix(cast(foundSymbol)); + Defined &symWhere = *foundSymbol; + saveSymIfNeeded(symWhere); + + // parse addent (replacement) + foundSymbol = file->findSymbolByValue(addend); + if (!foundSymbol) { + Defined *d = file->findSectionSymbol(addend); + if (!d) + fatal(title + "sym not found! addend: 0x" + Twine::utohexstr(addend) + "\n"); + foundSymbol = d; + } + Defined &inputSym = *foundSymbol; + addend = inputSym.isSection() ? (addend - inputSym.section->address) : 0; + + if (isDebug) { + lld::outs() << "handle relative reloc: inputSym: "; + file->traceSymbol(inputSym); + } + if (!foundSymbol->exportDynamic) + file->addAdltPrefix(&inputSym); + saveSymIfNeeded(inputSym); + + // parse section from offset of relative reloc + addRelativeReloc(cast(*symWhere.section), + symWhere.value, inputSym, addend, expr, type); + }; + switch (type) { + case R_AARCH64_RELATIVE: + handleRelativeReloc(); + return; + case R_AARCH64_GLOB_DAT: { + Symbol &s = file->getSymbol(symIndex); + s.needsGot = 1; + return; + } + case R_AARCH64_JUMP_SLOT: { + Symbol &s = file->getSymbol(symIndex); + if (s.isUndefined()) + s.needsPlt = 1; + return; + } + default: + break; + } + } + RelExpr expr = target.getRelExpr(type, sym, relocatedAddr); // Ignore R_*_NONE and other marker relocations. @@ -1326,6 +1403,72 @@ template void RelocationScanner::scanOne(RelTy *&i) { // Read an addend. int64_t addend = computeAddend(rel, expr, sym.isLocal()); + if (config->adlt) { + auto file = sec.getSharedFile(); + auto traceSym = [&](StringRef debugTitle, Symbol& sym) { + lld::outs() << debugTitle; + lld::outs() << "type: " << lld::toString(type) << " sym: "; + Symbol* p = &sym; + if (!p) { + lld::outs() << "unknown!\n"; + return; + } + file->traceSymbol(sym); + if (!sym.isDefined()) + return; + lld::outs() << "sec: "; + file->traceSection(*(cast(sym).section)); + }; + + bool isDebug = false; + auto strType = lld::toString(type); + auto debugTitle = "[RelocationScanner::handle relocs]: " + strType + ": "; + if (isDebug) { + traceSym(debugTitle, sym); + lld::outs() << " offset before: 0x" << Twine::utohexstr(offset) << '\n'; + } + if (offset + sec.address) + offset -= sec.address; + + Symbol *s = sym.getName().size() ? elf::symtab->find(sym.getName()) : &sym; + if (!s) // if symbol was not found in elf::symtab + s = &sym; + auto pushReloc = [&]() { + sec.relocations.push_back({expr, type, offset, addend, s}); + }; + + switch (type) { + // abs relocs + case R_AARCH64_ABS32: + case R_AARCH64_ABS64: + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + pushReloc(); + return; + // plt relocs + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: { + if (s && s->isDefined()) + expr = R_PC; // prev: R_PLT_PC + pushReloc(); + return; + } + + // got relocs + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + pushReloc(); + return; + default: + fatal("Unhandled reloc: " + strType); + break; + } + if (isDebug) + lld::outs() << " offset after: 0x" << Twine::utohexstr(offset) << "\n\n"; + } + if (config->emachine == EM_PPC64) { // We can separate the small code model relocations into 2 categories: // 1) Those that access the compiler generated .toc sections. @@ -1628,7 +1771,8 @@ void elf::postScanRelocations() { return; if (!sym.needsDynReloc()) return; - sym.allocateAux(); + if (!sym.allocateAux()) + return; if (sym.needsGot) addGotEntry(sym); @@ -1720,7 +1864,8 @@ void elf::postScanRelocations() { // Local symbols may need the aforementioned non-preemptible ifunc and GOT // handling. They don't need regular PLT. - for (ELFFileBase *file : ctx->objectFiles) + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) for (Symbol *sym : file->getLocalSymbols()) fn(*sym); } @@ -2168,7 +2313,8 @@ bool ThunkCreator::createThunks(uint32_t pass, // original target so another Thunk can be generated. if (pass > 0 && normalizeExistingThunk(rel, src)) continue; - + if (!rel.sym) + continue; // ADLT fix if (!target->needsThunk(rel.expr, rel.type, isec->file, src, *rel.sym, rel.addend)) continue; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 657c19a00ba3..d65ebd486dd2 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -307,10 +307,12 @@ public: return needsCopy || needsGot || needsPlt || needsTlsDesc || needsTlsGd || needsTlsGdToIe || needsGotDtprel || needsTlsIe; } - void allocateAux() { - assert(auxIdx == uint32_t(-1)); + bool allocateAux() { + if (auxIdx != uint32_t(-1)) + return false; auxIdx = symAux.size(); symAux.emplace_back(); + return true; } bool isSection() const { return type == llvm::ELF::STT_SECTION; } diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index f298b83bda0e..8a7208698c6d 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -1397,9 +1397,23 @@ DynamicSection::computeContents() { addInt(config->enableNewDtags ? DT_RUNPATH : DT_RPATH, part.dynStrTab->addString(config->rpath)); - for (SharedFile *file : ctx->sharedFiles) - if (file->isNeeded) - addInt(DT_NEEDED, part.dynStrTab->addString(file->soName)); + if (config->adlt) { + StringRef prevNeeded; + for (InputFile *file : ctx->sharedFilesExtended) { + auto *f = cast>(file); + for (size_t i = 0; i < f->dtNeeded.size(); i++) { + StringRef needed = f->dtNeeded[i]; + if (prevNeeded == needed) + continue; + addInt(DT_NEEDED, part.dynStrTab->addString(needed)); + prevNeeded = needed; + } + } + addInt(DT_SONAME, part.dynStrTab->addString(config->outputFile)); + } else + for (SharedFile *file : ctx->sharedFiles) + if (file->isNeeded) + addInt(DT_NEEDED, part.dynStrTab->addString(file->soName)); if (isMain) { if (!config->soName.empty()) @@ -1536,17 +1550,40 @@ DynamicSection::computeContents() { addInSec(DT_HASH, *part.hashTab); if (isMain) { - if (Out::preinitArray) { - addInt(DT_PREINIT_ARRAY, Out::preinitArray->addr); - addInt(DT_PREINIT_ARRAYSZ, Out::preinitArray->size); - } - if (Out::initArray) { - addInt(DT_INIT_ARRAY, Out::initArray->addr); - addInt(DT_INIT_ARRAYSZ, Out::initArray->size); - } - if (Out::finiArray) { - addInt(DT_FINI_ARRAY, Out::finiArray->addr); - addInt(DT_FINI_ARRAYSZ, Out::finiArray->size); + if (config->adlt) { + auto findSection = [](StringRef name, unsigned partition = 1) { + for (SectionCommand *cmd : script->sectionCommands) + if (auto *osd = dyn_cast(cmd)) + if (osd->osec.name == name && osd->osec.partition == partition) + return &osd->osec; + return (OutputSection *)nullptr; + }; + for (InputFile *file : ctx->sharedFilesExtended) { + auto *f = cast>(file); + auto initArray = findSection(f->addAdltPrefix(".init_array")); + auto finiArray = findSection(f->addAdltPrefix(".fini_array")); + if (initArray) { + addInt(DT_INIT_ARRAY, initArray->addr); + addInt(DT_INIT_ARRAYSZ, initArray->size); + } + if (finiArray) { + addInt(DT_FINI_ARRAY, finiArray->addr); + addInt(DT_FINI_ARRAYSZ, finiArray->size); + } + } + } else { + if (Out::preinitArray) { + addInt(DT_PREINIT_ARRAY, Out::preinitArray->addr); + addInt(DT_PREINIT_ARRAYSZ, Out::preinitArray->size); + } + if (Out::initArray) { + addInt(DT_INIT_ARRAY, Out::initArray->addr); + addInt(DT_INIT_ARRAYSZ, Out::initArray->size); + } + if (Out::finiArray) { + addInt(DT_FINI_ARRAY, Out::finiArray->addr); + addInt(DT_FINI_ARRAYSZ, Out::finiArray->size); + } } if (Symbol *b = symtab->find(config->init)) @@ -3410,7 +3447,8 @@ template void elf::splitSections() { llvm::TimeTraceScope timeScope("Split sections"); // splitIntoPieces needs to be called on each MergeInputSection // before calling finalizeContents(). - parallelForEach(ctx->objectFiles, [](ELFFileBase *file) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + parallelForEach(files, [](ELFFileBase *file) { for (InputSectionBase *sec : file->getSections()) { if (!sec) continue; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 5ec2d85f64e8..4ed885c2edd4 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -88,6 +88,9 @@ public: virtual bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const; + virtual void deRelocate(uint8_t *loc, const Relocation &rel, + uint64_t *val) const {}; // ADLT + virtual void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const = 0; void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const { diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index e27e656d43c7..4f4658d7cc24 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -628,7 +628,8 @@ template static void markUsedLocalSymbols() { // See MarkLive::resolveReloc(). if (config->gcSections) return; - for (ELFFileBase *file : ctx->objectFiles) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) { ObjFile *f = cast>(file); for (InputSectionBase *s : f->getSections()) { InputSection *isec = dyn_cast_or_null(s); @@ -699,7 +700,8 @@ template void Writer::copyLocalSymbols() { llvm::TimeTraceScope timeScope("Add local symbols"); if (config->copyRelocs && config->discard != DiscardPolicy::None) markUsedLocalSymbols(); - for (ELFFileBase *file : ctx->objectFiles) { + auto files = /*config->adlt ? ctx->sharedFilesExtended :*/ ctx->objectFiles; + for (ELFFileBase *file : files) { for (Symbol *b : file->getLocalSymbols()) { assert(b->isLocal() && "should have been caught in initializeSymbols()"); auto *dr = dyn_cast(b); @@ -1303,7 +1305,8 @@ static DenseMap buildSectionOrder() { for (Symbol *sym : symtab->symbols()) addSym(*sym); - for (ELFFileBase *file : ctx->objectFiles) + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (ELFFileBase *file : files) for (Symbol *sym : file->getLocalSymbols()) addSym(*sym); @@ -1719,7 +1722,8 @@ template void Writer::finalizeAddressDependentContent() { // block sections, input sections can shrink when the jump instructions at // the end of the section are relaxed. static void fixSymbolsAfterShrinking() { - for (InputFile *File : ctx->objectFiles) { + auto files = config->adlt ? ctx->sharedFilesExtended : ctx->objectFiles; + for (InputFile *File : files) { parallelForEach(File->getSymbols(), [&](Symbol *Sym) { auto *def = dyn_cast(Sym); if (!def) @@ -1937,9 +1941,19 @@ template void Writer::finalizeSections() { // determine if it needs special treatment, such as creating GOT, PLT, // copy relocations, etc. Note that relocations for non-alloc sections are // directly processed by InputSection::relocateNonAlloc. - for (InputSectionBase *sec : inputSections) - if (sec->isLive() && isa(sec) && (sec->flags & SHF_ALLOC)) - scanRelocations(*sec); + if (config->adlt) + for (InputFile *file : ctx->sharedFilesExtended) + for (InputSectionBase *sec : file->getSections()) { + bool isExclusive = sec && (sec->type == SHT_NULL || + sec->name.startswith(".got.plt") || + !sec->name.startswith(".debug_")); + if (isExclusive) + scanRelocations(*sec); + } + if (!config->adlt) + for (InputSectionBase *sec : inputSections) + if (sec->isLive() && isa(sec) && (sec->flags & SHF_ALLOC)) + scanRelocations(*sec); for (Partition &part : partitions) { for (EhInputSection *sec : part.ehFrame->sections) scanRelocations(*sec); @@ -2474,6 +2488,9 @@ template void Writer::fixSectionAlignments() { OutputSection *cmd = p->firstSec; if (!cmd) return; + if (config->adlt &&cmd->name == ".text") { + lld::outs() << "Writer::fixSectionAlignments()\n"; + } cmd->alignExpr = [align = cmd->alignment]() { return align; }; if (!cmd->addrExpr) { // Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid @@ -2611,6 +2628,11 @@ template void Writer::assignFileOffsets() { if (config->zSeparate != SeparateSegmentKind::None && lastRX && lastRX->lastSec == sec) off = alignToPowerOf2(off, config->maxPageSize); + bool debug = false; + if (debug) + lld::outs() << "assignFileOffsets() Sec: " << sec->name + << " Off: " << std::to_string(off) + << " Offset: " << std::to_string(sec->offset) << '\n'; } for (OutputSection *osec : outputSections) if (!(osec->flags & SHF_ALLOC)) { diff --git a/llvm-build/Makefile b/llvm-build/Makefile index ecb982228b0e..bc63938d787d 100644 --- a/llvm-build/Makefile +++ b/llvm-build/Makefile @@ -43,7 +43,7 @@ MULTILIB = $(patsubst $(dir $(shell $(filter-out $(ARCH_CFLAGS),$(CC)) -print-li endif MUSLBUILDDIR = build_$(or $(TARGET),$(ARCH))$(subst /,_,$(MULTILIB:%/=%)) HIDE = @ -BUILD_DEBUG = false +BUILD_DEBUG = true SED_ARGS = -e '/install-libs:/s/if/and/g' TOPDIR = $(shell pwd)/../../../.. @@ -92,7 +92,7 @@ endif endif ifeq ($(ARCH),aarch64) -CFLAGS = -march=armv8 -O2 -Wall -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack +CFLAGS = -march=armv8 -g -gdwarf-4 -O0 -Wall -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack else ifeq ($(ARCH),riscv64) CFLAGS = -march=rv64gc -O2 -Wall -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack @@ -225,7 +225,7 @@ musl_install_for_linux_user: musl_patch_for_linux_user make -sj install clean: - $(HIDE) rm -rf musl_copy_for_* linux_header_install_for_* + # $(HIDE) rm -rf musl_copy_for_* linux_header_install_for_* distclean: clean $(HIDE) rm -rf $(SYSROOTDIR)/lib $(SYSROOTDIR)/usr diff --git a/llvm-build/build.py b/llvm-build/build.py index f754b1f85fce..b3e18fa72eb0 100755 --- a/llvm-build/build.py +++ b/llvm-build/build.py @@ -1188,7 +1188,7 @@ class LlvmLibs(BuildUtils): defines = self.base_cmake_defines() ldflags = [] - cflags = [] + cflags = ["-g -gdwarf-4 -O0"] self.logger().info('Build libs for %s', llvm_triple) if self.build_config.target_debug: defines['CMAKE_BUILD_TYPE'] = 'Debug' -- Gitee