Index: vendor/lld/lld-release_900-r372316/ELF/Writer.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Writer.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Writer.cpp (revision 352529) @@ -0,0 +1,2691 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "AArch64ErrataFix.h" +#include "CallGraphSort.h" +#include "Config.h" +#include "LinkerScript.h" +#include "MapFile.h" +#include "OutputSections.h" +#include "Relocations.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/Filesystem.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "lld/Common/Threads.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/SHA1.h" +#include "llvm/Support/xxhash.h" +#include + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::support; +using namespace llvm::support::endian; + +using namespace lld; +using namespace lld::elf; + +namespace { +// The writer writes a SymbolTable result to a file. +template class Writer { +public: + Writer() : buffer(errorHandler().outputBuffer) {} + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Ehdr = typename ELFT::Ehdr; + using Elf_Phdr = typename ELFT::Phdr; + + void run(); + +private: + void copyLocalSymbols(); + void addSectionSymbols(); + void forEachRelSec(llvm::function_ref fn); + void sortSections(); + void resolveShfLinkOrder(); + void finalizeAddressDependentContent(); + void sortInputSections(); + void finalizeSections(); + void checkExecuteOnly(); + void setReservedSymbolSections(); + + std::vector createPhdrs(Partition &part); + void removeEmptyPTLoad(std::vector &phdrEntry); + void addPhdrForSection(Partition &part, unsigned shType, unsigned pType, + unsigned pFlags); + void assignFileOffsets(); + void assignFileOffsetsBinary(); + void setPhdrs(Partition &part); + void checkSections(); + void fixSectionAlignments(); + void openFile(); + void writeTrapInstr(); + void writeHeader(); + void writeSections(); + void writeSectionsBinary(); + void writeBuildId(); + + std::unique_ptr &buffer; + + void addRelIpltSymbols(); + void addStartEndSymbols(); + void addStartStopSymbols(OutputSection *sec); + + uint64_t fileSize; + uint64_t sectionHeaderOff; +}; +} // anonymous namespace + +static bool isSectionPrefix(StringRef prefix, StringRef name) { + return name.startswith(prefix) || name == prefix.drop_back(); +} + +StringRef elf::getOutputSectionName(const InputSectionBase *s) { + if (config->relocatable) + return s->name; + + // This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want + // to emit .rela.text.foo as .rela.text.bar for consistency (this is not + // technically required, but not doing it is odd). This code guarantees that. + if (auto *isec = dyn_cast(s)) { + if (InputSectionBase *rel = isec->getRelocatedSection()) { + OutputSection *out = rel->getOutputSection(); + if (s->type == SHT_RELA) + return saver.save(".rela" + out->name); + return saver.save(".rel" + out->name); + } + } + + // This check is for -z keep-text-section-prefix. This option separates text + // sections with prefix ".text.hot", ".text.unlikely", ".text.startup" or + // ".text.exit". + // When enabled, this allows identifying the hot code region (.text.hot) in + // the final binary which can be selectively mapped to huge pages or mlocked, + // for instance. + if (config->zKeepTextSectionPrefix) + for (StringRef v : + {".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."}) + if (isSectionPrefix(v, s->name)) + return v.drop_back(); + + for (StringRef v : + {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.", + ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.", + ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."}) + if (isSectionPrefix(v, s->name)) + return v.drop_back(); + + // CommonSection is identified as "COMMON" in linker scripts. + // By default, it should go to .bss section. + if (s->name == "COMMON") + return ".bss"; + + return s->name; +} + +static bool needsInterpSection() { + return !sharedFiles.empty() && !config->dynamicLinker.empty() && + script->needsInterpSection(); +} + +template void elf::writeResult() { Writer().run(); } + +template +void Writer::removeEmptyPTLoad(std::vector &phdrs) { + llvm::erase_if(phdrs, [&](const PhdrEntry *p) { + if (p->p_type != PT_LOAD) + return false; + if (!p->firstSec) + return true; + uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr; + return size == 0; + }); +} + +template static void copySectionsIntoPartitions() { + std::vector newSections; + for (unsigned part = 2; part != partitions.size() + 1; ++part) { + for (InputSectionBase *s : inputSections) { + if (!(s->flags & SHF_ALLOC) || !s->isLive()) + continue; + InputSectionBase *copy; + if (s->type == SHT_NOTE) + copy = make(cast(*s)); + else if (auto *es = dyn_cast(s)) + copy = make(*es); + else + continue; + copy->partition = part; + newSections.push_back(copy); + } + } + + inputSections.insert(inputSections.end(), newSections.begin(), + newSections.end()); +} + +template static void combineEhSections() { + for (InputSectionBase *&s : inputSections) { + // Ignore dead sections and the partition end marker (.part.end), + // whose partition number is out of bounds. + if (!s->isLive() || s->partition == 255) + continue; + + Partition &part = s->getPartition(); + if (auto *es = dyn_cast(s)) { + part.ehFrame->addSection(es); + s = nullptr; + } else if (s->kind() == SectionBase::Regular && part.armExidx && + part.armExidx->addSection(cast(s))) { + s = nullptr; + } + } + + std::vector &v = inputSections; + v.erase(std::remove(v.begin(), v.end(), nullptr), v.end()); +} + +static Defined *addOptionalRegular(StringRef name, SectionBase *sec, + uint64_t val, uint8_t stOther = STV_HIDDEN, + uint8_t binding = STB_GLOBAL) { + Symbol *s = symtab->find(name); + if (!s || s->isDefined()) + return nullptr; + + s->resolve(Defined{/*file=*/nullptr, name, binding, stOther, STT_NOTYPE, val, + /*size=*/0, sec}); + return cast(s); +} + +static Defined *addAbsolute(StringRef name) { + Symbol *sym = symtab->addSymbol(Defined{nullptr, name, STB_GLOBAL, STV_HIDDEN, + STT_NOTYPE, 0, 0, nullptr}); + return cast(sym); +} + +// The linker is expected to define some symbols depending on +// the linking result. This function defines such symbols. +void elf::addReservedSymbols() { + if (config->emachine == EM_MIPS) { + // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer + // so that it points to an absolute address which by default is relative + // to GOT. Default offset is 0x7ff0. + // See "Global Data Symbols" in Chapter 6 in the following document: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + ElfSym::mipsGp = addAbsolute("_gp"); + + // On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between + // start of function and 'gp' pointer into GOT. + if (symtab->find("_gp_disp")) + ElfSym::mipsGpDisp = addAbsolute("_gp_disp"); + + // The __gnu_local_gp is a magic symbol equal to the current value of 'gp' + // pointer. This symbol is used in the code generated by .cpload pseudo-op + // in case of using -mno-shared option. + // https://sourceware.org/ml/binutils/2004-12/msg00094.html + if (symtab->find("__gnu_local_gp")) + ElfSym::mipsLocalGp = addAbsolute("__gnu_local_gp"); + } else if (config->emachine == EM_PPC) { + // glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't + // support Small Data Area, define it arbitrarily as 0. + addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN); + } + + // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which + // combines the typical ELF GOT with the small data sections. It commonly + // includes .got .toc .sdata .sbss. The .TOC. symbol replaces both + // _GLOBAL_OFFSET_TABLE_ and _SDA_BASE_ from the 32-bit ABI. It is used to + // represent the TOC base which is offset by 0x8000 bytes from the start of + // the .got section. + // We do not allow _GLOBAL_OFFSET_TABLE_ to be defined by input objects as the + // correctness of some relocations depends on its value. + StringRef gotSymName = + (config->emachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_"; + + if (Symbol *s = symtab->find(gotSymName)) { + if (s->isDefined()) { + error(toString(s->file) + " cannot redefine linker defined symbol '" + + gotSymName + "'"); + return; + } + + uint64_t gotOff = 0; + if (config->emachine == EM_PPC64) + gotOff = 0x8000; + + s->resolve(Defined{/*file=*/nullptr, gotSymName, STB_GLOBAL, STV_HIDDEN, + STT_NOTYPE, gotOff, /*size=*/0, Out::elfHeader}); + ElfSym::globalOffsetTable = cast(s); + } + + // __ehdr_start is the location of ELF file headers. Note that we define + // this symbol unconditionally even when using a linker script, which + // differs from the behavior implemented by GNU linker which only define + // this symbol if ELF headers are in the memory mapped segment. + addOptionalRegular("__ehdr_start", Out::elfHeader, 0, STV_HIDDEN); + + // __executable_start is not documented, but the expectation of at + // least the Android libc is that it points to the ELF header. + addOptionalRegular("__executable_start", Out::elfHeader, 0, STV_HIDDEN); + + // __dso_handle symbol is passed to cxa_finalize as a marker to identify + // each DSO. The address of the symbol doesn't matter as long as they are + // different in different DSOs, so we chose the start address of the DSO. + addOptionalRegular("__dso_handle", Out::elfHeader, 0, STV_HIDDEN); + + // If linker script do layout we do not need to create any standart symbols. + if (script->hasSectionsCommand) + return; + + auto add = [](StringRef s, int64_t pos) { + return addOptionalRegular(s, Out::elfHeader, pos, STV_DEFAULT); + }; + + ElfSym::bss = add("__bss_start", 0); + ElfSym::end1 = add("end", -1); + ElfSym::end2 = add("_end", -1); + ElfSym::etext1 = add("etext", -1); + ElfSym::etext2 = add("_etext", -1); + ElfSym::edata1 = add("edata", -1); + ElfSym::edata2 = add("_edata", -1); +} + +static OutputSection *findSection(StringRef name, unsigned partition = 1) { + for (BaseCommand *base : script->sectionCommands) + if (auto *sec = dyn_cast(base)) + if (sec->name == name && sec->partition == partition) + return sec; + return nullptr; +} + +// Initialize Out members. +template static void createSyntheticSections() { + // Initialize all pointers with NULL. This is needed because + // you can call lld::elf::main more than once as a library. + memset(&Out::first, 0, sizeof(Out)); + + auto add = [](InputSectionBase *sec) { inputSections.push_back(sec); }; + + in.shStrTab = make(".shstrtab", false); + + Out::programHeaders = make("", 0, SHF_ALLOC); + Out::programHeaders->alignment = config->wordsize; + + if (config->strip != StripPolicy::All) { + in.strTab = make(".strtab", false); + in.symTab = make>(*in.strTab); + in.symTabShndx = make(); + } + + in.bss = make(".bss", 0, 1); + add(in.bss); + + // If there is a SECTIONS command and a .data.rel.ro section name use name + // .data.rel.ro.bss so that we match in the .data.rel.ro output section. + // This makes sure our relro is contiguous. + bool hasDataRelRo = + script->hasSectionsCommand && findSection(".data.rel.ro", 0); + in.bssRelRo = + make(hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1); + add(in.bssRelRo); + + // Add MIPS-specific sections. + if (config->emachine == EM_MIPS) { + if (!config->shared && config->hasDynSymTab) { + in.mipsRldMap = make(); + add(in.mipsRldMap); + } + if (auto *sec = MipsAbiFlagsSection::create()) + add(sec); + if (auto *sec = MipsOptionsSection::create()) + add(sec); + if (auto *sec = MipsReginfoSection::create()) + add(sec); + } + + for (Partition &part : partitions) { + auto add = [&](InputSectionBase *sec) { + sec->partition = part.getNumber(); + inputSections.push_back(sec); + }; + + if (!part.name.empty()) { + part.elfHeader = make>(); + part.elfHeader->name = part.name; + add(part.elfHeader); + + part.programHeaders = make>(); + add(part.programHeaders); + } + + if (config->buildId != BuildIdKind::None) { + part.buildId = make(); + add(part.buildId); + } + + part.dynStrTab = make(".dynstr", true); + part.dynSymTab = make>(*part.dynStrTab); + part.dynamic = make>(); + if (config->androidPackDynRelocs) { + part.relaDyn = make>( + config->isRela ? ".rela.dyn" : ".rel.dyn"); + } else { + part.relaDyn = make>( + config->isRela ? ".rela.dyn" : ".rel.dyn", config->zCombreloc); + } + + if (needsInterpSection()) + add(createInterpSection()); + + if (config->hasDynSymTab) { + part.dynSymTab = make>(*part.dynStrTab); + add(part.dynSymTab); + + part.verSym = make(); + add(part.verSym); + + if (!config->versionDefinitions.empty()) { + part.verDef = make(); + add(part.verDef); + } + + part.verNeed = make>(); + add(part.verNeed); + + if (config->gnuHash) { + part.gnuHashTab = make(); + add(part.gnuHashTab); + } + + if (config->sysvHash) { + part.hashTab = make(); + add(part.hashTab); + } + + add(part.dynamic); + add(part.dynStrTab); + add(part.relaDyn); + } + + if (config->relrPackDynRelocs) { + part.relrDyn = make>(); + add(part.relrDyn); + } + + if (!config->relocatable) { + if (config->ehFrameHdr) { + part.ehFrameHdr = make(); + add(part.ehFrameHdr); + } + part.ehFrame = make(); + add(part.ehFrame); + } + + if (config->emachine == EM_ARM && !config->relocatable) { + // The ARMExidxsyntheticsection replaces all the individual .ARM.exidx + // InputSections. + part.armExidx = make(); + add(part.armExidx); + } + } + + if (partitions.size() != 1) { + // Create the partition end marker. This needs to be in partition number 255 + // so that it is sorted after all other partitions. It also has other + // special handling (see createPhdrs() and combineEhSections()). + in.partEnd = make(".part.end", config->maxPageSize, 1); + in.partEnd->partition = 255; + add(in.partEnd); + + in.partIndex = make(); + addOptionalRegular("__part_index_begin", in.partIndex, 0); + addOptionalRegular("__part_index_end", in.partIndex, + in.partIndex->getSize()); + add(in.partIndex); + } + + // Add .got. MIPS' .got is so different from the other archs, + // it has its own class. + if (config->emachine == EM_MIPS) { + in.mipsGot = make(); + add(in.mipsGot); + } else { + in.got = make(); + add(in.got); + } + + if (config->emachine == EM_PPC) { + in.ppc32Got2 = make(); + add(in.ppc32Got2); + } + + if (config->emachine == EM_PPC64) { + in.ppc64LongBranchTarget = make(); + add(in.ppc64LongBranchTarget); + } + + if (config->emachine == EM_RISCV) { + in.riscvSdata = make(); + add(in.riscvSdata); + } + + in.gotPlt = make(); + add(in.gotPlt); + in.igotPlt = make(); + add(in.igotPlt); + + // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat + // it as a relocation and ensure the referenced section is created. + if (ElfSym::globalOffsetTable && config->emachine != EM_MIPS) { + if (target->gotBaseSymInGotPlt) + in.gotPlt->hasGotPltOffRel = true; + else + in.got->hasGotOffRel = true; + } + + if (config->gdbIndex) + add(GdbIndexSection::create()); + + // We always need to add rel[a].plt to output if it has entries. + // Even for static linking it can contain R_[*]_IRELATIVE relocations. + in.relaPlt = make>( + config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false); + add(in.relaPlt); + + // The relaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure + // that the IRelative relocations are processed last by the dynamic loader. + // We cannot place the iplt section in .rel.dyn when Android relocation + // packing is enabled because that would cause a section type mismatch. + // However, because the Android dynamic loader reads .rel.plt after .rel.dyn, + // we can get the desired behaviour by placing the iplt section in .rel.plt. + in.relaIplt = make>( + (config->emachine == EM_ARM && !config->androidPackDynRelocs) + ? ".rel.dyn" + : in.relaPlt->name, + /*sort=*/false); + add(in.relaIplt); + + in.plt = make(false); + add(in.plt); + in.iplt = make(true); + add(in.iplt); + + if (config->andFeatures) + add(make()); + + // .note.GNU-stack is always added when we are creating a re-linkable + // object file. Other linkers are using the presence of this marker + // section to control the executable-ness of the stack area, but that + // is irrelevant these days. Stack area should always be non-executable + // by default. So we emit this section unconditionally. + if (config->relocatable) + add(make()); + + if (in.symTab) + add(in.symTab); + if (in.symTabShndx) + add(in.symTabShndx); + add(in.shStrTab); + if (in.strTab) + add(in.strTab); +} + +// The main function of the writer. +template void Writer::run() { + // Make copies of any input sections that need to be copied into each + // partition. + copySectionsIntoPartitions(); + + // Create linker-synthesized sections such as .got or .plt. + // Such sections are of type input section. + createSyntheticSections(); + + // Some input sections that are used for exception handling need to be moved + // into synthetic sections. Do that now so that they aren't assigned to + // output sections in the usual way. + if (!config->relocatable) + combineEhSections(); + + // We want to process linker script commands. When SECTIONS command + // is given we let it create sections. + script->processSectionCommands(); + + // Linker scripts controls how input sections are assigned to output sections. + // Input sections that were not handled by scripts are called "orphans", and + // they are assigned to output sections by the default rule. Process that. + script->addOrphanSections(); + + if (config->discard != DiscardPolicy::All) + copyLocalSymbols(); + + if (config->copyRelocs) + addSectionSymbols(); + + // Now that we have a complete set of output sections. This function + // completes section contents. For example, we need to add strings + // to the string table, and add entries to .got and .plt. + // finalizeSections does that. + finalizeSections(); + checkExecuteOnly(); + if (errorCount()) + return; + + script->assignAddresses(); + + // If -compressed-debug-sections is specified, we need to compress + // .debug_* sections. Do it right now because it changes the size of + // output sections. + for (OutputSection *sec : outputSections) + sec->maybeCompress(); + + script->allocateHeaders(mainPart->phdrs); + + // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a + // 0 sized region. This has to be done late since only after assignAddresses + // we know the size of the sections. + for (Partition &part : partitions) + removeEmptyPTLoad(part.phdrs); + + if (!config->oFormatBinary) + assignFileOffsets(); + else + assignFileOffsetsBinary(); + + for (Partition &part : partitions) + setPhdrs(part); + + if (config->relocatable) + for (OutputSection *sec : outputSections) + sec->addr = 0; + + if (config->checkSections) + checkSections(); + + // It does not make sense try to open the file if we have error already. + if (errorCount()) + return; + // Write the result down to a file. + openFile(); + if (errorCount()) + return; + + if (!config->oFormatBinary) { + writeTrapInstr(); + writeHeader(); + writeSections(); + } else { + writeSectionsBinary(); + } + + // Backfill .note.gnu.build-id section content. This is done at last + // because the content is usually a hash value of the entire output file. + writeBuildId(); + if (errorCount()) + return; + + // Handle -Map and -cref options. + writeMapFile(); + writeCrossReferenceTable(); + if (errorCount()) + return; + + if (auto e = buffer->commit()) + error("failed to write to the output file: " + toString(std::move(e))); +} + +static bool shouldKeepInSymtab(const Defined &sym) { + if (sym.isSection()) + return false; + + if (config->discard == DiscardPolicy::None) + return true; + + // If -emit-reloc is given, all symbols including local ones need to be + // copied because they may be referenced by relocations. + if (config->emitRelocs) + return true; + + // In ELF assembly .L symbols are normally discarded by the assembler. + // If the assembler fails to do so, the linker discards them if + // * --discard-locals is used. + // * The symbol is in a SHF_MERGE section, which is normally the reason for + // the assembler keeping the .L symbol. + StringRef name = sym.getName(); + bool isLocal = name.startswith(".L") || name.empty(); + if (!isLocal) + return true; + + if (config->discard == DiscardPolicy::Locals) + return false; + + SectionBase *sec = sym.section; + return !sec || !(sec->flags & SHF_MERGE); +} + +static bool includeInSymtab(const Symbol &b) { + if (!b.isLocal() && !b.isUsedInRegularObj) + return false; + + if (auto *d = dyn_cast(&b)) { + // Always include absolute symbols. + SectionBase *sec = d->section; + if (!sec) + return true; + sec = sec->repl; + + // Exclude symbols pointing to garbage-collected sections. + if (isa(sec) && !sec->isLive()) + return false; + + if (auto *s = dyn_cast(sec)) + if (!s->getSectionPiece(d->value)->live) + return false; + return true; + } + return b.used; +} + +// Local symbols are not in the linker's symbol table. This function scans +// each object file's symbol table to copy local symbols to the output. +template void Writer::copyLocalSymbols() { + if (!in.symTab) + return; + for (InputFile *file : objectFiles) { + ObjFile *f = cast>(file); + for (Symbol *b : f->getLocalSymbols()) { + if (!b->isLocal()) + fatal(toString(f) + + ": broken object: getLocalSymbols returns a non-local symbol"); + auto *dr = dyn_cast(b); + + // No reason to keep local undefined symbol in symtab. + if (!dr) + continue; + if (!includeInSymtab(*b)) + continue; + if (!shouldKeepInSymtab(*dr)) + continue; + in.symTab->addSymbol(b); + } + } +} + +// Create a section symbol for each output section so that we can represent +// relocations that point to the section. If we know that no relocation is +// referring to a section (that happens if the section is a synthetic one), we +// don't create a section symbol for that section. +template void Writer::addSectionSymbols() { + for (BaseCommand *base : script->sectionCommands) { + auto *sec = dyn_cast(base); + if (!sec) + continue; + auto i = llvm::find_if(sec->sectionCommands, [](BaseCommand *base) { + if (auto *isd = dyn_cast(base)) + return !isd->sections.empty(); + return false; + }); + if (i == sec->sectionCommands.end()) + continue; + InputSection *isec = cast(*i)->sections[0]; + + // Relocations are not using REL[A] section symbols. + if (isec->type == SHT_REL || isec->type == SHT_RELA) + continue; + + // Unlike other synthetic sections, mergeable output sections contain data + // copied from input sections, and there may be a relocation pointing to its + // contents if -r or -emit-reloc are given. + if (isa(isec) && !(isec->flags & SHF_MERGE)) + continue; + + auto *sym = + make(isec->file, "", STB_LOCAL, /*stOther=*/0, STT_SECTION, + /*value=*/0, /*size=*/0, isec); + in.symTab->addSymbol(sym); + } +} + +// Today's loaders have a feature to make segments read-only after +// processing dynamic relocations to enhance security. PT_GNU_RELRO +// is defined for that. +// +// This function returns true if a section needs to be put into a +// PT_GNU_RELRO segment. +static bool isRelroSection(const OutputSection *sec) { + if (!config->zRelro) + return false; + + uint64_t flags = sec->flags; + + // Non-allocatable or non-writable sections don't need RELRO because + // they are not writable or not even mapped to memory in the first place. + // RELRO is for sections that are essentially read-only but need to + // be writable only at process startup to allow dynamic linker to + // apply relocations. + if (!(flags & SHF_ALLOC) || !(flags & SHF_WRITE)) + return false; + + // Once initialized, TLS data segments are used as data templates + // for a thread-local storage. For each new thread, runtime + // allocates memory for a TLS and copy templates there. No thread + // are supposed to use templates directly. Thus, it can be in RELRO. + if (flags & SHF_TLS) + return true; + + // .init_array, .preinit_array and .fini_array contain pointers to + // functions that are executed on process startup or exit. These + // pointers are set by the static linker, and they are not expected + // to change at runtime. But if you are an attacker, you could do + // interesting things by manipulating pointers in .fini_array, for + // example. So they are put into RELRO. + uint32_t type = sec->type; + if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY || + type == SHT_PREINIT_ARRAY) + return true; + + // .got contains pointers to external symbols. They are resolved by + // the dynamic linker when a module is loaded into memory, and after + // that they are not expected to change. So, it can be in RELRO. + if (in.got && sec == in.got->getParent()) + return true; + + // .toc is a GOT-ish section for PowerPC64. Their contents are accessed + // through r2 register, which is reserved for that purpose. Since r2 is used + // for accessing .got as well, .got and .toc need to be close enough in the + // virtual address space. Usually, .toc comes just after .got. Since we place + // .got into RELRO, .toc needs to be placed into RELRO too. + if (sec->name.equals(".toc")) + return true; + + // .got.plt contains pointers to external function symbols. They are + // by default resolved lazily, so we usually cannot put it into RELRO. + // However, if "-z now" is given, the lazy symbol resolution is + // disabled, which enables us to put it into RELRO. + if (sec == in.gotPlt->getParent()) + return config->zNow; + + // .dynamic section contains data for the dynamic linker, and + // there's no need to write to it at runtime, so it's better to put + // it into RELRO. + if (sec->name == ".dynamic") + return true; + + // Sections with some special names are put into RELRO. This is a + // bit unfortunate because section names shouldn't be significant in + // ELF in spirit. But in reality many linker features depend on + // magic section names. + StringRef s = sec->name; + return s == ".data.rel.ro" || s == ".bss.rel.ro" || s == ".ctors" || + s == ".dtors" || s == ".jcr" || s == ".eh_frame" || + s == ".openbsd.randomdata"; +} + +// We compute a rank for each section. The rank indicates where the +// section should be placed in the file. Instead of using simple +// numbers (0,1,2...), we use a series of flags. One for each decision +// point when placing the section. +// Using flags has two key properties: +// * It is easy to check if a give branch was taken. +// * It is easy two see how similar two ranks are (see getRankProximity). +enum RankFlags { + RF_NOT_ADDR_SET = 1 << 27, + RF_NOT_ALLOC = 1 << 26, + RF_PARTITION = 1 << 18, // Partition number (8 bits) + RF_NOT_PART_EHDR = 1 << 17, + RF_NOT_PART_PHDR = 1 << 16, + RF_NOT_INTERP = 1 << 15, + RF_NOT_NOTE = 1 << 14, + RF_WRITE = 1 << 13, + RF_EXEC_WRITE = 1 << 12, + RF_EXEC = 1 << 11, + RF_RODATA = 1 << 10, + RF_NOT_RELRO = 1 << 9, + RF_NOT_TLS = 1 << 8, + RF_BSS = 1 << 7, + RF_PPC_NOT_TOCBSS = 1 << 6, + RF_PPC_TOCL = 1 << 5, + RF_PPC_TOC = 1 << 4, + RF_PPC_GOT = 1 << 3, + RF_PPC_BRANCH_LT = 1 << 2, + RF_MIPS_GPREL = 1 << 1, + RF_MIPS_NOT_GOT = 1 << 0 +}; + +static unsigned getSectionRank(const OutputSection *sec) { + unsigned rank = sec->partition * RF_PARTITION; + + // We want to put section specified by -T option first, so we + // can start assigning VA starting from them later. + if (config->sectionStartMap.count(sec->name)) + return rank; + rank |= RF_NOT_ADDR_SET; + + // Allocatable sections go first to reduce the total PT_LOAD size and + // so debug info doesn't change addresses in actual code. + if (!(sec->flags & SHF_ALLOC)) + return rank | RF_NOT_ALLOC; + + if (sec->type == SHT_LLVM_PART_EHDR) + return rank; + rank |= RF_NOT_PART_EHDR; + + if (sec->type == SHT_LLVM_PART_PHDR) + return rank; + rank |= RF_NOT_PART_PHDR; + + // Put .interp first because some loaders want to see that section + // on the first page of the executable file when loaded into memory. + if (sec->name == ".interp") + return rank; + rank |= RF_NOT_INTERP; + + // Put .note sections (which make up one PT_NOTE) at the beginning so that + // they are likely to be included in a core file even if core file size is + // limited. In particular, we want a .note.gnu.build-id and a .note.tag to be + // included in a core to match core files with executables. + if (sec->type == SHT_NOTE) + return rank; + rank |= RF_NOT_NOTE; + + // Sort sections based on their access permission in the following + // order: R, RX, RWX, RW. This order is based on the following + // considerations: + // * Read-only sections come first such that they go in the + // PT_LOAD covering the program headers at the start of the file. + // * Read-only, executable sections come next. + // * Writable, executable sections follow such that .plt on + // architectures where it needs to be writable will be placed + // between .text and .data. + // * Writable sections come last, such that .bss lands at the very + // end of the last PT_LOAD. + bool isExec = sec->flags & SHF_EXECINSTR; + bool isWrite = sec->flags & SHF_WRITE; + + if (isExec) { + if (isWrite) + rank |= RF_EXEC_WRITE; + else + rank |= RF_EXEC; + } else if (isWrite) { + rank |= RF_WRITE; + } else if (sec->type == SHT_PROGBITS) { + // Make non-executable and non-writable PROGBITS sections (e.g .rodata + // .eh_frame) closer to .text. They likely contain PC or GOT relative + // relocations and there could be relocation overflow if other huge sections + // (.dynstr .dynsym) were placed in between. + rank |= RF_RODATA; + } + + // Place RelRo sections first. After considering SHT_NOBITS below, the + // ordering is PT_LOAD(PT_GNU_RELRO(.data.rel.ro .bss.rel.ro) | .data .bss), + // where | marks where page alignment happens. An alternative ordering is + // PT_LOAD(.data | PT_GNU_RELRO( .data.rel.ro .bss.rel.ro) | .bss), but it may + // waste more bytes due to 2 alignment places. + if (!isRelroSection(sec)) + rank |= RF_NOT_RELRO; + + // If we got here we know that both A and B are in the same PT_LOAD. + + // The TLS initialization block needs to be a single contiguous block in a R/W + // PT_LOAD, so stick TLS sections directly before the other RelRo R/W + // sections. Since p_filesz can be less than p_memsz, place NOBITS sections + // after PROGBITS. + if (!(sec->flags & SHF_TLS)) + rank |= RF_NOT_TLS; + + // Within TLS sections, or within other RelRo sections, or within non-RelRo + // sections, place non-NOBITS sections first. + if (sec->type == SHT_NOBITS) + rank |= RF_BSS; + + // Some architectures have additional ordering restrictions for sections + // within the same PT_LOAD. + if (config->emachine == EM_PPC64) { + // PPC64 has a number of special SHT_PROGBITS+SHF_ALLOC+SHF_WRITE sections + // that we would like to make sure appear is a specific order to maximize + // their coverage by a single signed 16-bit offset from the TOC base + // pointer. Conversely, the special .tocbss section should be first among + // all SHT_NOBITS sections. This will put it next to the loaded special + // PPC64 sections (and, thus, within reach of the TOC base pointer). + StringRef name = sec->name; + if (name != ".tocbss") + rank |= RF_PPC_NOT_TOCBSS; + + if (name == ".toc1") + rank |= RF_PPC_TOCL; + + if (name == ".toc") + rank |= RF_PPC_TOC; + + if (name == ".got") + rank |= RF_PPC_GOT; + + if (name == ".branch_lt") + rank |= RF_PPC_BRANCH_LT; + } + + if (config->emachine == EM_MIPS) { + // All sections with SHF_MIPS_GPREL flag should be grouped together + // because data in these sections is addressable with a gp relative address. + if (sec->flags & SHF_MIPS_GPREL) + rank |= RF_MIPS_GPREL; + + if (sec->name != ".got") + rank |= RF_MIPS_NOT_GOT; + } + + return rank; +} + +static bool compareSections(const BaseCommand *aCmd, const BaseCommand *bCmd) { + const OutputSection *a = cast(aCmd); + const OutputSection *b = cast(bCmd); + + if (a->sortRank != b->sortRank) + return a->sortRank < b->sortRank; + + if (!(a->sortRank & RF_NOT_ADDR_SET)) + return config->sectionStartMap.lookup(a->name) < + config->sectionStartMap.lookup(b->name); + return false; +} + +void PhdrEntry::add(OutputSection *sec) { + lastSec = sec; + if (!firstSec) + firstSec = sec; + p_align = std::max(p_align, sec->alignment); + if (p_type == PT_LOAD) + sec->ptLoad = this; +} + +// The beginning and the ending of .rel[a].plt section are marked +// with __rel[a]_iplt_{start,end} symbols if it is a statically linked +// executable. The runtime needs these symbols in order to resolve +// all IRELATIVE relocs on startup. For dynamic executables, we don't +// need these symbols, since IRELATIVE relocs are resolved through GOT +// and PLT. For details, see http://www.airs.com/blog/archives/403. +template void Writer::addRelIpltSymbols() { + if (config->relocatable || needsInterpSection()) + return; + + // By default, __rela_iplt_{start,end} belong to a dummy section 0 + // because .rela.plt might be empty and thus removed from output. + // We'll override Out::elfHeader with In.relaIplt later when we are + // sure that .rela.plt exists in output. + ElfSym::relaIpltStart = addOptionalRegular( + config->isRela ? "__rela_iplt_start" : "__rel_iplt_start", + Out::elfHeader, 0, STV_HIDDEN, STB_WEAK); + + ElfSym::relaIpltEnd = addOptionalRegular( + config->isRela ? "__rela_iplt_end" : "__rel_iplt_end", + Out::elfHeader, 0, STV_HIDDEN, STB_WEAK); +} + +template +void Writer::forEachRelSec( + llvm::function_ref fn) { + // Scan all relocations. Each relocation goes through a series + // of tests to 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 *isec : inputSections) + if (isec->isLive() && isa(isec) && (isec->flags & SHF_ALLOC)) + fn(*isec); + for (Partition &part : partitions) { + for (EhInputSection *es : part.ehFrame->sections) + fn(*es); + if (part.armExidx && part.armExidx->isLive()) + for (InputSection *ex : part.armExidx->exidxSections) + fn(*ex); + } +} + +// This function generates assignments for predefined symbols (e.g. _end or +// _etext) and inserts them into the commands sequence to be processed at the +// appropriate time. This ensures that the value is going to be correct by the +// time any references to these symbols are processed and is equivalent to +// defining these symbols explicitly in the linker script. +template void Writer::setReservedSymbolSections() { + if (ElfSym::globalOffsetTable) { + // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually + // to the start of the .got or .got.plt section. + InputSection *gotSection = in.gotPlt; + if (!target->gotBaseSymInGotPlt) + gotSection = in.mipsGot ? cast(in.mipsGot) + : cast(in.got); + ElfSym::globalOffsetTable->section = gotSection; + } + + // .rela_iplt_{start,end} mark the start and the end of .rela.plt section. + if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) { + ElfSym::relaIpltStart->section = in.relaIplt; + ElfSym::relaIpltEnd->section = in.relaIplt; + ElfSym::relaIpltEnd->value = in.relaIplt->getSize(); + } + + PhdrEntry *last = nullptr; + PhdrEntry *lastRO = nullptr; + + for (Partition &part : partitions) { + for (PhdrEntry *p : part.phdrs) { + if (p->p_type != PT_LOAD) + continue; + last = p; + if (!(p->p_flags & PF_W)) + lastRO = p; + } + } + + if (lastRO) { + // _etext is the first location after the last read-only loadable segment. + if (ElfSym::etext1) + ElfSym::etext1->section = lastRO->lastSec; + if (ElfSym::etext2) + ElfSym::etext2->section = lastRO->lastSec; + } + + if (last) { + // _edata points to the end of the last mapped initialized section. + OutputSection *edata = nullptr; + for (OutputSection *os : outputSections) { + if (os->type != SHT_NOBITS) + edata = os; + if (os == last->lastSec) + break; + } + + if (ElfSym::edata1) + ElfSym::edata1->section = edata; + if (ElfSym::edata2) + ElfSym::edata2->section = edata; + + // _end is the first location after the uninitialized data region. + if (ElfSym::end1) + ElfSym::end1->section = last->lastSec; + if (ElfSym::end2) + ElfSym::end2->section = last->lastSec; + } + + if (ElfSym::bss) + ElfSym::bss->section = findSection(".bss"); + + // Setup MIPS _gp_disp/__gnu_local_gp symbols which should + // be equal to the _gp symbol's value. + if (ElfSym::mipsGp) { + // Find GP-relative section with the lowest address + // and use this address to calculate default _gp value. + for (OutputSection *os : outputSections) { + if (os->flags & SHF_MIPS_GPREL) { + ElfSym::mipsGp->section = os; + ElfSym::mipsGp->value = 0x7ff0; + break; + } + } + } +} + +// We want to find how similar two ranks are. +// The more branches in getSectionRank that match, the more similar they are. +// Since each branch corresponds to a bit flag, we can just use +// countLeadingZeros. +static int getRankProximityAux(OutputSection *a, OutputSection *b) { + return countLeadingZeros(a->sortRank ^ b->sortRank); +} + +static int getRankProximity(OutputSection *a, BaseCommand *b) { + auto *sec = dyn_cast(b); + return (sec && sec->hasInputSections) ? getRankProximityAux(a, sec) : -1; +} + +// When placing orphan sections, we want to place them after symbol assignments +// so that an orphan after +// begin_foo = .; +// foo : { *(foo) } +// end_foo = .; +// doesn't break the intended meaning of the begin/end symbols. +// We don't want to go over sections since findOrphanPos is the +// one in charge of deciding the order of the sections. +// We don't want to go over changes to '.', since doing so in +// rx_sec : { *(rx_sec) } +// . = ALIGN(0x1000); +// /* The RW PT_LOAD starts here*/ +// rw_sec : { *(rw_sec) } +// would mean that the RW PT_LOAD would become unaligned. +static bool shouldSkip(BaseCommand *cmd) { + if (auto *assign = dyn_cast(cmd)) + return assign->name != "."; + return false; +} + +// We want to place orphan sections so that they share as much +// characteristics with their neighbors as possible. For example, if +// both are rw, or both are tls. +static std::vector::iterator +findOrphanPos(std::vector::iterator b, + std::vector::iterator e) { + OutputSection *sec = cast(*e); + + // Find the first element that has as close a rank as possible. + auto i = std::max_element(b, e, [=](BaseCommand *a, BaseCommand *b) { + return getRankProximity(sec, a) < getRankProximity(sec, b); + }); + if (i == e) + return e; + + // Consider all existing sections with the same proximity. + int proximity = getRankProximity(sec, *i); + for (; i != e; ++i) { + auto *curSec = dyn_cast(*i); + if (!curSec || !curSec->hasInputSections) + continue; + if (getRankProximity(sec, curSec) != proximity || + sec->sortRank < curSec->sortRank) + break; + } + + auto isOutputSecWithInputSections = [](BaseCommand *cmd) { + auto *os = dyn_cast(cmd); + return os && os->hasInputSections; + }; + auto j = std::find_if(llvm::make_reverse_iterator(i), + llvm::make_reverse_iterator(b), + isOutputSecWithInputSections); + i = j.base(); + + // As a special case, if the orphan section is the last section, put + // it at the very end, past any other commands. + // This matches bfd's behavior and is convenient when the linker script fully + // specifies the start of the file, but doesn't care about the end (the non + // alloc sections for example). + auto nextSec = std::find_if(i, e, isOutputSecWithInputSections); + if (nextSec == e) + return e; + + while (i != e && shouldSkip(*i)) + ++i; + return i; +} + +// Builds section order for handling --symbol-ordering-file. +static DenseMap buildSectionOrder() { + DenseMap sectionOrder; + // Use the rarely used option -call-graph-ordering-file to sort sections. + if (!config->callGraphProfile.empty()) + return computeCallGraphProfileOrder(); + + if (config->symbolOrderingFile.empty()) + return sectionOrder; + + struct SymbolOrderEntry { + int priority; + bool present; + }; + + // Build a map from symbols to their priorities. Symbols that didn't + // appear in the symbol ordering file have the lowest priority 0. + // All explicitly mentioned symbols have negative (higher) priorities. + DenseMap symbolOrder; + int priority = -config->symbolOrderingFile.size(); + for (StringRef s : config->symbolOrderingFile) + symbolOrder.insert({s, {priority++, false}}); + + // Build a map from sections to their priorities. + auto addSym = [&](Symbol &sym) { + auto it = symbolOrder.find(sym.getName()); + if (it == symbolOrder.end()) + return; + SymbolOrderEntry &ent = it->second; + ent.present = true; + + maybeWarnUnorderableSymbol(&sym); + + if (auto *d = dyn_cast(&sym)) { + if (auto *sec = dyn_cast_or_null(d->section)) { + int &priority = sectionOrder[cast(sec->repl)]; + priority = std::min(priority, ent.priority); + } + } + }; + + // We want both global and local symbols. We get the global ones from the + // symbol table and iterate the object files for the local ones. + symtab->forEachSymbol([&](Symbol *sym) { + if (!sym->isLazy()) + addSym(*sym); + }); + + for (InputFile *file : objectFiles) + for (Symbol *sym : file->getSymbols()) + if (sym->isLocal()) + addSym(*sym); + + if (config->warnSymbolOrdering) + for (auto orderEntry : symbolOrder) + if (!orderEntry.second.present) + warn("symbol ordering file: no such symbol: " + orderEntry.first); + + return sectionOrder; +} + +// Sorts the sections in ISD according to the provided section order. +static void +sortISDBySectionOrder(InputSectionDescription *isd, + const DenseMap &order) { + std::vector unorderedSections; + std::vector> orderedSections; + uint64_t unorderedSize = 0; + + for (InputSection *isec : isd->sections) { + auto i = order.find(isec); + if (i == order.end()) { + unorderedSections.push_back(isec); + unorderedSize += isec->getSize(); + continue; + } + orderedSections.push_back({isec, i->second}); + } + llvm::sort(orderedSections, [&](std::pair a, + std::pair b) { + return a.second < b.second; + }); + + // Find an insertion point for the ordered section list in the unordered + // section list. On targets with limited-range branches, this is the mid-point + // of the unordered section list. This decreases the likelihood that a range + // extension thunk will be needed to enter or exit the ordered region. If the + // ordered section list is a list of hot functions, we can generally expect + // the ordered functions to be called more often than the unordered functions, + // making it more likely that any particular call will be within range, and + // therefore reducing the number of thunks required. + // + // For example, imagine that you have 8MB of hot code and 32MB of cold code. + // If the layout is: + // + // 8MB hot + // 32MB cold + // + // only the first 8-16MB of the cold code (depending on which hot function it + // is actually calling) can call the hot code without a range extension thunk. + // However, if we use this layout: + // + // 16MB cold + // 8MB hot + // 16MB cold + // + // both the last 8-16MB of the first block of cold code and the first 8-16MB + // of the second block of cold code can call the hot code without a thunk. So + // we effectively double the amount of code that could potentially call into + // the hot code without a thunk. + size_t insPt = 0; + if (target->getThunkSectionSpacing() && !orderedSections.empty()) { + uint64_t unorderedPos = 0; + for (; insPt != unorderedSections.size(); ++insPt) { + unorderedPos += unorderedSections[insPt]->getSize(); + if (unorderedPos > unorderedSize / 2) + break; + } + } + + isd->sections.clear(); + for (InputSection *isec : makeArrayRef(unorderedSections).slice(0, insPt)) + isd->sections.push_back(isec); + for (std::pair p : orderedSections) + isd->sections.push_back(p.first); + for (InputSection *isec : makeArrayRef(unorderedSections).slice(insPt)) + isd->sections.push_back(isec); +} + +static void sortSection(OutputSection *sec, + const DenseMap &order) { + StringRef name = sec->name; + + // Sort input sections by section name suffixes for + // __attribute__((init_priority(N))). + if (name == ".init_array" || name == ".fini_array") { + if (!script->hasSectionsCommand) + sec->sortInitFini(); + return; + } + + // Sort input sections by the special rule for .ctors and .dtors. + if (name == ".ctors" || name == ".dtors") { + if (!script->hasSectionsCommand) + sec->sortCtorsDtors(); + return; + } + + // Never sort these. + if (name == ".init" || name == ".fini") + return; + + // .toc is allocated just after .got and is accessed using GOT-relative + // relocations. Object files compiled with small code model have an + // addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations. + // To reduce the risk of relocation overflow, .toc contents are sorted so that + // sections having smaller relocation offsets are at beginning of .toc + if (config->emachine == EM_PPC64 && name == ".toc") { + if (script->hasSectionsCommand) + return; + assert(sec->sectionCommands.size() == 1); + auto *isd = cast(sec->sectionCommands[0]); + llvm::stable_sort(isd->sections, + [](const InputSection *a, const InputSection *b) -> bool { + return a->file->ppc64SmallCodeModelTocRelocs && + !b->file->ppc64SmallCodeModelTocRelocs; + }); + return; + } + + // Sort input sections by priority using the list provided + // by --symbol-ordering-file. + if (!order.empty()) + for (BaseCommand *b : sec->sectionCommands) + if (auto *isd = dyn_cast(b)) + sortISDBySectionOrder(isd, order); +} + +// If no layout was provided by linker script, we want to apply default +// sorting for special input sections. This also handles --symbol-ordering-file. +template void Writer::sortInputSections() { + // Build the order once since it is expensive. + DenseMap order = buildSectionOrder(); + for (BaseCommand *base : script->sectionCommands) + if (auto *sec = dyn_cast(base)) + sortSection(sec, order); +} + +template void Writer::sortSections() { + script->adjustSectionsBeforeSorting(); + + // Don't sort if using -r. It is not necessary and we want to preserve the + // relative order for SHF_LINK_ORDER sections. + if (config->relocatable) + return; + + sortInputSections(); + + for (BaseCommand *base : script->sectionCommands) { + auto *os = dyn_cast(base); + if (!os) + continue; + os->sortRank = getSectionRank(os); + + // We want to assign rude approximation values to outSecOff fields + // to know the relative order of the input sections. We use it for + // sorting SHF_LINK_ORDER sections. See resolveShfLinkOrder(). + uint64_t i = 0; + for (InputSection *sec : getInputSections(os)) + sec->outSecOff = i++; + } + + if (!script->hasSectionsCommand) { + // We know that all the OutputSections are contiguous in this case. + auto isSection = [](BaseCommand *base) { return isa(base); }; + std::stable_sort( + llvm::find_if(script->sectionCommands, isSection), + llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(), + compareSections); + return; + } + + // Orphan sections are sections present in the input files which are + // not explicitly placed into the output file by the linker script. + // + // The sections in the linker script are already in the correct + // order. We have to figuere out where to insert the orphan + // sections. + // + // The order of the sections in the script is arbitrary and may not agree with + // compareSections. This means that we cannot easily define a strict weak + // ordering. To see why, consider a comparison of a section in the script and + // one not in the script. We have a two simple options: + // * Make them equivalent (a is not less than b, and b is not less than a). + // The problem is then that equivalence has to be transitive and we can + // have sections a, b and c with only b in a script and a less than c + // which breaks this property. + // * Use compareSectionsNonScript. Given that the script order doesn't have + // to match, we can end up with sections a, b, c, d where b and c are in the + // script and c is compareSectionsNonScript less than b. In which case d + // can be equivalent to c, a to b and d < a. As a concrete example: + // .a (rx) # not in script + // .b (rx) # in script + // .c (ro) # in script + // .d (ro) # not in script + // + // The way we define an order then is: + // * Sort only the orphan sections. They are in the end right now. + // * Move each orphan section to its preferred position. We try + // to put each section in the last position where it can share + // a PT_LOAD. + // + // There is some ambiguity as to where exactly a new entry should be + // inserted, because Commands contains not only output section + // commands but also other types of commands such as symbol assignment + // expressions. There's no correct answer here due to the lack of the + // formal specification of the linker script. We use heuristics to + // determine whether a new output command should be added before or + // after another commands. For the details, look at shouldSkip + // function. + + auto i = script->sectionCommands.begin(); + auto e = script->sectionCommands.end(); + auto nonScriptI = std::find_if(i, e, [](BaseCommand *base) { + if (auto *sec = dyn_cast(base)) + return sec->sectionIndex == UINT32_MAX; + return false; + }); + + // Sort the orphan sections. + std::stable_sort(nonScriptI, e, compareSections); + + // As a horrible special case, skip the first . assignment if it is before any + // section. We do this because it is common to set a load address by starting + // the script with ". = 0xabcd" and the expectation is that every section is + // after that. + auto firstSectionOrDotAssignment = + std::find_if(i, e, [](BaseCommand *cmd) { return !shouldSkip(cmd); }); + if (firstSectionOrDotAssignment != e && + isa(**firstSectionOrDotAssignment)) + ++firstSectionOrDotAssignment; + i = firstSectionOrDotAssignment; + + while (nonScriptI != e) { + auto pos = findOrphanPos(i, nonScriptI); + OutputSection *orphan = cast(*nonScriptI); + + // As an optimization, find all sections with the same sort rank + // and insert them with one rotate. + unsigned rank = orphan->sortRank; + auto end = std::find_if(nonScriptI + 1, e, [=](BaseCommand *cmd) { + return cast(cmd)->sortRank != rank; + }); + std::rotate(pos, nonScriptI, end); + nonScriptI = end; + } + + script->adjustSectionsAfterSorting(); +} + +static bool compareByFilePosition(InputSection *a, InputSection *b) { + InputSection *la = a->getLinkOrderDep(); + InputSection *lb = b->getLinkOrderDep(); + OutputSection *aOut = la->getParent(); + OutputSection *bOut = lb->getParent(); + + if (aOut != bOut) + return aOut->sectionIndex < bOut->sectionIndex; + return la->outSecOff < lb->outSecOff; +} + +template void Writer::resolveShfLinkOrder() { + for (OutputSection *sec : outputSections) { + if (!(sec->flags & SHF_LINK_ORDER)) + continue; + + // Link order may be distributed across several InputSectionDescriptions + // but sort must consider them all at once. + std::vector scriptSections; + std::vector sections; + for (BaseCommand *base : sec->sectionCommands) { + if (auto *isd = dyn_cast(base)) { + for (InputSection *&isec : isd->sections) { + scriptSections.push_back(&isec); + sections.push_back(isec); + } + } + } + + // The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated + // this processing inside the ARMExidxsyntheticsection::finalizeContents(). + if (!config->relocatable && config->emachine == EM_ARM && + sec->type == SHT_ARM_EXIDX) + continue; + + llvm::stable_sort(sections, compareByFilePosition); + + for (int i = 0, n = sections.size(); i < n; ++i) + *scriptSections[i] = sections[i]; + } +} + +// We need to generate and finalize the content that depends on the address of +// InputSections. As the generation of the content may also alter InputSection +// addresses we must converge to a fixed point. We do that here. See the comment +// in Writer::finalizeSections(). +template void Writer::finalizeAddressDependentContent() { + ThunkCreator tc; + AArch64Err843419Patcher a64p; + + // For some targets, like x86, this loop iterates only once. + for (;;) { + bool changed = false; + + script->assignAddresses(); + + if (target->needsThunks) + changed |= tc.createThunks(outputSections); + + if (config->fixCortexA53Errata843419) { + if (changed) + script->assignAddresses(); + changed |= a64p.createFixes(); + } + + if (in.mipsGot) + in.mipsGot->updateAllocSize(); + + for (Partition &part : partitions) { + changed |= part.relaDyn->updateAllocSize(); + if (part.relrDyn) + changed |= part.relrDyn->updateAllocSize(); + } + + if (!changed) + return; + } +} + +static void finalizeSynthetic(SyntheticSection *sec) { + if (sec && sec->isNeeded() && sec->getParent()) + sec->finalizeContents(); +} + +// In order to allow users to manipulate linker-synthesized sections, +// we had to add synthetic sections to the input section list early, +// even before we make decisions whether they are needed. This allows +// users to write scripts like this: ".mygot : { .got }". +// +// Doing it has an unintended side effects. If it turns out that we +// don't need a .got (for example) at all because there's no +// relocation that needs a .got, we don't want to emit .got. +// +// To deal with the above problem, this function is called after +// scanRelocations is called to remove synthetic sections that turn +// out to be empty. +static void removeUnusedSyntheticSections() { + // All input synthetic sections that can be empty are placed after + // all regular ones. We iterate over them all and exit at first + // non-synthetic. + for (InputSectionBase *s : llvm::reverse(inputSections)) { + SyntheticSection *ss = dyn_cast(s); + if (!ss) + return; + OutputSection *os = ss->getParent(); + if (!os || ss->isNeeded()) + continue; + + // If we reach here, then SS is an unused synthetic section and we want to + // remove it from corresponding input section description of output section. + for (BaseCommand *b : os->sectionCommands) + if (auto *isd = dyn_cast(b)) + llvm::erase_if(isd->sections, + [=](InputSection *isec) { return isec == ss; }); + } +} + +// Returns true if a symbol can be replaced at load-time by a symbol +// with the same name defined in other ELF executable or DSO. +static bool computeIsPreemptible(const Symbol &b) { + assert(!b.isLocal()); + + // Only symbols that appear in dynsym can be preempted. + if (!b.includeInDynsym()) + return false; + + // Only default visibility symbols can be preempted. + if (b.visibility != STV_DEFAULT) + return false; + + // At this point copy relocations have not been created yet, so any + // symbol that is not defined locally is preemptible. + if (!b.isDefined()) + return true; + + // If we have a dynamic list it specifies which local symbols are preemptible. + if (config->hasDynamicList) + return false; + + if (!config->shared) + return false; + + // -Bsymbolic means that definitions are not preempted. + if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc())) + return false; + return true; +} + +// Create output section objects and add them to OutputSections. +template void Writer::finalizeSections() { + Out::preinitArray = findSection(".preinit_array"); + Out::initArray = findSection(".init_array"); + Out::finiArray = findSection(".fini_array"); + + // The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop + // symbols for sections, so that the runtime can get the start and end + // addresses of each section by section name. Add such symbols. + if (!config->relocatable) { + addStartEndSymbols(); + for (BaseCommand *base : script->sectionCommands) + if (auto *sec = dyn_cast(base)) + addStartStopSymbols(sec); + } + + // Add _DYNAMIC symbol. Unlike GNU gold, our _DYNAMIC symbol has no type. + // It should be okay as no one seems to care about the type. + // Even the author of gold doesn't remember why gold behaves that way. + // https://sourceware.org/ml/binutils/2002-03/msg00360.html + if (mainPart->dynamic->parent) + symtab->addSymbol(Defined{/*file=*/nullptr, "_DYNAMIC", STB_WEAK, + STV_HIDDEN, STT_NOTYPE, + /*value=*/0, /*size=*/0, mainPart->dynamic}); + + // Define __rel[a]_iplt_{start,end} symbols if needed. + addRelIpltSymbols(); + + // RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800 if not defined. + // This symbol should only be defined in an executable. + if (config->emachine == EM_RISCV && !config->shared) + ElfSym::riscvGlobalPointer = + addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800, + STV_DEFAULT, STB_GLOBAL); + + if (config->emachine == EM_X86_64) { + // On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a + // way that: + // + // 1) Without relaxation: it produces a dynamic TLSDESC relocation that + // computes 0. + // 2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address in + // the TLS block). + // + // 2) is special cased in @tpoff computation. To satisfy 1), we define it as + // an absolute symbol of zero. This is different from GNU linkers which + // define _TLS_MODULE_BASE_ relative to the first TLS section. + Symbol *s = symtab->find("_TLS_MODULE_BASE_"); + if (s && s->isUndefined()) { + s->resolve(Defined{/*file=*/nullptr, s->getName(), STB_GLOBAL, STV_HIDDEN, + STT_TLS, /*value=*/0, 0, + /*section=*/nullptr}); + ElfSym::tlsModuleBase = cast(s); + } + } + + // This responsible for splitting up .eh_frame section into + // pieces. The relocation scan uses those pieces, so this has to be + // earlier. + for (Partition &part : partitions) + finalizeSynthetic(part.ehFrame); + + symtab->forEachSymbol([](Symbol *s) { + if (!s->isPreemptible) + s->isPreemptible = computeIsPreemptible(*s); + }); + + // Scan relocations. This must be done after every symbol is declared so that + // we can correctly decide if a dynamic relocation is needed. + if (!config->relocatable) { + forEachRelSec(scanRelocations); + reportUndefinedSymbols(); + } + + addIRelativeRelocs(); + + if (in.plt && in.plt->isNeeded()) + in.plt->addSymbols(); + if (in.iplt && in.iplt->isNeeded()) + in.iplt->addSymbols(); + + if (!config->allowShlibUndefined) { + // Error on undefined symbols in a shared object, if all of its DT_NEEDED + // entires are seen. These cases would otherwise lead to runtime errors + // reported by the dynamic linker. + // + // ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker to + // catch more cases. That is too much for us. Our approach resembles the one + // used in ld.gold, achieves a good balance to be useful but not too smart. + for (SharedFile *file : sharedFiles) + file->allNeededIsKnown = + llvm::all_of(file->dtNeeded, [&](StringRef needed) { + return symtab->soNames.count(needed); + }); + + symtab->forEachSymbol([](Symbol *sym) { + if (sym->isUndefined() && !sym->isWeak()) + if (auto *f = dyn_cast_or_null(sym->file)) + if (f->allNeededIsKnown) + error(toString(f) + ": undefined reference to " + toString(*sym)); + }); + } + + // Now that we have defined all possible global symbols including linker- + // synthesized ones. Visit all symbols to give the finishing touches. + symtab->forEachSymbol([](Symbol *sym) { + if (!includeInSymtab(*sym)) + return; + if (in.symTab) + in.symTab->addSymbol(sym); + + if (sym->includeInDynsym()) { + partitions[sym->partition - 1].dynSymTab->addSymbol(sym); + if (auto *file = dyn_cast_or_null(sym->file)) + if (file->isNeeded && !sym->isUndefined()) + addVerneed(sym); + } + }); + + // We also need to scan the dynamic relocation tables of the other partitions + // and add any referenced symbols to the partition's dynsym. + for (Partition &part : MutableArrayRef(partitions).slice(1)) { + DenseSet syms; + for (const SymbolTableEntry &e : part.dynSymTab->getSymbols()) + syms.insert(e.sym); + for (DynamicReloc &reloc : part.relaDyn->relocs) + if (reloc.sym && !reloc.useSymVA && syms.insert(reloc.sym).second) + part.dynSymTab->addSymbol(reloc.sym); + } + + // Do not proceed if there was an undefined symbol. + if (errorCount()) + return; + + if (in.mipsGot) + in.mipsGot->build(); + + removeUnusedSyntheticSections(); + + sortSections(); + + // Now that we have the final list, create a list of all the + // OutputSections for convenience. + for (BaseCommand *base : script->sectionCommands) + if (auto *sec = dyn_cast(base)) + outputSections.push_back(sec); + + // Prefer command line supplied address over other constraints. + for (OutputSection *sec : outputSections) { + auto i = config->sectionStartMap.find(sec->name); + if (i != config->sectionStartMap.end()) + sec->addrExpr = [=] { return i->second; }; + } + + // This is a bit of a hack. A value of 0 means undef, so we set it + // to 1 to make __ehdr_start defined. The section number is not + // particularly relevant. + Out::elfHeader->sectionIndex = 1; + + for (size_t i = 0, e = outputSections.size(); i != e; ++i) { + OutputSection *sec = outputSections[i]; + sec->sectionIndex = i + 1; + sec->shName = in.shStrTab->addString(sec->name); + } + + // Binary and relocatable output does not have PHDRS. + // The headers have to be created before finalize as that can influence the + // image base and the dynamic section on mips includes the image base. + if (!config->relocatable && !config->oFormatBinary) { + for (Partition &part : partitions) { + part.phdrs = script->hasPhdrsCommands() ? script->createPhdrs() + : createPhdrs(part); + if (config->emachine == EM_ARM) { + // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME + addPhdrForSection(part, SHT_ARM_EXIDX, PT_ARM_EXIDX, PF_R); + } + if (config->emachine == EM_MIPS) { + // Add separate segments for MIPS-specific sections. + addPhdrForSection(part, SHT_MIPS_REGINFO, PT_MIPS_REGINFO, PF_R); + addPhdrForSection(part, SHT_MIPS_OPTIONS, PT_MIPS_OPTIONS, PF_R); + addPhdrForSection(part, SHT_MIPS_ABIFLAGS, PT_MIPS_ABIFLAGS, PF_R); + } + } + Out::programHeaders->size = sizeof(Elf_Phdr) * mainPart->phdrs.size(); + + // Find the TLS segment. This happens before the section layout loop so that + // Android relocation packing can look up TLS symbol addresses. We only need + // to care about the main partition here because all TLS symbols were moved + // to the main partition (see MarkLive.cpp). + for (PhdrEntry *p : mainPart->phdrs) + if (p->p_type == PT_TLS) + Out::tlsPhdr = p; + } + + // Some symbols are defined in term of program headers. Now that we + // have the headers, we can find out which sections they point to. + setReservedSymbolSections(); + + finalizeSynthetic(in.bss); + finalizeSynthetic(in.bssRelRo); + finalizeSynthetic(in.symTabShndx); + finalizeSynthetic(in.shStrTab); + finalizeSynthetic(in.strTab); + finalizeSynthetic(in.got); + finalizeSynthetic(in.mipsGot); + finalizeSynthetic(in.igotPlt); + finalizeSynthetic(in.gotPlt); + finalizeSynthetic(in.relaIplt); + finalizeSynthetic(in.relaPlt); + finalizeSynthetic(in.plt); + finalizeSynthetic(in.iplt); + finalizeSynthetic(in.ppc32Got2); + finalizeSynthetic(in.riscvSdata); + finalizeSynthetic(in.partIndex); + + // Dynamic section must be the last one in this list and dynamic + // symbol table section (dynSymTab) must be the first one. + for (Partition &part : partitions) { + finalizeSynthetic(part.armExidx); + finalizeSynthetic(part.dynSymTab); + finalizeSynthetic(part.gnuHashTab); + finalizeSynthetic(part.hashTab); + finalizeSynthetic(part.verDef); + finalizeSynthetic(part.relaDyn); + finalizeSynthetic(part.relrDyn); + finalizeSynthetic(part.ehFrameHdr); + finalizeSynthetic(part.verSym); + finalizeSynthetic(part.verNeed); + finalizeSynthetic(part.dynamic); + } + + if (!script->hasSectionsCommand && !config->relocatable) + fixSectionAlignments(); + + // SHFLinkOrder processing must be processed after relative section placements are + // known but before addresses are allocated. + resolveShfLinkOrder(); + + // This is used to: + // 1) Create "thunks": + // Jump instructions in many ISAs have small displacements, and therefore + // they cannot jump to arbitrary addresses in memory. For example, RISC-V + // JAL instruction can target only +-1 MiB from PC. It is a linker's + // responsibility to create and insert small pieces of code between + // sections to extend the ranges if jump targets are out of range. Such + // code pieces are called "thunks". + // + // We add thunks at this stage. We couldn't do this before this point + // because this is the earliest point where we know sizes of sections and + // their layouts (that are needed to determine if jump targets are in + // range). + // + // 2) Update the sections. We need to generate content that depends on the + // address of InputSections. For example, MIPS GOT section content or + // android packed relocations sections content. + // + // 3) Assign the final values for the linker script symbols. Linker scripts + // sometimes using forward symbol declarations. We want to set the correct + // values. They also might change after adding the thunks. + finalizeAddressDependentContent(); + + // finalizeAddressDependentContent may have added local symbols to the static symbol table. + finalizeSynthetic(in.symTab); + finalizeSynthetic(in.ppc64LongBranchTarget); + + // Fill other section headers. The dynamic table is finalized + // at the end because some tags like RELSZ depend on result + // of finalizing other sections. + for (OutputSection *sec : outputSections) + sec->finalize(); +} + +// Ensure data sections are not mixed with executable sections when +// -execute-only is used. -execute-only is a feature to make pages executable +// but not readable, and the feature is currently supported only on AArch64. +template void Writer::checkExecuteOnly() { + if (!config->executeOnly) + return; + + for (OutputSection *os : outputSections) + if (os->flags & SHF_EXECINSTR) + for (InputSection *isec : getInputSections(os)) + if (!(isec->flags & SHF_EXECINSTR)) + error("cannot place " + toString(isec) + " into " + toString(os->name) + + ": -execute-only does not support intermingling data and code"); +} + +// The linker is expected to define SECNAME_start and SECNAME_end +// symbols for a few sections. This function defines them. +template void Writer::addStartEndSymbols() { + // If a section does not exist, there's ambiguity as to how we + // define _start and _end symbols for an init/fini section. Since + // the loader assume that the symbols are always defined, we need to + // always define them. But what value? The loader iterates over all + // pointers between _start and _end to run global ctors/dtors, so if + // the section is empty, their symbol values don't actually matter + // as long as _start and _end point to the same location. + // + // That said, we don't want to set the symbols to 0 (which is + // probably the simplest value) because that could cause some + // program to fail to link due to relocation overflow, if their + // program text is above 2 GiB. We use the address of the .text + // section instead to prevent that failure. + // + // In a rare sitaution, .text section may not exist. If that's the + // case, use the image base address as a last resort. + OutputSection *Default = findSection(".text"); + if (!Default) + Default = Out::elfHeader; + + auto define = [=](StringRef start, StringRef end, OutputSection *os) { + if (os) { + addOptionalRegular(start, os, 0); + addOptionalRegular(end, os, -1); + } else { + addOptionalRegular(start, Default, 0); + addOptionalRegular(end, Default, 0); + } + }; + + define("__preinit_array_start", "__preinit_array_end", Out::preinitArray); + define("__init_array_start", "__init_array_end", Out::initArray); + define("__fini_array_start", "__fini_array_end", Out::finiArray); + + if (OutputSection *sec = findSection(".ARM.exidx")) + define("__exidx_start", "__exidx_end", sec); +} + +// If a section name is valid as a C identifier (which is rare because of +// the leading '.'), linkers are expected to define __start_ and +// __stop_ symbols. They are at beginning and end of the section, +// respectively. This is not requested by the ELF standard, but GNU ld and +// gold provide the feature, and used by many programs. +template +void Writer::addStartStopSymbols(OutputSection *sec) { + StringRef s = sec->name; + if (!isValidCIdentifier(s)) + return; + addOptionalRegular(saver.save("__start_" + s), sec, 0, STV_PROTECTED); + addOptionalRegular(saver.save("__stop_" + s), sec, -1, STV_PROTECTED); +} + +static bool needsPtLoad(OutputSection *sec) { + if (!(sec->flags & SHF_ALLOC) || sec->noload) + return false; + + // Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is + // responsible for allocating space for them, not the PT_LOAD that + // contains the TLS initialization image. + if ((sec->flags & SHF_TLS) && sec->type == SHT_NOBITS) + return false; + return true; +} + +// Linker scripts are responsible for aligning addresses. Unfortunately, most +// linker scripts are designed for creating two PT_LOADs only, one RX and one +// RW. This means that there is no alignment in the RO to RX transition and we +// cannot create a PT_LOAD there. +static uint64_t computeFlags(uint64_t flags) { + if (config->omagic) + return PF_R | PF_W | PF_X; + if (config->executeOnly && (flags & PF_X)) + return flags & ~PF_R; + if (config->singleRoRx && !(flags & PF_W)) + return flags | PF_X; + return flags; +} + +// Decide which program headers to create and which sections to include in each +// one. +template +std::vector Writer::createPhdrs(Partition &part) { + std::vector ret; + auto addHdr = [&](unsigned type, unsigned flags) -> PhdrEntry * { + ret.push_back(make(type, flags)); + return ret.back(); + }; + + unsigned partNo = part.getNumber(); + bool isMain = partNo == 1; + + // The first phdr entry is PT_PHDR which describes the program header itself. + if (isMain) + addHdr(PT_PHDR, PF_R)->add(Out::programHeaders); + else + addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent()); + + // PT_INTERP must be the second entry if exists. + if (OutputSection *cmd = findSection(".interp", partNo)) + addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd); + + // Add the first PT_LOAD segment for regular output sections. + uint64_t flags = computeFlags(PF_R); + PhdrEntry *load = nullptr; + + // Add the headers. We will remove them if they don't fit. + // In the other partitions the headers are ordinary sections, so they don't + // need to be added here. + if (isMain) { + load = addHdr(PT_LOAD, flags); + load->add(Out::elfHeader); + load->add(Out::programHeaders); + } + + // PT_GNU_RELRO includes all sections that should be marked as + // read-only by dynamic linker after proccessing relocations. + // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give + // an error message if more than one PT_GNU_RELRO PHDR is required. + PhdrEntry *relRo = make(PT_GNU_RELRO, PF_R); + bool inRelroPhdr = false; + OutputSection *relroEnd = nullptr; + for (OutputSection *sec : outputSections) { + if (sec->partition != partNo || !needsPtLoad(sec)) + continue; + if (isRelroSection(sec)) { + inRelroPhdr = true; + if (!relroEnd) + relRo->add(sec); + else + error("section: " + sec->name + " is not contiguous with other relro" + + " sections"); + } else if (inRelroPhdr) { + inRelroPhdr = false; + relroEnd = sec; + } + } + + for (OutputSection *sec : outputSections) { + if (!(sec->flags & SHF_ALLOC)) + break; + if (!needsPtLoad(sec)) + continue; + + // Normally, sections in partitions other than the current partition are + // ignored. But partition number 255 is a special case: it contains the + // partition end marker (.part.end). It needs to be added to the main + // partition so that a segment is created for it in the main partition, + // which will cause the dynamic loader to reserve space for the other + // partitions. + if (sec->partition != partNo) { + if (isMain && sec->partition == 255) + addHdr(PT_LOAD, computeFlags(sec->getPhdrFlags()))->add(sec); + continue; + } + + // Segments are contiguous memory regions that has the same attributes + // (e.g. executable or writable). There is one phdr for each segment. + // Therefore, we need to create a new phdr when the next section has + // different flags or is loaded at a discontiguous address or memory + // region using AT or AT> linker script command, respectively. At the same + // time, we don't want to create a separate load segment for the headers, + // even if the first output section has an AT or AT> attribute. + uint64_t newFlags = computeFlags(sec->getPhdrFlags()); + if (!load || + ((sec->lmaExpr || + (sec->lmaRegion && (sec->lmaRegion != load->firstSec->lmaRegion))) && + load->lastSec != Out::programHeaders) || + sec->memRegion != load->firstSec->memRegion || flags != newFlags || + sec == relroEnd) { + load = addHdr(PT_LOAD, newFlags); + flags = newFlags; + } + + load->add(sec); + } + + // Add a TLS segment if any. + PhdrEntry *tlsHdr = make(PT_TLS, PF_R); + for (OutputSection *sec : outputSections) + if (sec->partition == partNo && sec->flags & SHF_TLS) + tlsHdr->add(sec); + if (tlsHdr->firstSec) + ret.push_back(tlsHdr); + + // Add an entry for .dynamic. + if (OutputSection *sec = part.dynamic->getParent()) + addHdr(PT_DYNAMIC, sec->getPhdrFlags())->add(sec); + + if (relRo->firstSec) + ret.push_back(relRo); + + // PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr. + if (part.ehFrame->isNeeded() && part.ehFrameHdr && + part.ehFrame->getParent() && part.ehFrameHdr->getParent()) + addHdr(PT_GNU_EH_FRAME, part.ehFrameHdr->getParent()->getPhdrFlags()) + ->add(part.ehFrameHdr->getParent()); + + // PT_OPENBSD_RANDOMIZE is an OpenBSD-specific feature. That makes + // the dynamic linker fill the segment with random data. + if (OutputSection *cmd = findSection(".openbsd.randomdata", partNo)) + addHdr(PT_OPENBSD_RANDOMIZE, cmd->getPhdrFlags())->add(cmd); + + // PT_GNU_STACK is a special section to tell the loader to make the + // pages for the stack non-executable. If you really want an executable + // stack, you can pass -z execstack, but that's not recommended for + // security reasons. + unsigned perm = PF_R | PF_W; + if (config->zExecstack) + perm |= PF_X; + addHdr(PT_GNU_STACK, perm)->p_memsz = config->zStackSize; + + // PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable + // is expected to perform W^X violations, such as calling mprotect(2) or + // mmap(2) with PROT_WRITE | PROT_EXEC, which is prohibited by default on + // OpenBSD. + if (config->zWxneeded) + addHdr(PT_OPENBSD_WXNEEDED, PF_X); + + // Create one PT_NOTE per a group of contiguous SHT_NOTE sections with the + // same alignment. + PhdrEntry *note = nullptr; + for (OutputSection *sec : outputSections) { + if (sec->partition != partNo) + continue; + if (sec->type == SHT_NOTE && (sec->flags & SHF_ALLOC)) { + if (!note || sec->lmaExpr || note->lastSec->alignment != sec->alignment) + note = addHdr(PT_NOTE, PF_R); + note->add(sec); + } else { + note = nullptr; + } + } + return ret; +} + +template +void Writer::addPhdrForSection(Partition &part, unsigned shType, + unsigned pType, unsigned pFlags) { + unsigned partNo = part.getNumber(); + auto i = llvm::find_if(outputSections, [=](OutputSection *cmd) { + return cmd->partition == partNo && cmd->type == shType; + }); + if (i == outputSections.end()) + return; + + PhdrEntry *entry = make(pType, pFlags); + entry->add(*i); + part.phdrs.push_back(entry); +} + +// The first section of each PT_LOAD, the first section in PT_GNU_RELRO and the +// first section after PT_GNU_RELRO have to be page aligned so that the dynamic +// linker can set the permissions. +template void Writer::fixSectionAlignments() { + auto pageAlign = [](OutputSection *cmd) { + if (cmd && !cmd->addrExpr) + cmd->addrExpr = [=] { + return alignTo(script->getDot(), config->maxPageSize); + }; + }; + + for (Partition &part : partitions) { + for (const PhdrEntry *p : part.phdrs) + if (p->p_type == PT_LOAD && p->firstSec) + pageAlign(p->firstSec); + } +} + +// Compute an in-file position for a given section. The file offset must be the +// same with its virtual address modulo the page size, so that the loader can +// load executables without any address adjustment. +static uint64_t computeFileOffset(OutputSection *os, uint64_t off) { + // The first section in a PT_LOAD has to have congruent offset and address + // module the page size. + if (os->ptLoad && os->ptLoad->firstSec == os) { + uint64_t alignment = + std::max(os->ptLoad->p_align, config->maxPageSize); + return alignTo(off, alignment, os->addr); + } + + // File offsets are not significant for .bss sections other than the first one + // in a PT_LOAD. By convention, we keep section offsets monotonically + // increasing rather than setting to zero. + if (os->type == SHT_NOBITS) + return off; + + // If the section is not in a PT_LOAD, we just have to align it. + if (!os->ptLoad) + return alignTo(off, os->alignment); + + // If two sections share the same PT_LOAD the file offset is calculated + // using this formula: Off2 = Off1 + (VA2 - VA1). + OutputSection *first = os->ptLoad->firstSec; + return first->offset + os->addr - first->addr; +} + +// Set an in-file position to a given section and returns the end position of +// the section. +static uint64_t setFileOffset(OutputSection *os, uint64_t off) { + off = computeFileOffset(os, off); + os->offset = off; + + if (os->type == SHT_NOBITS) + return off; + return off + os->size; +} + +template void Writer::assignFileOffsetsBinary() { + uint64_t off = 0; + for (OutputSection *sec : outputSections) + if (sec->flags & SHF_ALLOC) + off = setFileOffset(sec, off); + fileSize = alignTo(off, config->wordsize); +} + +static std::string rangeToString(uint64_t addr, uint64_t len) { + return "[0x" + utohexstr(addr) + ", 0x" + utohexstr(addr + len - 1) + "]"; +} + +// Assign file offsets to output sections. +template void Writer::assignFileOffsets() { + uint64_t off = 0; + off = setFileOffset(Out::elfHeader, off); + off = setFileOffset(Out::programHeaders, off); + + PhdrEntry *lastRX = nullptr; + for (Partition &part : partitions) + for (PhdrEntry *p : part.phdrs) + if (p->p_type == PT_LOAD && (p->p_flags & PF_X)) + lastRX = p; + + for (OutputSection *sec : outputSections) { + off = setFileOffset(sec, off); + if (script->hasSectionsCommand) + continue; + + // If this is a last section of the last executable segment and that + // segment is the last loadable segment, align the offset of the + // following section to avoid loading non-segments parts of the file. + if (lastRX && lastRX->lastSec == sec) + off = alignTo(off, config->commonPageSize); + } + + sectionHeaderOff = alignTo(off, config->wordsize); + fileSize = sectionHeaderOff + (outputSections.size() + 1) * sizeof(Elf_Shdr); + + // Our logic assumes that sections have rising VA within the same segment. + // With use of linker scripts it is possible to violate this rule and get file + // offset overlaps or overflows. That should never happen with a valid script + // which does not move the location counter backwards and usually scripts do + // not do that. Unfortunately, there are apps in the wild, for example, Linux + // kernel, which control segment distribution explicitly and move the counter + // backwards, so we have to allow doing that to support linking them. We + // perform non-critical checks for overlaps in checkSectionOverlap(), but here + // we want to prevent file size overflows because it would crash the linker. + for (OutputSection *sec : outputSections) { + if (sec->type == SHT_NOBITS) + continue; + if ((sec->offset > fileSize) || (sec->offset + sec->size > fileSize)) + error("unable to place section " + sec->name + " at file offset " + + rangeToString(sec->offset, sec->size) + + "; check your linker script for overflows"); + } +} + +// Finalize the program headers. We call this function after we assign +// file offsets and VAs to all sections. +template void Writer::setPhdrs(Partition &part) { + for (PhdrEntry *p : part.phdrs) { + OutputSection *first = p->firstSec; + OutputSection *last = p->lastSec; + + if (first) { + p->p_filesz = last->offset - first->offset; + if (last->type != SHT_NOBITS) + p->p_filesz += last->size; + + p->p_memsz = last->addr + last->size - first->addr; + p->p_offset = first->offset; + p->p_vaddr = first->addr; + + // File offsets in partitions other than the main partition are relative + // to the offset of the ELF headers. Perform that adjustment now. + if (part.elfHeader) + p->p_offset -= part.elfHeader->getParent()->offset; + + if (!p->hasLMA) + p->p_paddr = first->getLMA(); + } + + if (p->p_type == PT_LOAD) { + p->p_align = std::max(p->p_align, config->maxPageSize); + } else if (p->p_type == PT_GNU_RELRO) { + p->p_align = 1; + // The glibc dynamic loader rounds the size down, so we need to round up + // to protect the last page. This is a no-op on FreeBSD which always + // rounds up. + p->p_memsz = alignTo(p->p_memsz, config->commonPageSize); + } + } +} + +// A helper struct for checkSectionOverlap. +namespace { +struct SectionOffset { + OutputSection *sec; + uint64_t offset; +}; +} // namespace + +// Check whether sections overlap for a specific address range (file offsets, +// load and virtual adresses). +static void checkOverlap(StringRef name, std::vector §ions, + bool isVirtualAddr) { + llvm::sort(sections, [=](const SectionOffset &a, const SectionOffset &b) { + return a.offset < b.offset; + }); + + // Finding overlap is easy given a vector is sorted by start position. + // If an element starts before the end of the previous element, they overlap. + for (size_t i = 1, end = sections.size(); i < end; ++i) { + SectionOffset a = sections[i - 1]; + SectionOffset b = sections[i]; + if (b.offset >= a.offset + a.sec->size) + continue; + + // If both sections are in OVERLAY we allow the overlapping of virtual + // addresses, because it is what OVERLAY was designed for. + if (isVirtualAddr && a.sec->inOverlay && b.sec->inOverlay) + continue; + + errorOrWarn("section " + a.sec->name + " " + name + + " range overlaps with " + b.sec->name + "\n>>> " + a.sec->name + + " range is " + rangeToString(a.offset, a.sec->size) + "\n>>> " + + b.sec->name + " range is " + + rangeToString(b.offset, b.sec->size)); + } +} + +// Check for overlapping sections and address overflows. +// +// In this function we check that none of the output sections have overlapping +// file offsets. For SHF_ALLOC sections we also check that the load address +// ranges and the virtual address ranges don't overlap +template void Writer::checkSections() { + // First, check that section's VAs fit in available address space for target. + for (OutputSection *os : outputSections) + if ((os->addr + os->size < os->addr) || + (!ELFT::Is64Bits && os->addr + os->size > UINT32_MAX)) + errorOrWarn("section " + os->name + " at 0x" + utohexstr(os->addr) + + " of size 0x" + utohexstr(os->size) + + " exceeds available address space"); + + // Check for overlapping file offsets. In this case we need to skip any + // section marked as SHT_NOBITS. These sections don't actually occupy space in + // the file so Sec->Offset + Sec->Size can overlap with others. If --oformat + // binary is specified only add SHF_ALLOC sections are added to the output + // file so we skip any non-allocated sections in that case. + std::vector fileOffs; + for (OutputSection *sec : outputSections) + if (sec->size > 0 && sec->type != SHT_NOBITS && + (!config->oFormatBinary || (sec->flags & SHF_ALLOC))) + fileOffs.push_back({sec, sec->offset}); + checkOverlap("file", fileOffs, false); + + // When linking with -r there is no need to check for overlapping virtual/load + // addresses since those addresses will only be assigned when the final + // executable/shared object is created. + if (config->relocatable) + return; + + // Checking for overlapping virtual and load addresses only needs to take + // into account SHF_ALLOC sections since others will not be loaded. + // Furthermore, we also need to skip SHF_TLS sections since these will be + // mapped to other addresses at runtime and can therefore have overlapping + // ranges in the file. + std::vector vmas; + for (OutputSection *sec : outputSections) + if (sec->size > 0 && (sec->flags & SHF_ALLOC) && !(sec->flags & SHF_TLS)) + vmas.push_back({sec, sec->addr}); + checkOverlap("virtual address", vmas, true); + + // Finally, check that the load addresses don't overlap. This will usually be + // the same as the virtual addresses but can be different when using a linker + // script with AT(). + std::vector lmas; + for (OutputSection *sec : outputSections) + if (sec->size > 0 && (sec->flags & SHF_ALLOC) && !(sec->flags & SHF_TLS)) + lmas.push_back({sec, sec->getLMA()}); + checkOverlap("load address", lmas, false); +} + +// The entry point address is chosen in the following ways. +// +// 1. the '-e' entry command-line option; +// 2. the ENTRY(symbol) command in a linker control script; +// 3. the value of the symbol _start, if present; +// 4. the number represented by the entry symbol, if it is a number; +// 5. the address of the first byte of the .text section, if present; +// 6. the address 0. +static uint64_t getEntryAddr() { + // Case 1, 2 or 3 + if (Symbol *b = symtab->find(config->entry)) + return b->getVA(); + + // Case 4 + uint64_t addr; + if (to_integer(config->entry, addr)) + return addr; + + // Case 5 + if (OutputSection *sec = findSection(".text")) { + if (config->warnMissingEntry) + warn("cannot find entry symbol " + config->entry + "; defaulting to 0x" + + utohexstr(sec->addr)); + return sec->addr; + } + + // Case 6 + if (config->warnMissingEntry) + warn("cannot find entry symbol " + config->entry + + "; not setting start address"); + return 0; +} + +static uint16_t getELFType() { + if (config->isPic) + return ET_DYN; + if (config->relocatable) + return ET_REL; + return ET_EXEC; +} + +template void Writer::writeHeader() { + writeEhdr(Out::bufferStart, *mainPart); + writePhdrs(Out::bufferStart + sizeof(Elf_Ehdr), *mainPart); + + auto *eHdr = reinterpret_cast(Out::bufferStart); + eHdr->e_type = getELFType(); + eHdr->e_entry = getEntryAddr(); + eHdr->e_shoff = sectionHeaderOff; + + // Write the section header table. + // + // The ELF header can only store numbers up to SHN_LORESERVE in the e_shnum + // and e_shstrndx fields. When the value of one of these fields exceeds + // SHN_LORESERVE ELF requires us to put sentinel values in the ELF header and + // use fields in the section header at index 0 to store + // the value. The sentinel values and fields are: + // e_shnum = 0, SHdrs[0].sh_size = number of sections. + // e_shstrndx = SHN_XINDEX, SHdrs[0].sh_link = .shstrtab section index. + auto *sHdrs = reinterpret_cast(Out::bufferStart + eHdr->e_shoff); + size_t num = outputSections.size() + 1; + if (num >= SHN_LORESERVE) + sHdrs->sh_size = num; + else + eHdr->e_shnum = num; + + uint32_t strTabIndex = in.shStrTab->getParent()->sectionIndex; + if (strTabIndex >= SHN_LORESERVE) { + sHdrs->sh_link = strTabIndex; + eHdr->e_shstrndx = SHN_XINDEX; + } else { + eHdr->e_shstrndx = strTabIndex; + } + + for (OutputSection *sec : outputSections) + sec->writeHeaderTo(++sHdrs); +} + +// Open a result file. +template void Writer::openFile() { + uint64_t maxSize = config->is64 ? INT64_MAX : UINT32_MAX; + if (fileSize != size_t(fileSize) || maxSize < fileSize) { + error("output file too large: " + Twine(fileSize) + " bytes"); + return; + } + + unlinkAsync(config->outputFile); + unsigned flags = 0; + if (!config->relocatable) + flags = FileOutputBuffer::F_executable; + Expected> bufferOrErr = + FileOutputBuffer::create(config->outputFile, fileSize, flags); + + if (!bufferOrErr) { + error("failed to open " + config->outputFile + ": " + + llvm::toString(bufferOrErr.takeError())); + return; + } + buffer = std::move(*bufferOrErr); + Out::bufferStart = buffer->getBufferStart(); +} + +template void Writer::writeSectionsBinary() { + for (OutputSection *sec : outputSections) + if (sec->flags & SHF_ALLOC) + sec->writeTo(Out::bufferStart + sec->offset); +} + +static void fillTrap(uint8_t *i, uint8_t *end) { + for (; i + 4 <= end; i += 4) + memcpy(i, &target->trapInstr, 4); +} + +// Fill the last page of executable segments with trap instructions +// instead of leaving them as zero. Even though it is not required by any +// standard, it is in general a good thing to do for security reasons. +// +// We'll leave other pages in segments as-is because the rest will be +// overwritten by output sections. +template void Writer::writeTrapInstr() { + if (script->hasSectionsCommand) + return; + + for (Partition &part : partitions) { + // Fill the last page. + for (PhdrEntry *p : part.phdrs) + if (p->p_type == PT_LOAD && (p->p_flags & PF_X)) + fillTrap(Out::bufferStart + alignDown(p->firstSec->offset + p->p_filesz, + config->commonPageSize), + Out::bufferStart + alignTo(p->firstSec->offset + p->p_filesz, + config->commonPageSize)); + + // Round up the file size of the last segment to the page boundary iff it is + // an executable segment to ensure that other tools don't accidentally + // trim the instruction padding (e.g. when stripping the file). + PhdrEntry *last = nullptr; + for (PhdrEntry *p : part.phdrs) + if (p->p_type == PT_LOAD) + last = p; + + if (last && (last->p_flags & PF_X)) + last->p_memsz = last->p_filesz = + alignTo(last->p_filesz, config->commonPageSize); + } +} + +// Write section contents to a mmap'ed file. +template void Writer::writeSections() { + // In -r or -emit-relocs mode, write the relocation sections first as in + // ELf_Rel targets we might find out that we need to modify the relocated + // section while doing it. + for (OutputSection *sec : outputSections) + if (sec->type == SHT_REL || sec->type == SHT_RELA) + sec->writeTo(Out::bufferStart + sec->offset); + + for (OutputSection *sec : outputSections) + if (sec->type != SHT_REL && sec->type != SHT_RELA) + sec->writeTo(Out::bufferStart + sec->offset); +} + +// Split one uint8 array into small pieces of uint8 arrays. +static std::vector> split(ArrayRef arr, + size_t chunkSize) { + std::vector> ret; + while (arr.size() > chunkSize) { + ret.push_back(arr.take_front(chunkSize)); + arr = arr.drop_front(chunkSize); + } + if (!arr.empty()) + ret.push_back(arr); + return ret; +} + +// Computes a hash value of Data using a given hash function. +// In order to utilize multiple cores, we first split data into 1MB +// chunks, compute a hash for each chunk, and then compute a hash value +// of the hash values. +static void +computeHash(llvm::MutableArrayRef hashBuf, + llvm::ArrayRef data, + std::function arr)> hashFn) { + std::vector> chunks = split(data, 1024 * 1024); + std::vector hashes(chunks.size() * hashBuf.size()); + + // Compute hash values. + parallelForEachN(0, chunks.size(), [&](size_t i) { + hashFn(hashes.data() + i * hashBuf.size(), chunks[i]); + }); + + // Write to the final output buffer. + hashFn(hashBuf.data(), hashes); +} + +template void Writer::writeBuildId() { + if (!mainPart->buildId || !mainPart->buildId->getParent()) + return; + + if (config->buildId == BuildIdKind::Hexstring) { + for (Partition &part : partitions) + part.buildId->writeBuildId(config->buildIdVector); + return; + } + + // Compute a hash of all sections of the output file. + size_t hashSize = mainPart->buildId->hashSize; + std::vector buildId(hashSize); + llvm::ArrayRef buf{Out::bufferStart, size_t(fileSize)}; + + switch (config->buildId) { + case BuildIdKind::Fast: + computeHash(buildId, buf, [](uint8_t *dest, ArrayRef arr) { + write64le(dest, xxHash64(arr)); + }); + break; + case BuildIdKind::Md5: + computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef arr) { + memcpy(dest, MD5::hash(arr).data(), hashSize); + }); + break; + case BuildIdKind::Sha1: + computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef arr) { + memcpy(dest, SHA1::hash(arr).data(), hashSize); + }); + break; + case BuildIdKind::Uuid: + if (auto ec = llvm::getRandomBytes(buildId.data(), hashSize)) + error("entropy source failure: " + ec.message()); + break; + default: + llvm_unreachable("unknown BuildIdKind"); + } + for (Partition &part : partitions) + part.buildId->writeBuildId(buildId); +} + +template void elf::writeResult(); +template void elf::writeResult(); +template void elf::writeResult(); +template void elf::writeResult(); Property changes on: vendor/lld/lld-release_900-r372316/ELF/Writer.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/PPC.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/PPC.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/PPC.cpp (revision 352529) @@ -0,0 +1,441 @@ +//===- PPC.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "OutputSections.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class PPC final : public TargetInfo { +public: + PPC(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotHeader(uint8_t *buf) const override; + void writePltHeader(uint8_t *buf) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + int getTlsGdRelaxSkip(RelType type) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +static uint16_t lo(uint32_t v) { return v; } +static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; } + +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); +} + +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); +} + +void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { + // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an + // absolute address from a specific .plt slot (usually called .got.plt on + // other targets) and jumps there. + // + // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load + // time. The .glink section is not used. + // b) With lazy binding, the .plt entry points to a `b PLTresolve` + // instruction in .glink, filled in by PPC::writeGotPlt(). + + // Write N `b PLTresolve` first. + for (size_t i = 0; i != numEntries; ++i) + write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i)); + buf += 4 * numEntries; + + // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve() + // computes the PLT index (by computing the distance from the landing b to + // itself) and calls _dl_runtime_resolve() (in glibc). + uint32_t got = in.got->getVA(); + uint32_t glink = in.plt->getVA(); // VA of .glink + const uint8_t *end = buf + 64; + if (config->isPic) { + uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12; + uint32_t gotBcl = got + 4 - (glink + afterBcl); + write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha + write32(buf + 4, 0x7c0802a6); // mflr r0 + write32(buf + 8, 0x429f0005); // bcl 20,30,.+4 + write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l + write32(buf + 16, 0x7d8802a6); // mflr r12 + write32(buf + 20, 0x7c0803a6); // mtlr r0 + write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12 + write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha + if (ha(gotBcl) == ha(gotBcl + 4)) { + write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, + 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12) + } else { + write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12) + } + write32(buf + 40, 0x7c0903a6); // mtctr 0 + write32(buf + 44, 0x7c0b5a14); // add r0,11,11 + write32(buf + 48, 0x7d605a14); // add r11,0,11 + write32(buf + 52, 0x4e800420); // bctr + buf += 56; + } else { + write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha + write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha + if (ha(got + 4) == ha(got + 8)) + write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12) + else + write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12) + write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l + write32(buf + 16, 0x7c0903a6); // mtctr r0 + write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11 + if (ha(got + 4) == ha(got + 8)) + write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12) + else + write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12) + write32(buf + 28, 0x7d605a14); // add r11,r0,r11 + write32(buf + 32, 0x4e800420); // bctr + buf += 36; + } + + // Pad with nop. They should not be executed. + for (; buf < end; buf += 4) + write32(buf, 0x60000000); +} + +PPC::PPC() { + gotRel = R_PPC_GLOB_DAT; + noneRel = R_PPC_NONE; + pltRel = R_PPC_JMP_SLOT; + relativeRel = R_PPC_RELATIVE; + iRelativeRel = R_PPC_IRELATIVE; + symbolicRel = R_PPC_ADDR32; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 3; + gotPltHeaderEntriesNum = 0; + pltHeaderSize = 64; // size of PLTresolve in .glink + pltEntrySize = 4; + + needsThunks = true; + + tlsModuleIndexRel = R_PPC_DTPMOD32; + tlsOffsetRel = R_PPC_DTPREL32; + tlsGotRel = R_PPC_TPREL32; + + defaultMaxPageSize = 65536; + defaultImageBase = 0x10000000; + + write32(trapInstr.data(), 0x7fe00008); +} + +void PPC::writeGotHeader(uint8_t *buf) const { + // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC + // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], + // link_map in _GLOBAL_OFFSET_TABLE_[2]. + write32(buf, mainPart->dynamic->getVA()); +} + +void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { + // Address of the symbol resolver stub in .glink . + write32(buf, in.plt->getVA() + 4 * s.pltIndex); +} + +bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC_REL24 && type != R_PPC_PLTREL24) + return false; + if (s.isInPlt()) + return true; + if (s.isUndefWeak()) + return false; + return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA())); +} + +uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } + +bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t offset = dst - src; + if (type == R_PPC_REL24 || type == R_PPC_PLTREL24) + return isInt<26>(offset); + llvm_unreachable("unsupported relocation type used in branch"); +} + +RelExpr PPC::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_PPC_NONE: + return R_NONE; + case R_PPC_ADDR16_HA: + case R_PPC_ADDR16_HI: + case R_PPC_ADDR16_LO: + case R_PPC_ADDR32: + return R_ABS; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL32: + return R_DTPREL; + case R_PPC_REL14: + case R_PPC_REL32: + case R_PPC_LOCAL24PC: + case R_PPC_REL16_LO: + case R_PPC_REL16_HI: + case R_PPC_REL16_HA: + return R_PC; + case R_PPC_GOT16: + return R_GOT_OFF; + case R_PPC_REL24: + return R_PLT_PC; + case R_PPC_PLTREL24: + return R_PPC32_PLTREL; + case R_PPC_GOT_TLSGD16: + return R_TLSGD_GOT; + case R_PPC_GOT_TLSLD16: + return R_TLSLD_GOT; + case R_PPC_GOT_TPREL16: + return R_GOT_OFF; + case R_PPC_TLS: + return R_TLSIE_HINT; + case R_PPC_TLSGD: + return R_TLSDESC_CALL; + case R_PPC_TLSLD: + return R_TLSLD_HINT; + case R_PPC_TPREL16: + case R_PPC_TPREL16_HA: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + return R_TLS; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +RelType PPC::getDynRel(RelType type) const { + if (type == R_PPC_ADDR32) + return type; + return R_PPC_NONE; +} + +static std::pair fromDTPREL(RelType type, uint64_t val) { + uint64_t dtpBiasedVal = val - 0x8000; + switch (type) { + case R_PPC_DTPREL16: + return {R_PPC64_ADDR16, dtpBiasedVal}; + case R_PPC_DTPREL16_HA: + return {R_PPC_ADDR16_HA, dtpBiasedVal}; + case R_PPC_DTPREL16_HI: + return {R_PPC_ADDR16_HI, dtpBiasedVal}; + case R_PPC_DTPREL16_LO: + return {R_PPC_ADDR16_LO, dtpBiasedVal}; + case R_PPC_DTPREL32: + return {R_PPC_ADDR32, dtpBiasedVal}; + default: + return {type, val}; + } +} + +void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + RelType newType; + std::tie(newType, val) = fromDTPREL(type, val); + switch (newType) { + case R_PPC_ADDR16: + checkIntUInt(loc, val, 16, type); + write16(loc, val); + break; + case R_PPC_GOT16: + case R_PPC_GOT_TLSGD16: + case R_PPC_GOT_TLSLD16: + case R_PPC_GOT_TPREL16: + case R_PPC_TPREL16: + checkInt(loc, val, 16, type); + write16(loc, val); + break; + case R_PPC_ADDR16_HA: + case R_PPC_DTPREL16_HA: + case R_PPC_GOT_TLSGD16_HA: + case R_PPC_GOT_TLSLD16_HA: + case R_PPC_GOT_TPREL16_HA: + case R_PPC_REL16_HA: + case R_PPC_TPREL16_HA: + write16(loc, ha(val)); + break; + case R_PPC_ADDR16_HI: + case R_PPC_DTPREL16_HI: + case R_PPC_GOT_TLSGD16_HI: + case R_PPC_GOT_TLSLD16_HI: + case R_PPC_GOT_TPREL16_HI: + case R_PPC_REL16_HI: + case R_PPC_TPREL16_HI: + write16(loc, val >> 16); + break; + case R_PPC_ADDR16_LO: + case R_PPC_DTPREL16_LO: + case R_PPC_GOT_TLSGD16_LO: + case R_PPC_GOT_TLSLD16_LO: + case R_PPC_GOT_TPREL16_LO: + case R_PPC_REL16_LO: + case R_PPC_TPREL16_LO: + write16(loc, val); + break; + case R_PPC_ADDR32: + case R_PPC_REL32: + write32(loc, val); + break; + case R_PPC_REL14: { + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); + break; + } + case R_PPC_REL24: + case R_PPC_LOCAL24PC: + case R_PPC_PLTREL24: { + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); + break; + } + default: + llvm_unreachable("unknown relocation"); + } +} + +RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return expr; +} + +int PPC::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation + // R_PPC_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC_TLSGD || type == R_PPC_TLSLD) + return 2; + return 1; +} + +void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: { + // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) + uint32_t insn = readFromHalf16(loc); + writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000)); + relocateOne(loc, R_PPC_GOT_TPREL16, val); + break; + } + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 + write32(loc, 0x7c631214); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: + // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha + writeFromHalf16(loc, 0x3c620000 | ha(val)); + break; + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l + write32(loc, 0x38630000 | lo(val)); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSLD16: + // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 + writeFromHalf16(loc, 0x3c620000); + break; + case R_PPC_TLSLD: + // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel + // = r3+x-0x7000, so add 4096 to r3. + // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 + write32(loc, 0x38631000); + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + relocateOne(loc, type, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TPREL16: { + // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha + uint32_t rt = readFromHalf16(loc) & 0x03e00000; + writeFromHalf16(loc, 0x3c020000 | rt | ha(val)); + break; + } + case R_PPC_TLS: { + uint32_t insn = read32(loc); + if (insn >> 26 != 31) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l + uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val)); + break; + } + default: + llvm_unreachable("unsupported relocation for TLS IE to LE relaxation"); + } +} + +TargetInfo *elf::getPPCTargetInfo() { + static PPC target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/PPC.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/PPC64.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/PPC64.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/PPC64.cpp (revision 352529) @@ -0,0 +1,1095 @@ +//===- PPC64.cpp ----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +static uint64_t ppc64TocOffset = 0x8000; +static uint64_t dynamicThreadPointerOffset = 0x8000; + +// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform +// instructions that can be used as part of the initial exec TLS sequence. +enum XFormOpcd { + LBZX = 87, + LHZX = 279, + LWZX = 23, + LDX = 21, + STBX = 215, + STHX = 407, + STWX = 151, + STDX = 149, + ADD = 266, +}; + +enum DFormOpcd { + LBZ = 34, + LBZU = 35, + LHZ = 40, + LHZU = 41, + LHAU = 43, + LWZ = 32, + LWZU = 33, + LFSU = 49, + LD = 58, + LFDU = 51, + STB = 38, + STBU = 39, + STH = 44, + STHU = 45, + STW = 36, + STWU = 37, + STFSU = 53, + STFDU = 55, + STD = 62, + ADDI = 14 +}; + +uint64_t elf::getPPC64TocBase() { + // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The + // TOC starts where the first of these sections starts. We always create a + // .got when we see a relocation that uses it, so for us the start is always + // the .got. + uint64_t tocVA = in.got->getVA(); + + // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 + // thus permitting a full 64 Kbytes segment. Note that the glibc startup + // code (crt1.o) assumes that you can get from the TOC base to the + // start of the .toc section with only a single (signed) 16-bit relocation. + return tocVA + ppc64TocOffset; +} + +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { + // The offset is encoded into the 3 most significant bits of the st_other + // field, with some special values described in section 3.4.1 of the ABI: + // 0 --> Zero offset between the GEP and LEP, and the function does NOT use + // the TOC pointer (r2). r2 will hold the same value on returning from + // the function as it did on entering the function. + // 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a + // caller-saved register for all callers. + // 2-6 --> The binary logarithm of the offset eg: + // 2 --> 2^2 = 4 bytes --> 1 instruction. + // 6 --> 2^6 = 64 bytes --> 16 instructions. + // 7 --> Reserved. + uint8_t gepToLep = (stOther >> 5) & 7; + if (gepToLep < 2) + return 0; + + // The value encoded in the st_other bits is the + // log-base-2(offset). + if (gepToLep < 7) + return 1 << gepToLep; + + error("reserved value of 7 in the 3 most-significant-bits of st_other"); + return 0; +} + +bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { + // The only small code model relocations that access the .toc section. + return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; +} + +// Find the R_PPC64_ADDR64 in .rela.toc with matching offset. +template +static std::pair +getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { + if (tocSec->numRelocations == 0) + return {}; + + // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by + // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the + // relocation index in most cases. + // + // In rare cases a TOC entry may store a constant that doesn't need an + // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8 + // points to a relocation with larger r_offset. Do a linear probe then. + // Constants are extremely uncommon in .toc and the extra number of array + // accesses can be seen as a small constant. + ArrayRef relas = tocSec->template relas(); + uint64_t index = std::min(offset / 8, relas.size() - 1); + for (;;) { + if (relas[index].r_offset == offset) { + Symbol &sym = tocSec->getFile()->getRelocTargetSym(relas[index]); + return {dyn_cast(&sym), getAddend(relas[index])}; + } + if (relas[index].r_offset < offset || index == 0) + break; + --index; + } + return {}; +} + +// When accessing a symbol defined in another translation unit, compilers +// reserve a .toc entry, allocate a local label and generate toc-indirect +// instuctions: +// +// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA +// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry +// ld/lwa 3, 0(3) # load the value from the address +// +// .section .toc,"aw",@progbits +// .LC0: .tc var[TC],var +// +// If var is defined, non-preemptable and addressable with a 32-bit signed +// offset from the toc base, the address of var can be computed by adding an +// offset to the toc base, saving a load. +// +// addis 3,2,var@toc@ha # this may be relaxed to a nop, +// addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc +// ld/lwa 3, 0(3) # load the value from the address +// +// Returns true if the relaxation is performed. +bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, + uint8_t *bufLoc) { + assert(config->tocOptimize); + if (rel.addend < 0) + return false; + + // If the symbol is not the .toc section, this isn't a toc-indirection. + Defined *defSym = dyn_cast(rel.sym); + if (!defSym || !defSym->isSection() || defSym->section->name != ".toc") + return false; + + Defined *d; + int64_t addend; + auto *tocISB = cast(defSym->section); + std::tie(d, addend) = + config->isLE ? getRelaTocSymAndAddend(tocISB, rel.addend) + : getRelaTocSymAndAddend(tocISB, rel.addend); + + // Only non-preemptable defined symbols can be relaxed. + if (!d || d->isPreemptible) + return false; + + // Two instructions can materialize a 32-bit signed offset from the toc base. + uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); + if (!isInt<32>(tocRelative)) + return false; + + // Add PPC64TocOffset that will be subtracted by relocateOne(). + target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset); + return true; +} + +namespace { +class PPC64 final : public TargetInfo { +public: + PPC64(); + int getTlsGdRelaxSkip(RelType type) const override; + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writeGotHeader(uint8_t *buf) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; +}; +} // namespace + +// Relocation masks following the #lo(value), #hi(value), #ha(value), +// #higher(value), #highera(value), #highest(value), and #highesta(value) +// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi +// document. +static uint16_t lo(uint64_t v) { return v; } +static uint16_t hi(uint64_t v) { return v >> 16; } +static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; } +static uint16_t higher(uint64_t v) { return v >> 32; } +static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; } +static uint16_t highest(uint64_t v) { return v >> 48; } +static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; } + +// Extracts the 'PO' field of an instruction encoding. +static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); } + +static bool isDQFormInstruction(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { + default: + return false; + case 56: + // The only instruction with a primary opcode of 56 is `lq`. + return true; + case 61: + // There are both DS and DQ instruction forms with this primary opcode. + // Namely `lxv` and `stxv` are the DQ-forms that use it. + // The DS 'XO' bits being set to 01 is restricted to DQ form. + return (encoding & 3) == 0x1; + } +} + +static bool isInstructionUpdateForm(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { + default: + return false; + case LBZU: + case LHAU: + case LHZU: + case LWZU: + case LFSU: + case LFDU: + case STBU: + case STHU: + case STWU: + case STFSU: + case STFDU: + return true; + // LWA has the same opcode as LD, and the DS bits is what differentiates + // between LD/LDU/LWA + case LD: + case STD: + return (encoding & 3) == 1; + } +} + +// There are a number of places when we either want to read or write an +// instruction when handling a half16 relocation type. On big-endian the buffer +// pointer is pointing into the middle of the word we want to extract, and on +// little-endian it is pointing to the start of the word. These 2 helpers are to +// simplify reading and writing in that context. +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); +} + +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); +} + +PPC64::PPC64() { + gotRel = R_PPC64_GLOB_DAT; + noneRel = R_PPC64_NONE; + pltRel = R_PPC64_JMP_SLOT; + relativeRel = R_PPC64_RELATIVE; + iRelativeRel = R_PPC64_IRELATIVE; + symbolicRel = R_PPC64_ADDR64; + pltEntrySize = 4; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + gotPltHeaderEntriesNum = 2; + pltHeaderSize = 60; + needsThunks = true; + + tlsModuleIndexRel = R_PPC64_DTPMOD64; + tlsOffsetRel = R_PPC64_DTPREL64; + + tlsGotRel = R_PPC64_TPREL64; + + needsMoreStackNonSplit = false; + + // We need 64K pages (at least under glibc/Linux, the loader won't + // set different permissions on a finer granularity than that). + defaultMaxPageSize = 65536; + + // The PPC64 ELF ABI v1 spec, says: + // + // It is normally desirable to put segments with different characteristics + // in separate 256 Mbyte portions of the address space, to give the + // operating system full paging flexibility in the 64-bit address space. + // + // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers + // use 0x10000000 as the starting address. + defaultImageBase = 0x10000000; + + write32(trapInstr.data(), 0x7fe00008); +} + +int PPC64::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation + // R_PPC64_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD) + return 2; + return 1; +} + +static uint32_t getEFlags(InputFile *file) { + if (config->ekind == ELF64BEKind) + return cast>(file)->getObj().getHeader()->e_flags; + return cast>(file)->getObj().getHeader()->e_flags; +} + +// This file implements v2 ABI. This function makes sure that all +// object files have v2 or an unspecified version as an ABI version. +uint32_t PPC64::calcEFlags() const { + for (InputFile *f : objectFiles) { + uint32_t flag = getEFlags(f); + if (flag == 1) + error(toString(f) + ": ABI version 1 is not supported"); + else if (flag > 2) + error(toString(f) + ": unrecognized e_flags: " + Twine(flag)); + } + return 2; +} + +void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC64_TOC16_HA: + // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop". + relocateOne(loc, type, val); + break; + case R_PPC64_TOC16_LO_DS: { + // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or + // "addi reg, 2, var@toc". + uint32_t insn = readFromHalf16(loc); + if (getPrimaryOpCode(insn) != LD) + error("expected a 'ld' for got-indirect to toc-relative relaxing"); + writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000); + relocateOne(loc, R_PPC64_TOC16_LO, val); + break; + } + default: + llvm_unreachable("unexpected relocation type"); + } +} + +void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. + // The general dynamic code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x + // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x + // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x + // R_PPC64_REL24 __tls_get_addr + // nop None None + + // Relaxing to local exec entails converting: + // addis r3, r2, x@got@tlsgd@ha into nop + // addi r3, r3, x@got@tlsgd@l into addis r3, r13, x@tprel@ha + // bl __tls_get_addr(x@tlsgd) into nop + // nop into addi r3, r3, x@tprel@l + + switch (type) { + case R_PPC64_GOT_TLSGD16_HA: + writeFromHalf16(loc, 0x60000000); // nop + break; + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSGD16_LO: + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); + break; + case R_PPC64_TLSGD: + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38630000); // addi r3, r3 + // Since we are relocating a half16 type relocation and Loc + 4 points to + // the start of an instruction we need to advance the buffer by an extra + // 2 bytes on BE. + relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. + // The local dynamic code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r3, r2, x@got@tlsld@ha R_PPC64_GOT_TLSLD16_HA x + // addi r3, r3, x@got@tlsld@l R_PPC64_GOT_TLSLD16_LO x + // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSLD x + // R_PPC64_REL24 __tls_get_addr + // nop None None + + // Relaxing to local exec entails converting: + // addis r3, r2, x@got@tlsld@ha into nop + // addi r3, r3, x@got@tlsld@l into addis r3, r13, 0 + // bl __tls_get_addr(x@tlsgd) into nop + // nop into addi r3, r3, 4096 + + switch (type) { + case R_PPC64_GOT_TLSLD16_HA: + writeFromHalf16(loc, 0x60000000); // nop + break; + case R_PPC64_GOT_TLSLD16_LO: + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0 + break; + case R_PPC64_TLSLD: + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38631000); // addi r3, r3, 4096 + break; + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_HA: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_DS: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_LO_DS: + relocateOne(loc, type, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +unsigned elf::getPPCDFormOp(unsigned secondaryOp) { + switch (secondaryOp) { + case LBZX: + return LBZ; + case LHZX: + return LHZ; + case LWZX: + return LWZ; + case LDX: + return LD; + case STBX: + return STB; + case STHX: + return STH; + case STWX: + return STW; + case STDX: + return STD; + case ADD: + return ADDI; + default: + return 0; + } +} + +void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + // The initial exec code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x + // ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x + // add r9, r9, x@tls R_PPC64_TLS x + + // Relaxing to local exec entails converting: + // addis r9, r2, x@got@tprel@ha into nop + // ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha + // add r9, r9, x@tls into addi r9, r9, x@tprel@l + + // x@tls R_PPC64_TLS is a relocation which does not compute anything, + // it is replaced with r13 (thread pointer). + + // The add instruction in the initial exec sequence has multiple variations + // that need to be handled. If we are building an address it will use an add + // instruction, if we are accessing memory it will use any of the X-form + // indexed load or store instructions. + + unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; + switch (type) { + case R_PPC64_GOT_TPREL16_HA: + write32(loc - offset, 0x60000000); // nop + break; + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_TPREL16_DS: { + uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10 + write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); + break; + } + case R_PPC64_TLS: { + uint32_t primaryOp = getPrimaryOpCode(read32(loc)); + if (primaryOp != 31) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); + relocateOne(loc + offset, R_PPC64_TPREL16_LO, val); + break; + } + default: + llvm_unreachable("unknown relocation for IE to LE"); + break; + } +} + +RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_PPC64_NONE: + return R_NONE; + case R_PPC64_ADDR16: + case R_PPC64_ADDR16_DS: + case R_PPC64_ADDR16_HA: + case R_PPC64_ADDR16_HI: + case R_PPC64_ADDR16_HIGHER: + case R_PPC64_ADDR16_HIGHERA: + case R_PPC64_ADDR16_HIGHEST: + case R_PPC64_ADDR16_HIGHESTA: + case R_PPC64_ADDR16_LO: + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR32: + case R_PPC64_ADDR64: + return R_ABS; + case R_PPC64_GOT16: + case R_PPC64_GOT16_DS: + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_HI: + case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_LO_DS: + return R_GOT_OFF; + case R_PPC64_TOC16: + case R_PPC64_TOC16_DS: + case R_PPC64_TOC16_HI: + case R_PPC64_TOC16_LO: + return R_GOTREL; + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL; + case R_PPC64_TOC: + return R_PPC64_TOCBASE; + case R_PPC64_REL14: + case R_PPC64_REL24: + return R_PPC64_CALL_PLT; + case R_PPC64_REL16_LO: + case R_PPC64_REL16_HA: + case R_PPC64_REL16_HI: + case R_PPC64_REL32: + case R_PPC64_REL64: + return R_PC; + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSGD16_HI: + case R_PPC64_GOT_TLSGD16_LO: + return R_TLSGD_GOT; + case R_PPC64_GOT_TLSLD16: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSLD16_HI: + case R_PPC64_GOT_TLSLD16_LO: + return R_TLSLD_GOT; + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_TPREL16_DS: + case R_PPC64_GOT_TPREL16_HI: + return R_GOT_OFF; + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_DS: + case R_PPC64_GOT_DTPREL16_HI: + return R_TLSLD_GOT_OFF; + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_HA: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_DS: + case R_PPC64_TPREL16_LO_DS: + case R_PPC64_TPREL16_HIGHER: + case R_PPC64_TPREL16_HIGHERA: + case R_PPC64_TPREL16_HIGHEST: + case R_PPC64_TPREL16_HIGHESTA: + return R_TLS; + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_DS: + case R_PPC64_DTPREL16_HA: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_HIGHER: + case R_PPC64_DTPREL16_HIGHERA: + case R_PPC64_DTPREL16_HIGHEST: + case R_PPC64_DTPREL16_HIGHESTA: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_LO_DS: + case R_PPC64_DTPREL64: + return R_DTPREL; + case R_PPC64_TLSGD: + return R_TLSDESC_CALL; + case R_PPC64_TLSLD: + return R_TLSLD_HINT; + case R_PPC64_TLS: + return R_TLSIE_HINT; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +RelType PPC64::getDynRel(RelType type) const { + if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC) + return R_PPC64_ADDR64; + return R_PPC64_NONE; +} + +void PPC64::writeGotHeader(uint8_t *buf) const { + write64(buf, getPPC64TocBase()); +} + +void PPC64::writePltHeader(uint8_t *buf) const { + // The generic resolver stub goes first. + write32(buf + 0, 0x7c0802a6); // mflr r0 + write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> + write32(buf + 8, 0x7d6802a6); // mflr r11 + write32(buf + 12, 0x7c0803a6); // mtlr r0 + write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12 + write32(buf + 20, 0x380cffcc); // subi r0,r12,52 + write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2 + write32(buf + 28, 0xe98b002c); // ld r12,44(r11) + write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11 + write32(buf + 36, 0xe98b0000); // ld r12,0(r11) + write32(buf + 40, 0xe96b0008); // ld r11,8(r11) + write32(buf + 44, 0x7d8903a6); // mtctr r12 + write32(buf + 48, 0x4e800420); // bctr + + // The 'bcl' instruction will set the link register to the address of the + // following instruction ('mflr r11'). Here we store the offset from that + // instruction to the first entry in the GotPlt section. + int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8); + write64(buf + 52, gotPltOffset); +} + +void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + int32_t offset = pltHeaderSize + index * pltEntrySize; + // bl __glink_PLTresolve + write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); +} + +static std::pair toAddr16Rel(RelType type, uint64_t val) { + // Relocations relative to the toc-base need to be adjusted by the Toc offset. + uint64_t tocBiasedVal = val - ppc64TocOffset; + // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset. + uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset; + + switch (type) { + // TOC biased relocation. + case R_PPC64_GOT16: + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSLD16: + case R_PPC64_TOC16: + return {R_PPC64_ADDR16, tocBiasedVal}; + case R_PPC64_GOT16_DS: + case R_PPC64_TOC16_DS: + case R_PPC64_GOT_TPREL16_DS: + case R_PPC64_GOT_DTPREL16_DS: + return {R_PPC64_ADDR16_DS, tocBiasedVal}; + case R_PPC64_GOT16_HA: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_TOC16_HA: + return {R_PPC64_ADDR16_HA, tocBiasedVal}; + case R_PPC64_GOT16_HI: + case R_PPC64_GOT_TLSGD16_HI: + case R_PPC64_GOT_TLSLD16_HI: + case R_PPC64_GOT_TPREL16_HI: + case R_PPC64_GOT_DTPREL16_HI: + case R_PPC64_TOC16_HI: + return {R_PPC64_ADDR16_HI, tocBiasedVal}; + case R_PPC64_GOT16_LO: + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TLSLD16_LO: + case R_PPC64_TOC16_LO: + return {R_PPC64_ADDR16_LO, tocBiasedVal}; + case R_PPC64_GOT16_LO_DS: + case R_PPC64_TOC16_LO_DS: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_LO_DS: + return {R_PPC64_ADDR16_LO_DS, tocBiasedVal}; + + // Dynamic Thread pointer biased relocation types. + case R_PPC64_DTPREL16: + return {R_PPC64_ADDR16, dtpBiasedVal}; + case R_PPC64_DTPREL16_DS: + return {R_PPC64_ADDR16_DS, dtpBiasedVal}; + case R_PPC64_DTPREL16_HA: + return {R_PPC64_ADDR16_HA, dtpBiasedVal}; + case R_PPC64_DTPREL16_HI: + return {R_PPC64_ADDR16_HI, dtpBiasedVal}; + case R_PPC64_DTPREL16_HIGHER: + return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal}; + case R_PPC64_DTPREL16_HIGHERA: + return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal}; + case R_PPC64_DTPREL16_HIGHEST: + return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal}; + case R_PPC64_DTPREL16_HIGHESTA: + return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal}; + case R_PPC64_DTPREL16_LO: + return {R_PPC64_ADDR16_LO, dtpBiasedVal}; + case R_PPC64_DTPREL16_LO_DS: + return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal}; + case R_PPC64_DTPREL64: + return {R_PPC64_ADDR64, dtpBiasedVal}; + + default: + return {type, val}; + } +} + +static bool isTocOptType(RelType type) { + switch (type) { + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_LO_DS: + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + case R_PPC64_TOC16_LO: + return true; + default: + return false; + } +} + +void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + // We need to save the original relocation type to use in diagnostics, and + // use the original type to determine if we should toc-optimize the + // instructions being relocated. + RelType originalType = type; + bool shouldTocOptimize = isTocOptType(type); + // For dynamic thread pointer relative, toc-relative, and got-indirect + // relocations, proceed in terms of the corresponding ADDR16 relocation type. + std::tie(type, val) = toAddr16Rel(type, val); + + switch (type) { + case R_PPC64_ADDR14: { + checkAlignment(loc, val, 4, type); + // Preserve the AA/LK bits in the branch instruction + uint8_t aalk = loc[3]; + write16(loc + 2, (aalk & 3) | (val & 0xfffc)); + break; + } + case R_PPC64_ADDR16: + checkIntUInt(loc, val, 16, originalType); + write16(loc, val); + break; + case R_PPC64_ADDR32: + checkIntUInt(loc, val, 32, originalType); + write32(loc, val); + break; + case R_PPC64_ADDR16_DS: + case R_PPC64_TPREL16_DS: { + checkInt(loc, val, 16, originalType); + // DQ-form instructions use bits 28-31 as part of the instruction encoding + // DS-form instructions only use bits 30-31. + uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + write16(loc, (read16(loc) & mask) | lo(val)); + } break; + case R_PPC64_ADDR16_HA: + case R_PPC64_REL16_HA: + case R_PPC64_TPREL16_HA: + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) + writeFromHalf16(loc, 0x60000000); + else + write16(loc, ha(val)); + break; + case R_PPC64_ADDR16_HI: + case R_PPC64_REL16_HI: + case R_PPC64_TPREL16_HI: + write16(loc, hi(val)); + break; + case R_PPC64_ADDR16_HIGHER: + case R_PPC64_TPREL16_HIGHER: + write16(loc, higher(val)); + break; + case R_PPC64_ADDR16_HIGHERA: + case R_PPC64_TPREL16_HIGHERA: + write16(loc, highera(val)); + break; + case R_PPC64_ADDR16_HIGHEST: + case R_PPC64_TPREL16_HIGHEST: + write16(loc, highest(val)); + break; + case R_PPC64_ADDR16_HIGHESTA: + case R_PPC64_TPREL16_HIGHESTA: + write16(loc, highesta(val)); + break; + case R_PPC64_ADDR16_LO: + case R_PPC64_REL16_LO: + case R_PPC64_TPREL16_LO: + // When the high-adjusted part of a toc relocation evalutes to 0, it is + // changed into a nop. The lo part then needs to be updated to use the + // toc-pointer register r2, as the base register. + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { + uint32_t insn = readFromHalf16(loc); + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + + "can't toc-optimize an update instruction: 0x" + + utohexstr(insn)); + writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val)); + } else { + write16(loc, lo(val)); + } + break; + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_TPREL16_LO_DS: { + // DQ-form instructions use bits 28-31 as part of the instruction encoding + // DS-form instructions only use bits 30-31. + uint32_t insn = readFromHalf16(loc); + uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { + // When the high-adjusted part of a toc relocation evalutes to 0, it is + // changed into a nop. The lo part then needs to be updated to use the toc + // pointer register r2, as the base register. + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + + "Can't toc-optimize an update instruction: 0x" + + Twine::utohexstr(insn)); + insn &= 0xffe00000 | mask; + writeFromHalf16(loc, insn | 0x00020000 | lo(val)); + } else { + write16(loc, (read16(loc) & mask) | lo(val)); + } + } break; + case R_PPC64_TPREL16: + checkInt(loc, val, 16, originalType); + write16(loc, val); + break; + case R_PPC64_REL32: + checkInt(loc, val, 32, type); + write32(loc, val); + break; + case R_PPC64_ADDR64: + case R_PPC64_REL64: + case R_PPC64_TOC: + write64(loc, val); + break; + case R_PPC64_REL14: { + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); + break; + } + case R_PPC64_REL24: { + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); + break; + } + case R_PPC64_DTPREL64: + write64(loc, val - dynamicThreadPointerOffset); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC64_REL14 && type != R_PPC64_REL24) + return false; + + // If a function is in the Plt it needs to be called with a call-stub. + if (s.isInPlt()) + return true; + + // If a symbol is a weak undefined and we are compiling an executable + // it doesn't need a range-extending thunk since it can't be called. + if (s.isUndefWeak() && !config->shared) + return false; + + // If the offset exceeds the range of the branch type then it will need + // a range-extending thunk. + // See the comment in getRelocTargetVA() about R_PPC64_CALL. + return !inBranchRange(type, branchAddr, + s.getVA() + + getPPC64GlobalEntryToLocalEntryOffset(s.stOther)); +} + +uint32_t PPC64::getThunkSectionSpacing() const { + // See comment in Arch/ARM.cpp for a more detailed explanation of + // getThunkSectionSpacing(). For PPC64 we pick the constant here based on + // R_PPC64_REL24, which is used by unconditional branch instructions. + // 0x2000000 = (1 << 24-1) * 4 + return 0x2000000; +} + +bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + int64_t offset = dst - src; + if (type == R_PPC64_REL14) + return isInt<16>(offset); + if (type == R_PPC64_REL24) + return isInt<26>(offset); + llvm_unreachable("unsupported relocation type used in branch"); +} + +RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return expr; +} + +// Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. +// The general dynamic code sequence for a global `x` uses 4 instructions. +// Instruction Relocation Symbol +// addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x +// addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x +// bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x +// R_PPC64_REL24 __tls_get_addr +// nop None None +// +// Relaxing to initial-exec entails: +// 1) Convert the addis/addi pair that builds the address of the tls_index +// struct for 'x' to an addis/ld pair that loads an offset from a got-entry. +// 2) Convert the call to __tls_get_addr to a nop. +// 3) Convert the nop following the call to an add of the loaded offset to the +// thread pointer. +// Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is +// used as the relaxation hint for both steps 2 and 3. +void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC64_GOT_TLSGD16_HA: + // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to + // addis rT, r2, sym@got@tprel@ha. + relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val); + return; + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSGD16_LO: { + // Relax from addi r3, rA, sym@got@tlsgd@l to + // ld r3, sym@got@tprel@l(rA) + uint32_t ra = (readFromHalf16(loc) & (0x1f << 16)); + writeFromHalf16(loc, 0xe8600000 | ra); + relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val); + return; + } + case R_PPC64_TLSGD: + write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + return; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +// The prologue for a split-stack function is expected to look roughly +// like this: +// .Lglobal_entry_point: +// # TOC pointer initalization. +// ... +// .Llocal_entry_point: +// # load the __private_ss member of the threads tcbhead. +// ld r0,-0x7000-64(r13) +// # subtract the functions stack size from the stack pointer. +// addis r12, r1, ha(-stack-frame size) +// addi r12, r12, l(-stack-frame size) +// # compare needed to actual and branch to allocate_more_stack if more +// # space is needed, otherwise fallthrough to 'normal' function body. +// cmpld cr7,r12,r0 +// blt- cr7, .Lallocate_more_stack +// +// -) The allocate_more_stack block might be placed after the split-stack +// prologue and the `blt-` replaced with a `bge+ .Lnormal_func_body` +// instead. +// -) If either the addis or addi is not needed due to the stack size being +// smaller then 32K or a multiple of 64K they will be replaced with a nop, +// but there will always be 2 instructions the linker can overwrite for the +// adjusted stack size. +// +// The linkers job here is to increase the stack size used in the addis/addi +// pair by split-stack-size-adjust. +// addis r12, r1, ha(-stack-frame size - split-stack-adjust-size) +// addi r12, r12, l(-stack-frame size - split-stack-adjust-size) +bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { + // If the caller has a global entry point adjust the buffer past it. The start + // of the split-stack prologue will be at the local entry point. + loc += getPPC64GlobalEntryToLocalEntryOffset(stOther); + + // At the very least we expect to see a load of some split-stack data from the + // tcb, and 2 instructions that calculate the ending stack address this + // function will require. If there is not enough room for at least 3 + // instructions it can't be a split-stack prologue. + if (loc + 12 >= end) + return false; + + // First instruction must be `ld r0, -0x7000-64(r13)` + if (read32(loc) != 0xe80d8fc0) + return false; + + int16_t hiImm = 0; + int16_t loImm = 0; + // First instruction can be either an addis if the frame size is larger then + // 32K, or an addi if the size is less then 32K. + int32_t firstInstr = read32(loc + 4); + if (getPrimaryOpCode(firstInstr) == 15) { + hiImm = firstInstr & 0xFFFF; + } else if (getPrimaryOpCode(firstInstr) == 14) { + loImm = firstInstr & 0xFFFF; + } else { + return false; + } + + // Second instruction is either an addi or a nop. If the first instruction was + // an addi then LoImm is set and the second instruction must be a nop. + uint32_t secondInstr = read32(loc + 8); + if (!loImm && getPrimaryOpCode(secondInstr) == 14) { + loImm = secondInstr & 0xFFFF; + } else if (secondInstr != 0x60000000) { + return false; + } + + // The register operands of the first instruction should be the stack-pointer + // (r1) as the input (RA) and r12 as the output (RT). If the second + // instruction is not a nop, then it should use r12 as both input and output. + auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT, + uint8_t expectedRA) { + return ((instr & 0x3E00000) >> 21 == expectedRT) && + ((instr & 0x1F0000) >> 16 == expectedRA); + }; + if (!checkRegOperands(firstInstr, 12, 1)) + return false; + if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12)) + return false; + + int32_t stackFrameSize = (hiImm * 65536) + loImm; + // Check that the adjusted size doesn't overflow what we can represent with 2 + // instructions. + if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) { + error(getErrorLocation(loc) + "split-stack prologue adjustment overflows"); + return false; + } + + int32_t adjustedStackFrameSize = + stackFrameSize - config->splitStackAdjustSize; + + loImm = adjustedStackFrameSize & 0xFFFF; + hiImm = (adjustedStackFrameSize + 0x8000) >> 16; + if (hiImm) { + write32(loc + 4, 0x3D810000 | (uint16_t)hiImm); + // If the low immediate is zero the second instruction will be a nop. + secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000; + write32(loc + 8, secondInstr); + } else { + // addi r12, r1, imm + write32(loc + 4, (0x39810000) | (uint16_t)loImm); + write32(loc + 8, 0x60000000); + } + + return true; +} + +TargetInfo *elf::getPPC64TargetInfo() { + static PPC64 target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/PPC64.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/AArch64.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/AArch64.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/AArch64.cpp (revision 352529) @@ -0,0 +1,590 @@ +//===- AArch64.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "Thunks.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +// Page(Expr) is the page address of the expression Expr, defined +// as (Expr & ~0xFFF). (This applies even if the machine page size +// supported by the platform has a different value.) +uint64_t elf::getAArch64Page(uint64_t expr) { + return expr & ~static_cast(0xFFF); +} + +namespace { +class AArch64 : public TargetInfo { +public: + AArch64(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + bool usesOnlyLowPageBits(RelType type) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +AArch64::AArch64() { + copyRel = R_AARCH64_COPY; + relativeRel = R_AARCH64_RELATIVE; + iRelativeRel = R_AARCH64_IRELATIVE; + gotRel = R_AARCH64_GLOB_DAT; + noneRel = R_AARCH64_NONE; + pltRel = R_AARCH64_JUMP_SLOT; + symbolicRel = R_AARCH64_ABS64; + tlsDescRel = R_AARCH64_TLSDESC; + tlsGotRel = R_AARCH64_TLS_TPREL64; + pltEntrySize = 16; + pltHeaderSize = 32; + defaultMaxPageSize = 65536; + + // Align to the 2 MiB page size (known as a superpage or huge page). + // FreeBSD automatically promotes 2 MiB-aligned allocations. + defaultImageBase = 0x200000; + + needsThunks = true; +} + +RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_AARCH64_TLSDESC_ADR_PAGE21: + return R_AARCH64_TLSDESC_PAGE; + case R_AARCH64_TLSDESC_LD64_LO12: + case R_AARCH64_TLSDESC_ADD_LO12: + return R_TLSDESC; + case R_AARCH64_TLSDESC_CALL: + return R_TLSDESC_CALL; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: + return R_TLS; + case R_AARCH64_CALL26: + case R_AARCH64_CONDBR19: + case R_AARCH64_JUMP26: + case R_AARCH64_TSTBR14: + return R_PLT_PC; + case R_AARCH64_PREL16: + case R_AARCH64_PREL32: + case R_AARCH64_PREL64: + case R_AARCH64_ADR_PREL_LO21: + case R_AARCH64_LD_PREL_LO19: + return R_PC; + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADR_PREL_PG_HI21_NC: + return R_AARCH64_PAGE_PC; + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + return R_GOT; + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + return R_AARCH64_GOT_PAGE_PC; + case R_AARCH64_NONE: + return R_NONE; + default: + return R_ABS; + } +} + +RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) { + if (type == R_AARCH64_TLSDESC_ADR_PAGE21) + return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; + return R_RELAX_TLS_GD_TO_IE_ABS; + } + return expr; +} + +bool AArch64::usesOnlyLowPageBits(RelType type) const { + switch (type) { + default: + return false; + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12: + case R_AARCH64_TLSDESC_LD64_LO12: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + return true; + } +} + +RelType AArch64::getDynRel(RelType type) const { + if (type == R_AARCH64_ABS64) + return type; + return R_AARCH64_NONE; +} + +void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { + write64le(buf, in.plt->getVA()); +} + +void AArch64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + memcpy(buf, pltData, sizeof(pltData)); + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 4)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); +} + +void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + memcpy(buf, inst, sizeof(inst)); + + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); +} + +bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + // ELF for the ARM 64-bit architecture, section Call and Jump relocations + // only permits range extension thunks for R_AARCH64_CALL26 and + // R_AARCH64_JUMP26 relocation types. + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) + return false; + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); +} + +uint32_t AArch64::getThunkSectionSpacing() const { + // See comment in Arch/ARM.cpp for a more detailed explanation of + // getThunkSectionSpacing(). For AArch64 the only branches we are permitted to + // Thunk have a range of +/- 128 MiB + return (128 * 1024 * 1024) - 0x30000; +} + +bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) + return true; + // The AArch64 call and unconditional branch instructions have a range of + // +/- 128 MiB. + uint64_t range = 128 * 1024 * 1024; + if (dst > src) { + // Immediate of branch is signed. + range -= 4; + return dst - src <= range; + } + return src - dst <= range; +} + +static void write32AArch64Addr(uint8_t *l, uint64_t imm) { + uint32_t immLo = (imm & 0x3) << 29; + uint32_t immHi = (imm & 0x1FFFFC) << 3; + uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); + write32le(l, (read32le(l) & ~mask) | immLo | immHi); +} + +// Return the bits [Start, End] from Val shifted Start bits. +// For instance, getBits(0xF0, 4, 8) returns 0xF. +static uint64_t getBits(uint64_t val, int start, int end) { + uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1; + return (val >> start) & mask; +} + +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } + +// Update the immediate field in a AARCH64 ldr, str, and add instruction. +static void or32AArch64Imm(uint8_t *l, uint64_t imm) { + or32le(l, (imm & 0xFFF) << 10); +} + +void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_AARCH64_ABS16: + case R_AARCH64_PREL16: + checkIntUInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_AARCH64_ABS32: + case R_AARCH64_PREL32: + checkIntUInt(loc, val, 32, type); + write32le(loc, val); + break; + case R_AARCH64_ABS64: + case R_AARCH64_PREL64: + write64le(loc, val); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + or32AArch64Imm(loc, val); + break; + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + case R_AARCH64_TLSDESC_ADR_PAGE21: + checkInt(loc, val, 33, type); + LLVM_FALLTHROUGH; + case R_AARCH64_ADR_PREL_PG_HI21_NC: + write32AArch64Addr(loc, val >> 12); + break; + case R_AARCH64_ADR_PREL_LO21: + checkInt(loc, val, 21, type); + write32AArch64Addr(loc, val); + break; + case R_AARCH64_JUMP26: + // Normally we would just write the bits of the immediate field, however + // when patching instructions for the cpu errata fix -fix-cortex-a53-843419 + // we want to replace a non-branch instruction with a branch immediate + // instruction. By writing all the bits of the instruction including the + // opcode and the immediate (0 001 | 01 imm26) we can do this + // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of + // the instruction we want to patch. + write32le(loc, 0x14000000); + LLVM_FALLTHROUGH; + case R_AARCH64_CALL26: + checkInt(loc, val, 28, type); + or32le(loc, (val & 0x0FFFFFFC) >> 2); + break; + case R_AARCH64_CONDBR19: + case R_AARCH64_LD_PREL_LO19: + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + or32le(loc, (val & 0x1FFFFC) << 3); + break; + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: + or32AArch64Imm(loc, getBits(val, 0, 11)); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: + checkAlignment(loc, val, 2, type); + or32AArch64Imm(loc, getBits(val, 1, 11)); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: + checkAlignment(loc, val, 4, type); + or32AArch64Imm(loc, getBits(val, 2, 11)); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + 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: + checkAlignment(loc, val, 8, type); + or32AArch64Imm(loc, getBits(val, 3, 11)); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: + checkAlignment(loc, val, 16, type); + or32AArch64Imm(loc, getBits(val, 4, 11)); + break; + case R_AARCH64_MOVW_UABS_G0_NC: + or32le(loc, (val & 0xFFFF) << 5); + break; + case R_AARCH64_MOVW_UABS_G1_NC: + or32le(loc, (val & 0xFFFF0000) >> 11); + break; + case R_AARCH64_MOVW_UABS_G2_NC: + or32le(loc, (val & 0xFFFF00000000) >> 27); + break; + case R_AARCH64_MOVW_UABS_G3: + or32le(loc, (val & 0xFFFF000000000000) >> 43); + break; + case R_AARCH64_TSTBR14: + checkInt(loc, val, 16, type); + or32le(loc, (val & 0xFFFC) << 3); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + checkUInt(loc, val, 24, type); + or32AArch64Imm(loc, val >> 12); + break; + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12: + or32AArch64Imm(loc, val); + break; + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + } +} + +void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + // TLSDESC Global-Dynamic relocation are in the form: + // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] + // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] + // add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12] + // .tlsdesccall [R_AARCH64_TLSDESC_CALL] + // blr x1 + // And it can optimized to: + // movz x0, #0x0, lsl #16 + // movk x0, #0x10 + // nop + // nop + checkUInt(loc, val, 32, type); + + switch (type) { + case R_AARCH64_TLSDESC_ADD_LO12: + case R_AARCH64_TLSDESC_CALL: + write32le(loc, 0xd503201f); // nop + return; + case R_AARCH64_TLSDESC_ADR_PAGE21: + write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz + return; + case R_AARCH64_TLSDESC_LD64_LO12: + write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk + return; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + // TLSDESC Global-Dynamic relocation are in the form: + // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] + // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] + // add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12] + // .tlsdesccall [R_AARCH64_TLSDESC_CALL] + // blr x1 + // And it can optimized to: + // adrp x0, :gottprel:v + // ldr x0, [x0, :gottprel_lo12:v] + // nop + // nop + + switch (type) { + case R_AARCH64_TLSDESC_ADD_LO12: + case R_AARCH64_TLSDESC_CALL: + write32le(loc, 0xd503201f); // nop + break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + write32le(loc, 0x90000000); // adrp + relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); + break; + case R_AARCH64_TLSDESC_LD64_LO12: + write32le(loc, 0xf9400000); // ldr + relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + checkUInt(loc, val, 32, type); + + if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { + // Generate MOVZ. + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5)); + return; + } + if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { + // Generate MOVK. + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5)); + return; + } + llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); +} + +// AArch64 may use security features in variant PLT sequences. These are: +// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target +// Indicator (BTI) introduced in armv8.5-a. The additional instructions used +// in the variant Plt sequences are encoded in the Hint space so they can be +// deployed on older architectures, which treat the instructions as a nop. +// PAC and BTI can be combined leading to the following combinations: +// writePltHeader +// writePltHeaderBti (no PAC Header needed) +// writePlt +// writePltBti (BTI only) +// writePltPac (PAC only) +// writePltBtiPac (BTI and PAC) +// +// When PAC is enabled the dynamic loader encrypts the address that it places +// in the .got.plt using the pacia1716 instruction which encrypts the value in +// x17 using the modifier in x16. The static linker places autia1716 before the +// indirect branch to x17 to authenticate the address in x17 with the modifier +// in x16. This makes it more difficult for an attacker to modify the value in +// the .got.plt. +// +// When BTI is enabled all indirect branches must land on a bti instruction. +// The static linker must place a bti instruction at the start of any PLT entry +// that may be the target of an indirect branch. As the PLT entries call the +// lazy resolver indirectly this must have a bti instruction at start. In +// general a bti instruction is not needed for a PLT entry as indirect calls +// are resolved to the function address and not the PLT entry for the function. +// There are a small number of cases where the PLT address can escape, such as +// taking the address of a function or ifunc via a non got-generating +// relocation, and a shared library refers to that symbol. +// +// We use the bti c variant of the instruction which permits indirect branches +// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI +// guarantees that all indirect branches from code requiring BTI protection +// will go via x16/x17 + +namespace { +class AArch64BtiPac final : public AArch64 { +public: + AArch64BtiPac(); + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + +private: + bool btiHeader; // bti instruction needed in PLT Header + bool btiEntry; // bti instruction needed in PLT Entry + bool pacEntry; // autia1716 instruction needed in PLT Entry +}; +} // namespace + +AArch64BtiPac::AArch64BtiPac() { + btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // A BTI (Branch Target Indicator) Plt Entry is only required if the + // address of the PLT entry can be taken by the program, which permits an + // indirect jump to the PLT entry. This can happen when the address + // of the PLT entry for a function is canonicalised due to the address of + // the function in an executable being taken by a shared library. + // FIXME: There is a potential optimization to omit the BTI if we detect + // that the address of the PLT entry isn't taken. + btiEntry = btiHeader && !config->shared; + pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + + if (btiEntry || pacEntry) + pltEntrySize = 24; +} + +void AArch64BtiPac::writePltHeader(uint8_t *buf) const { + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t pltData[] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + + if (btiHeader) { + // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C + // instruction. + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + plt += sizeof(btiData); + } + memcpy(buf, pltData, sizeof(pltData)); + + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 8)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); + if (!btiHeader) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(pltData), nopData, sizeof(nopData)); +} + +void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // The PLT entry is of the form: + // [btiData] addrInst (pacBr | stdBr) [nopData] + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t addrInst[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n])) + }; + const uint8_t pacBr[] = { + 0x9f, 0x21, 0x03, 0xd5, // autia1716 + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + const uint8_t stdBr[] = { + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + if (btiEntry) { + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + pltEntryAddr += sizeof(btiData); + } + + memcpy(buf, addrInst, sizeof(addrInst)); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - + getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); + + if (pacEntry) + memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); + else + memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr)); + if (!btiEntry) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); +} + +static TargetInfo *getTargetInfo() { + if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | + GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + static AArch64BtiPac t; + return &t; + } + static AArch64 t; + return &t; +} + +TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/AArch64.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/AMDGPU.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/AMDGPU.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/AMDGPU.cpp (revision 352529) @@ -0,0 +1,113 @@ +//===- AMDGPU.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.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 AMDGPU final : public TargetInfo { +public: + AMDGPU(); + uint32_t calcEFlags() const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; +}; +} // namespace + +AMDGPU::AMDGPU() { + relativeRel = R_AMDGPU_RELATIVE64; + gotRel = R_AMDGPU_ABS64; + noneRel = R_AMDGPU_NONE; + symbolicRel = R_AMDGPU_ABS64; +} + +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader()->e_flags; +} + +uint32_t AMDGPU::calcEFlags() const { + assert(!objectFiles.empty()); + uint32_t ret = getEFlags(objectFiles[0]); + + // Verify that all input files have the same e_flags. + for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + if (ret == getEFlags(f)) + continue; + error("incompatible e_flags: " + toString(f)); + return 0; + } + return ret; +} + +void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_AMDGPU_ABS32: + case R_AMDGPU_GOTPCREL: + case R_AMDGPU_GOTPCREL32_LO: + case R_AMDGPU_REL32: + case R_AMDGPU_REL32_LO: + write32le(loc, val); + break; + case R_AMDGPU_ABS64: + case R_AMDGPU_REL64: + write64le(loc, val); + break; + case R_AMDGPU_GOTPCREL32_HI: + case R_AMDGPU_REL32_HI: + write32le(loc, val >> 32); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_AMDGPU_ABS32: + case R_AMDGPU_ABS64: + return R_ABS; + case R_AMDGPU_REL32: + case R_AMDGPU_REL32_LO: + case R_AMDGPU_REL32_HI: + case R_AMDGPU_REL64: + return R_PC; + case R_AMDGPU_GOTPCREL: + case R_AMDGPU_GOTPCREL32_LO: + case R_AMDGPU_GOTPCREL32_HI: + return R_GOT_PC; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +RelType AMDGPU::getDynRel(RelType type) const { + if (type == R_AMDGPU_ABS64) + return type; + return R_AMDGPU_NONE; +} + +TargetInfo *elf::getAMDGPUTargetInfo() { + static AMDGPU target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/AMDGPU.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/ARM.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/ARM.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/ARM.cpp (revision 352529) @@ -0,0 +1,606 @@ +//===- ARM.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "Thunks.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class ARM final : public TargetInfo { +public: + ARM(); + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) 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, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void addPltSymbols(InputSection &isec, uint64_t off) const override; + void addPltHeaderSymbols(InputSection &isd) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +ARM::ARM() { + copyRel = R_ARM_COPY; + relativeRel = R_ARM_RELATIVE; + iRelativeRel = R_ARM_IRELATIVE; + gotRel = R_ARM_GLOB_DAT; + noneRel = R_ARM_NONE; + pltRel = R_ARM_JUMP_SLOT; + symbolicRel = R_ARM_ABS32; + tlsGotRel = R_ARM_TLS_TPOFF32; + tlsModuleIndexRel = R_ARM_TLS_DTPMOD32; + tlsOffsetRel = R_ARM_TLS_DTPOFF32; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; + needsThunks = true; +} + +uint32_t ARM::calcEFlags() const { + // The ABIFloatType is used by loaders to detect the floating point calling + // convention. + uint32_t abiFloatType = 0; + if (config->armVFPArgs == ARMVFPArgKind::Base || + config->armVFPArgs == ARMVFPArgKind::Default) + abiFloatType = EF_ARM_ABI_FLOAT_SOFT; + else if (config->armVFPArgs == ARMVFPArgKind::VFP) + abiFloatType = EF_ARM_ABI_FLOAT_HARD; + + // We don't currently use any features incompatible with EF_ARM_EABI_VER5, + // but we don't have any firm guarantees of conformance. Linux AArch64 + // kernels (as of 2016) require an EABI version to be set. + return EF_ARM_EABI_VER5 | abiFloatType; +} + +RelExpr ARM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_ARM_THM_JUMP11: + return R_PC; + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PC24: + case R_ARM_PLT32: + case R_ARM_PREL31: + case R_ARM_THM_JUMP19: + case R_ARM_THM_JUMP24: + case R_ARM_THM_CALL: + return R_PLT_PC; + case R_ARM_GOTOFF32: + // (S + A) - GOT_ORG + return R_GOTREL; + case R_ARM_GOT_BREL: + // GOT(S) + A - GOT_ORG + return R_GOT_OFF; + case R_ARM_GOT_PREL: + case R_ARM_TLS_IE32: + // GOT(S) + A - P + return R_GOT_PC; + case R_ARM_SBREL32: + return R_ARM_SBREL; + case R_ARM_TARGET1: + return config->target1Rel ? R_PC : R_ABS; + case R_ARM_TARGET2: + if (config->target2 == Target2Policy::Rel) + return R_PC; + if (config->target2 == Target2Policy::Abs) + return R_ABS; + return R_GOT_PC; + case R_ARM_TLS_GD32: + return R_TLSGD_PC; + case R_ARM_TLS_LDM32: + return R_TLSLD_PC; + case R_ARM_BASE_PREL: + // B(S) + A - P + // FIXME: currently B(S) assumed to be .got, this may not hold for all + // platforms. + return R_GOTONLY_PC; + case R_ARM_MOVW_PREL_NC: + case R_ARM_MOVT_PREL: + case R_ARM_REL32: + case R_ARM_THM_MOVW_PREL_NC: + case R_ARM_THM_MOVT_PREL: + return R_PC; + case R_ARM_NONE: + return R_NONE; + case R_ARM_TLS_LE32: + return R_TLS; + case R_ARM_V4BX: + // V4BX is just a marker to indicate there's a "bx rN" instruction at the + // given address. It can be used to implement a special linker mode which + // rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and + // not ARMv4 output, we can just ignore it. + return R_HINT; + default: + return R_ABS; + } +} + +RelType ARM::getDynRel(RelType type) const { + if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel)) + return R_ARM_ABS32; + return R_ARM_NONE; +} + +void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const { + write32le(buf, in.plt->getVA()); +} + +void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + // An ARM entry is the address of the ifunc resolver function. + write32le(buf, s.getVA()); +} + +// Long form PLT Header that does not have any restrictions on the displacement +// of the .plt from the .plt.got. +static void writePltHeaderLong(uint8_t *buf) { + const uint8_t pltData[] = { + 0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]! + 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2 + 0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr + 0x08, 0xf0, 0xbe, 0xe5, // ldr pc, [lr, #8] + 0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8 + 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary + 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary + 0xd4, 0xd4, 0xd4, 0xd4}; + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t l1 = in.plt->getVA() + 8; + write32le(buf + 16, gotPlt - l1 - 8); +} + +// The default PLT header requires the .plt.got to be within 128 Mb of the +// .plt in the positive direction. +void ARM::writePltHeader(uint8_t *buf) const { + // Use a similar sequence to that in writePlt(), the difference is the calling + // conventions mean we use lr instead of ip. The PLT entry is responsible for + // saving lr on the stack, the dynamic loader is responsible for reloading + // it. + const uint32_t pltData[] = { + 0xe52de004, // L1: str lr, [sp,#-4]! + 0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4) + 0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4) + 0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4) + }; + + uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4; + if (!llvm::isUInt<27>(offset)) { + // We cannot encode the Offset, use the long form. + writePltHeaderLong(buf); + return; + } + write32le(buf + 0, pltData[0]); + write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff)); + write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff)); + write32le(buf + 12, pltData[3] | (offset & 0xfff)); + memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary + memcpy(buf + 20, trapInstr.data(), 4); + memcpy(buf + 24, trapInstr.data(), 4); + memcpy(buf + 28, trapInstr.data(), 4); +} + +void ARM::addPltHeaderSymbols(InputSection &isec) const { + addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec); +} + +// Long form PLT entries that do not have any restrictions on the displacement +// of the .plt from the .plt.got. +static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) { + const uint8_t pltData[] = { + 0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2 + 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc + 0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip] + 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8 + }; + memcpy(buf, pltData, sizeof(pltData)); + uint64_t l1 = pltEntryAddr + 4; + write32le(buf + 12, gotPltEntryAddr - l1 - 8); +} + +// The default PLT entries require the .plt.got to be within 128 Mb of the +// .plt in the positive direction. +void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // The PLT entry is similar to the example given in Appendix A of ELF for + // the Arm Architecture. Instead of using the Group Relocations to find the + // optimal rotation for the 8-bit immediate used in the add instructions we + // hard code the most compact rotations for simplicity. This saves a load + // instruction over the long plt sequences. + const uint32_t pltData[] = { + 0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8 + 0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8 + 0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8 + }; + + uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8; + if (!llvm::isUInt<27>(offset)) { + // We cannot encode the Offset, use the long form. + writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff); + return; + } + write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff)); + write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff)); + write32le(buf + 8, pltData[2] | (offset & 0xfff)); + memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary +} + +void ARM::addPltSymbols(InputSection &isec, uint64_t off) const { + addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec); +} + +bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + // If S is an undefined weak symbol and does not have a PLT entry then it + // will be resolved as a branch to the next instruction. + if (s.isUndefWeak() && !s.isInPlt()) + return false; + // A state change from ARM to Thumb and vice versa must go through an + // interworking thunk if the relocation type is not R_ARM_CALL or + // R_ARM_THM_CALL. + switch (type) { + case R_ARM_PC24: + case R_ARM_PLT32: + case R_ARM_JUMP24: + // Source is ARM, all PLT entries are ARM so no interworking required. + // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). + if (expr == R_PC && ((s.getVA() & 1) == 1)) + return true; + LLVM_FALLTHROUGH; + case R_ARM_CALL: { + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); + } + case R_ARM_THM_JUMP19: + case R_ARM_THM_JUMP24: + // Source is Thumb, all PLT entries are ARM so interworking is required. + // Otherwise we need to interwork if Symbol has bit 0 clear (ARM). + if (expr == R_PLT_PC || ((s.getVA() & 1) == 0)) + return true; + LLVM_FALLTHROUGH; + case R_ARM_THM_CALL: { + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); + } + } + return false; +} + +uint32_t ARM::getThunkSectionSpacing() const { + // The placing of pre-created ThunkSections is controlled by the value + // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to + // place the ThunkSection such that all branches from the InputSections + // prior to the ThunkSection can reach a Thunk placed at the end of the + // ThunkSection. Graphically: + // | up to thunkSectionSpacing .text input sections | + // | ThunkSection | + // | up to thunkSectionSpacing .text input sections | + // | ThunkSection | + + // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This + // is to match the most common expected case of a Thumb 2 encoded BL, BLX or + // B.W: + // ARM B, BL, BLX range +/- 32MiB + // Thumb B.W, BL, BLX range +/- 16MiB + // Thumb B.W range +/- 1MiB + // If a branch cannot reach a pre-created ThunkSection a new one will be + // created so we can handle the rare cases of a Thumb 2 conditional branch. + // We intentionally use a lower size for thunkSectionSpacing than the maximum + // branch range so the end of the ThunkSection is more likely to be within + // range of the branch instruction that is furthest away. The value we shorten + // thunkSectionSpacing by is set conservatively to allow us to create 16,384 + // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to + // one of the Thunks going out of range. + + // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch + // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except + // ARMv6T2) the range is +/- 4MiB. + + return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000 + : 0x400000 - 0x7500; +} + +bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t range; + uint64_t instrSize; + + switch (type) { + case R_ARM_PC24: + case R_ARM_PLT32: + case R_ARM_JUMP24: + case R_ARM_CALL: + range = 0x2000000; + instrSize = 4; + break; + case R_ARM_THM_JUMP19: + range = 0x100000; + instrSize = 2; + break; + case R_ARM_THM_JUMP24: + case R_ARM_THM_CALL: + range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; + instrSize = 2; + break; + default: + return true; + } + // PC at Src is 2 instructions ahead, immediate of branch is signed + if (src > dst) + range -= 2 * instrSize; + else + range += instrSize; + + if ((dst & 0x1) == 0) + // Destination is ARM, if ARM caller then Src is already 4-byte aligned. + // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure + // destination will be 4 byte aligned. + src &= ~0x3; + else + // Bit 0 == 1 denotes Thumb state, it is not part of the range + dst &= ~0x1; + + uint64_t distance = (src > dst) ? src - dst : dst - src; + return distance <= range; +} + +void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_ARM_ABS32: + case R_ARM_BASE_PREL: + case R_ARM_GOTOFF32: + case R_ARM_GOT_BREL: + case R_ARM_GOT_PREL: + case R_ARM_REL32: + case R_ARM_RELATIVE: + case R_ARM_SBREL32: + case R_ARM_TARGET1: + case R_ARM_TARGET2: + case R_ARM_TLS_GD32: + case R_ARM_TLS_IE32: + case R_ARM_TLS_LDM32: + case R_ARM_TLS_LDO32: + case R_ARM_TLS_LE32: + case R_ARM_TLS_TPOFF32: + case R_ARM_TLS_DTPOFF32: + write32le(loc, val); + break; + case R_ARM_PREL31: + checkInt(loc, val, 31, type); + write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); + break; + case R_ARM_CALL: + // R_ARM_CALL is used for BL and BLX instructions, depending on the + // value of bit 0 of Val, we must select a BL or BLX instruction + if (val & 1) { + // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. + // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' + checkInt(loc, val, 26, type); + write32le(loc, 0xfa000000 | // opcode + ((val & 2) << 23) | // H + ((val >> 2) & 0x00ffffff)); // imm24 + break; + } + if ((read32le(loc) & 0xfe000000) == 0xfa000000) + // BLX (always unconditional) instruction to an ARM Target, select an + // unconditional BL. + write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); + // fall through as BL encoding is shared with B + LLVM_FALLTHROUGH; + case R_ARM_JUMP24: + case R_ARM_PC24: + case R_ARM_PLT32: + checkInt(loc, val, 26, type); + write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); + break; + case R_ARM_THM_JUMP11: + checkInt(loc, val, 12, type); + write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); + break; + case R_ARM_THM_JUMP19: + // Encoding T3: Val = S:J2:J1:imm6:imm11:0 + checkInt(loc, val, 21, type); + write16le(loc, + (read16le(loc) & 0xfbc0) | // opcode cond + ((val >> 10) & 0x0400) | // S + ((val >> 12) & 0x003f)); // imm6 + write16le(loc + 2, + 0x8000 | // opcode + ((val >> 8) & 0x0800) | // J2 + ((val >> 5) & 0x2000) | // J1 + ((val >> 1) & 0x07ff)); // imm11 + break; + case R_ARM_THM_CALL: + // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the + // value of bit 0 of Val, we must select a BL or BLX instruction + if ((val & 1) == 0) { + // Ensure BLX destination is 4-byte aligned. As BLX instruction may + // only be two byte aligned. This must be done before overflow check + val = alignTo(val, 4); + } + // Bit 12 is 0 for BLX, 1 for BL + write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); + if (!config->armJ1J2BranchEncoding) { + // Older Arm architectures do not support R_ARM_THM_JUMP24 and have + // different encoding rules and range due to J1 and J2 always being 1. + checkInt(loc, val, 23, type); + write16le(loc, + 0xf000 | // opcode + ((val >> 12) & 0x07ff)); // imm11 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode + 0x2800 | // J1 == J2 == 1 + ((val >> 1) & 0x07ff)); // imm11 + break; + } + // Fall through as rest of encoding is the same as B.W + LLVM_FALLTHROUGH; + case R_ARM_THM_JUMP24: + // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 + checkInt(loc, val, 25, type); + write16le(loc, + 0xf000 | // opcode + ((val >> 14) & 0x0400) | // S + ((val >> 12) & 0x03ff)); // imm10 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode + (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1 + (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2 + ((val >> 1) & 0x07ff)); // imm11 + break; + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVW_PREL_NC: + write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) | + (val & 0x0fff)); + break; + case R_ARM_MOVT_ABS: + case R_ARM_MOVT_PREL: + write32le(loc, (read32le(loc) & ~0x000f0fff) | + (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff)); + break; + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVT_PREL: + // Encoding T1: A = imm4:i:imm3:imm8 + write16le(loc, + 0xf2c0 | // opcode + ((val >> 17) & 0x0400) | // i + ((val >> 28) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val >> 12) & 0x7000) | // imm3 + ((val >> 16) & 0x00ff)); // imm8 + break; + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVW_PREL_NC: + // Encoding T3: A = imm4:i:imm3:imm8 + write16le(loc, + 0xf240 | // opcode + ((val >> 1) & 0x0400) | // i + ((val >> 12) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val << 4) & 0x7000) | // imm3 + (val & 0x00ff)); // imm8 + break; + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + } +} + +int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + default: + return 0; + case R_ARM_ABS32: + case R_ARM_BASE_PREL: + case R_ARM_GOTOFF32: + case R_ARM_GOT_BREL: + case R_ARM_GOT_PREL: + case R_ARM_REL32: + case R_ARM_TARGET1: + case R_ARM_TARGET2: + case R_ARM_TLS_GD32: + case R_ARM_TLS_LDM32: + case R_ARM_TLS_LDO32: + case R_ARM_TLS_IE32: + case R_ARM_TLS_LE32: + return SignExtend64<32>(read32le(buf)); + case R_ARM_PREL31: + return SignExtend64<31>(read32le(buf)); + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PC24: + case R_ARM_PLT32: + return SignExtend64<26>(read32le(buf) << 2); + case R_ARM_THM_JUMP11: + return SignExtend64<12>(read16le(buf) << 1); + case R_ARM_THM_JUMP19: { + // Encoding T3: A = S:J2:J1:imm10:imm6:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<20>(((hi & 0x0400) << 10) | // S + ((lo & 0x0800) << 8) | // J2 + ((lo & 0x2000) << 5) | // J1 + ((hi & 0x003f) << 12) | // imm6 + ((lo & 0x07ff) << 1)); // imm11:0 + } + case R_ARM_THM_CALL: + if (!config->armJ1J2BranchEncoding) { + // Older Arm architectures do not support R_ARM_THM_JUMP24 and have + // different encoding rules and range due to J1 and J2 always being 1. + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11 + ((lo & 0x7ff) << 1)); // imm11:0 + break; + } + LLVM_FALLTHROUGH; + case R_ARM_THM_JUMP24: { + // Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0 + // I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S) + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<24>(((hi & 0x0400) << 14) | // S + (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1 + (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2 + ((hi & 0x003ff) << 12) | // imm0 + ((lo & 0x007ff) << 1)); // imm11:0 + } + // ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and + // MOVT is in the range -32768 <= A < 32768 + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + case R_ARM_MOVW_PREL_NC: + case R_ARM_MOVT_PREL: { + uint64_t val = read32le(buf) & 0x000f0fff; + return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff)); + } + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_PREL_NC: + case R_ARM_THM_MOVT_PREL: { + // Encoding T3: A = imm4:i:imm3:imm8 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4 + ((hi & 0x0400) << 1) | // i + ((lo & 0x7000) >> 4) | // imm3 + (lo & 0x00ff)); // imm8 + } + } +} + +TargetInfo *elf::getARMTargetInfo() { + static ARM target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/ARM.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/AVR.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/AVR.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/AVR.cpp (revision 352529) @@ -0,0 +1,76 @@ +//===- AVR.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// AVR is a Harvard-architecture 8-bit micrcontroller designed for small +// baremetal programs. All AVR-family processors have 32 8-bit registers. +// The tiniest AVR has 32 byte RAM and 1 KiB program memory, and the largest +// one supports up to 2^24 data address space and 2^22 code address space. +// +// Since it is a baremetal programming, there's usually no loader to load +// ELF files on AVRs. You are expected to link your program against address +// 0 and pull out a .text section from the result using objcopy, so that you +// can write the linked code to on-chip flush memory. You can do that with +// the following commands: +// +// ld.lld -Ttext=0 -o foo foo.o +// objcopy -O binary --only-section=.text foo output.bin +// +// Note that the current AVR support is very preliminary so you can't +// link any useful program yet, though. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.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 AVR final : public TargetInfo { +public: + AVR(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +AVR::AVR() { noneRel = R_AVR_NONE; } + +RelExpr AVR::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + return R_ABS; +} + +void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_AVR_CALL: { + uint16_t hi = val >> 17; + uint16_t lo = val >> 1; + write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1)); + write16le(loc + 2, lo); + break; + } + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + } +} + +TargetInfo *elf::getAVRTargetInfo() { + static AVR target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/AVR.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/Hexagon.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/Hexagon.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/Hexagon.cpp (revision 352529) @@ -0,0 +1,291 @@ +//===-- Hexagon.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.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 Hexagon final : public TargetInfo { +public: + Hexagon(); + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; +}; +} // namespace + +Hexagon::Hexagon() { + pltRel = R_HEX_JMP_SLOT; + relativeRel = R_HEX_RELATIVE; + gotRel = R_HEX_GLOB_DAT; + symbolicRel = R_HEX_32; + + // The zero'th GOT entry is reserved for the address of _DYNAMIC. The + // next 3 are reserved for the dynamic loader. + gotPltHeaderEntriesNum = 4; + + pltEntrySize = 16; + pltHeaderSize = 32; + + // Hexagon Linux uses 64K pages by default. + defaultMaxPageSize = 0x10000; + noneRel = R_HEX_NONE; +} + +uint32_t Hexagon::calcEFlags() const { + assert(!objectFiles.empty()); + + // The architecture revision must always be equal to or greater than + // greatest revision in the list of inputs. + uint32_t ret = 0; + for (InputFile *f : objectFiles) { + uint32_t eflags = cast>(f)->getObj().getHeader()->e_flags; + if (eflags > ret) + ret = eflags; + } + return ret; +} + +static uint32_t applyMask(uint32_t mask, uint32_t data) { + uint32_t result = 0; + size_t off = 0; + + for (size_t bit = 0; bit != 32; ++bit) { + uint32_t valBit = (data >> off) & 1; + uint32_t maskBit = (mask >> bit) & 1; + if (maskBit) { + result |= (valBit << bit); + ++off; + } + } + return result; +} + +RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_HEX_B9_PCREL: + case R_HEX_B9_PCREL_X: + case R_HEX_B13_PCREL: + case R_HEX_B15_PCREL: + case R_HEX_B15_PCREL_X: + case R_HEX_6_PCREL_X: + case R_HEX_32_PCREL: + return R_PC; + case R_HEX_B22_PCREL: + case R_HEX_PLT_B22_PCREL: + case R_HEX_B22_PCREL_X: + case R_HEX_B32_PCREL_X: + return R_PLT_PC; + case R_HEX_GOT_11_X: + case R_HEX_GOT_16_X: + case R_HEX_GOT_32_6_X: + return R_HEXAGON_GOT; + default: + return R_ABS; + } +} + +static uint32_t findMaskR6(uint32_t insn) { + // There are (arguably too) many relocation masks for the DSP's + // R_HEX_6_X type. The table below is used to select the correct mask + // for the given instruction. + struct InstructionMask { + uint32_t cmpMask; + uint32_t relocMask; + }; + + static const InstructionMask r6[] = { + {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f}, + {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80}, + {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0}, + {0x42000000, 0x000020f8}, {0x43000000, 0x000007e0}, + {0x44000000, 0x000020f8}, {0x45000000, 0x000007e0}, + {0x46000000, 0x000020f8}, {0x47000000, 0x000007e0}, + {0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000}, + {0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60}, + {0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60}, + {0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f}, + {0xad000000, 0x0000003f}, {0xaf000000, 0x00030078}, + {0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0}, + {0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}}; + + // Duplex forms have a fixed mask and parse bits 15:14 are always + // zero. Non-duplex insns will always have at least one bit set in the + // parse field. + if ((0xC000 & insn) == 0x0) + return 0x03f00000; + + for (InstructionMask i : r6) + if ((0xff000000 & insn) == i.cmpMask) + return i.relocMask; + + error("unrecognized instruction for R_HEX_6 relocation: 0x" + + utohexstr(insn)); + return 0; +} + +static uint32_t findMaskR8(uint32_t insn) { + if ((0xff000000 & insn) == 0xde000000) + return 0x00e020e8; + if ((0xff000000 & insn) == 0x3c000000) + return 0x0000207f; + return 0x00001fe0; +} + +static uint32_t findMaskR11(uint32_t insn) { + if ((0xff000000 & insn) == 0xa1000000) + return 0x060020ff; + return 0x06003fe0; +} + +static uint32_t findMaskR16(uint32_t insn) { + if ((0xff000000 & insn) == 0x48000000) + return 0x061f20ff; + if ((0xff000000 & insn) == 0x49000000) + return 0x061f3fe0; + if ((0xff000000 & insn) == 0x78000000) + return 0x00df3fe0; + if ((0xff000000 & insn) == 0xb0000000) + return 0x0fe03fe0; + + error("unrecognized instruction for R_HEX_16_X relocation: 0x" + + utohexstr(insn)); + return 0; +} + +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } + +void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_HEX_NONE: + break; + case R_HEX_6_PCREL_X: + case R_HEX_6_X: + or32le(loc, applyMask(findMaskR6(read32le(loc)), val)); + break; + case R_HEX_8_X: + or32le(loc, applyMask(findMaskR8(read32le(loc)), val)); + break; + case R_HEX_9_X: + or32le(loc, applyMask(0x00003fe0, val & 0x3f)); + break; + case R_HEX_10_X: + or32le(loc, applyMask(0x00203fe0, val & 0x3f)); + break; + case R_HEX_11_X: + case R_HEX_GOT_11_X: + or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f)); + break; + case R_HEX_12_X: + or32le(loc, applyMask(0x000007e0, val)); + break; + case R_HEX_16_X: // These relocs only have 6 effective bits. + case R_HEX_GOT_16_X: + or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f)); + break; + case R_HEX_32: + case R_HEX_32_PCREL: + or32le(loc, val); + break; + case R_HEX_32_6_X: + case R_HEX_GOT_32_6_X: + or32le(loc, applyMask(0x0fff3fff, val >> 6)); + break; + case R_HEX_B9_PCREL: + or32le(loc, applyMask(0x003000fe, val >> 2)); + break; + case R_HEX_B9_PCREL_X: + or32le(loc, applyMask(0x003000fe, val & 0x3f)); + break; + case R_HEX_B13_PCREL: + or32le(loc, applyMask(0x00202ffe, val >> 2)); + break; + case R_HEX_B15_PCREL: + or32le(loc, applyMask(0x00df20fe, val >> 2)); + break; + case R_HEX_B15_PCREL_X: + or32le(loc, applyMask(0x00df20fe, val & 0x3f)); + break; + case R_HEX_B22_PCREL: + case R_HEX_PLT_B22_PCREL: + or32le(loc, applyMask(0x1ff3ffe, val >> 2)); + break; + case R_HEX_B22_PCREL_X: + or32le(loc, applyMask(0x1ff3ffe, val & 0x3f)); + break; + case R_HEX_B32_PCREL_X: + or32le(loc, applyMask(0x0fff3fff, val >> 6)); + break; + case R_HEX_HI16: + or32le(loc, applyMask(0x00c03fff, val >> 16)); + break; + case R_HEX_LO16: + or32le(loc, applyMask(0x00c03fff, val)); + break; + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + break; + } +} + +void Hexagon::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { + 0x00, 0x40, 0x00, 0x00, // { immext (#0) + 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0 + 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn + 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2 + 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1 + 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn + 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker + 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment + }; + memcpy(buf, pltData, sizeof(pltData)); + + // Offset from PLT0 to the GOT. + uint64_t off = in.gotPlt->getVA() - in.plt->getVA(); + relocateOne(buf, R_HEX_B32_PCREL_X, off); + relocateOne(buf + 4, R_HEX_6_PCREL_X, off); +} + +void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { + 0x00, 0x40, 0x00, 0x00, // { immext (#0) + 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } + 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) + 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 + }; + memcpy(buf, inst, sizeof(inst)); + + relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); + relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); +} + +TargetInfo *elf::getHexagonTargetInfo() { + static Hexagon target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/Hexagon.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/MSP430.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/MSP430.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/MSP430.cpp (revision 352529) @@ -0,0 +1,93 @@ +//===- MSP430.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set +// has only 27 core instructions orthogonally augmented with a variety +// of addressing modes for source and destination operands. Entire address space +// of MSP430 is 64KB (the extended MSP430X architecture is not considered here). +// A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty +// of peripherals and is generally optimized for a low power consumption. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.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 MSP430 final : public TargetInfo { +public: + MSP430(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +MSP430::MSP430() { + // mov.b #0, r3 + trapInstr = {0x43, 0x43, 0x43, 0x43}; +} + +RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_MSP430_10_PCREL: + case R_MSP430_16_PCREL: + case R_MSP430_16_PCREL_BYTE: + case R_MSP430_2X_PCREL: + case R_MSP430_RL_PCREL: + case R_MSP430_SYM_DIFF: + return R_PC; + default: + return R_ABS; + } +} + +void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_MSP430_8: + checkIntUInt(loc, val, 8, type); + *loc = val; + break; + case R_MSP430_16: + case R_MSP430_16_PCREL: + case R_MSP430_16_BYTE: + case R_MSP430_16_PCREL_BYTE: + checkIntUInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_MSP430_32: + checkIntUInt(loc, val, 32, type); + write32le(loc, val); + break; + case R_MSP430_10_PCREL: { + int16_t offset = ((int16_t)val >> 1) - 1; + checkInt(loc, offset, 10, type); + write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF)); + break; + } + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + } +} + +TargetInfo *elf::getMSP430TargetInfo() { + static MSP430 target; + return ⌖ +} Index: vendor/lld/lld-release_900-r372316/ELF/Arch/Mips.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/Mips.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/Mips.cpp (revision 352529) @@ -0,0 +1,741 @@ +//===- MIPS.cpp -----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "OutputSections.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "Thunks.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.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 { +template class MIPS final : public TargetInfo { +public: + MIPS(); + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + bool usesOnlyLowPageBits(RelType type) const override; +}; +} // namespace + +template MIPS::MIPS() { + gotPltHeaderEntriesNum = 2; + defaultMaxPageSize = 65536; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + copyRel = R_MIPS_COPY; + noneRel = R_MIPS_NONE; + pltRel = R_MIPS_JUMP_SLOT; + needsThunks = true; + + // Set `sigrie 1` as a trap instruction. + write32(trapInstr.data(), 0x04170001); + + if (ELFT::Is64Bits) { + relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; + symbolicRel = R_MIPS_64; + tlsGotRel = R_MIPS_TLS_TPREL64; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; + tlsOffsetRel = R_MIPS_TLS_DTPREL64; + } else { + relativeRel = R_MIPS_REL32; + symbolicRel = R_MIPS_32; + tlsGotRel = R_MIPS_TLS_TPREL32; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; + tlsOffsetRel = R_MIPS_TLS_DTPREL32; + } +} + +template uint32_t MIPS::calcEFlags() const { + return calcMipsEFlags(); +} + +template +RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + // See comment in the calculateMipsRelChain. + if (ELFT::Is64Bits || config->mipsN32Abi) + type &= 0xff; + + switch (type) { + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + return R_HINT; + case R_MIPS_GPREL16: + case R_MIPS_GPREL32: + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_GPREL7_S2: + return R_MIPS_GOTREL; + case R_MIPS_26: + case R_MICROMIPS_26_S1: + return R_PLT; + case R_MICROMIPS_PC26_S1: + return R_PLT_PC; + case R_MIPS_HI16: + case R_MIPS_LO16: + case R_MIPS_HIGHER: + case R_MIPS_HIGHEST: + case R_MICROMIPS_HI16: + case R_MICROMIPS_LO16: + // R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate + // offset between start of function and 'gp' value which by default + // equal to the start of .got section. In that case we consider these + // relocations as relative. + if (&s == ElfSym::mipsGpDisp) + return R_MIPS_GOT_GP_PC; + if (&s == ElfSym::mipsLocalGp) + return R_MIPS_GOT_GP; + LLVM_FALLTHROUGH; + case R_MIPS_32: + case R_MIPS_64: + case R_MIPS_GOT_OFST: + case R_MIPS_SUB: + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_TPREL64: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + return R_ABS; + case R_MIPS_PC32: + case R_MIPS_PC16: + case R_MIPS_PC19_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC26_S2: + case R_MIPS_PCHI16: + case R_MIPS_PCLO16: + case R_MICROMIPS_PC7_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC18_S3: + case R_MICROMIPS_PC19_S2: + case R_MICROMIPS_PC23_S2: + case R_MICROMIPS_PC21_S1: + return R_PC; + case R_MIPS_GOT16: + case R_MICROMIPS_GOT16: + if (s.isLocal()) + return R_MIPS_GOT_LOCAL_PAGE; + LLVM_FALLTHROUGH; + case R_MIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_TLS_GOTTPREL: + case R_MICROMIPS_CALL16: + case R_MICROMIPS_TLS_GOTTPREL: + return R_MIPS_GOT_OFF; + case R_MIPS_CALL_HI16: + case R_MIPS_CALL_LO16: + case R_MIPS_GOT_HI16: + case R_MIPS_GOT_LO16: + case R_MICROMIPS_CALL_HI16: + case R_MICROMIPS_CALL_LO16: + case R_MICROMIPS_GOT_HI16: + case R_MICROMIPS_GOT_LO16: + return R_MIPS_GOT_OFF32; + case R_MIPS_GOT_PAGE: + return R_MIPS_GOT_LOCAL_PAGE; + case R_MIPS_TLS_GD: + case R_MICROMIPS_TLS_GD: + return R_MIPS_TLSGD; + case R_MIPS_TLS_LDM: + case R_MICROMIPS_TLS_LDM: + return R_MIPS_TLSLD; + case R_MIPS_NONE: + return R_NONE; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +template RelType MIPS::getDynRel(RelType type) const { + if (type == symbolicRel) + return type; + return R_MIPS_NONE; +} + +template +void MIPS::writeGotPlt(uint8_t *buf, const Symbol &) const { + uint64_t va = in.plt->getVA(); + if (isMicroMips()) + va |= 1; + write32(buf, va); +} + +template static uint32_t readShuffle(const uint8_t *loc) { + // The major opcode of a microMIPS instruction needs to appear + // in the first 16-bit word (lowest address) for efficient hardware + // decode so that it knows if the instruction is 16-bit or 32-bit + // as early as possible. To do so, little-endian binaries keep 16-bit + // words in a big-endian order. That is why we have to swap these + // words to get a correct value. + uint32_t v = read32(loc); + if (E == support::little) + return (v << 16) | (v >> 16); + return v; +} + +template +static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint32_t instr = read32(loc); + uint32_t mask = 0xffffffff >> (32 - bitsSize); + uint32_t data = (instr & ~mask) | ((v >> shift) & mask); + write32(loc, data); +} + +template +static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + // See comments in readShuffle for purpose of this code. + uint16_t *words = (uint16_t *)loc; + if (E == support::little) + std::swap(words[0], words[1]); + + writeValue(loc, v, bitsSize, shift); + + if (E == support::little) + std::swap(words[0], words[1]); +} + +template +static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint16_t instr = read16(loc); + uint16_t mask = 0xffff >> (16 - bitsSize); + uint16_t data = (instr & ~mask) | ((v >> shift) & mask); + write16(loc, data); +} + +template void MIPS::writePltHeader(uint8_t *buf) const { + const endianness e = ELFT::TargetEndianness; + if (isMicroMips()) { + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + // Overwrite trap instructions written by Writer::writeTrapInstr. + memset(buf, 0, pltHeaderSize); + + write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16(buf + 4, 0xff23); // lw $25, 0($3) + write16(buf + 8, 0x0535); // subu16 $2, $2, $3 + write16(buf + 10, 0x2525); // srl16 $2, $2, 2 + write16(buf + 12, 0x3302); // addiu $24, $2, -2 + write16(buf + 14, 0xfffe); + write16(buf + 16, 0x0dff); // move $15, $31 + if (isMipsR6()) { + write16(buf + 18, 0x0f83); // move $28, $3 + write16(buf + 20, 0x472b); // jalrc $25 + write16(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); + } else { + write16(buf + 18, 0x45f9); // jalrc $25 + write16(buf + 20, 0x0f83); // move $28, $3 + write16(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); + } + return; + } + + if (config->mipsN32Abi) { + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 + } else if (ELFT::Is64Bits) { + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3 + } else { + write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 + } + + uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809; + write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 + write32(buf + 28, 0x2718fffe); // subu $24, $24, 2 + + uint64_t gotPlt = in.gotPlt->getVA(); + writeValue(buf, gotPlt + 0x8000, 16, 16); + writeValue(buf + 4, gotPlt, 16, 0); + writeValue(buf + 8, gotPlt, 16, 0); +} + +template +void MIPS::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const endianness e = ELFT::TargetEndianness; + if (isMicroMips()) { + // Overwrite trap instructions written by Writer::writeTrapInstr. + memset(buf, 0, pltEntrySize); + + if (isMipsR6()) { + write16(buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x0f02); // move $24, $2 + write16(buf + 10, 0x4723); // jrc $25 / jr16 $25 + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); + } else { + write16(buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16(buf + 10, 0x0f02); // move $24, $2 + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); + } + return; + } + + uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000; + uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009) + : (config->zHazardplt ? 0x03200408 : 0x03200008); + uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000; + + write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) + write32(buf + 8, jrInst); // jr $25 / jr.hb $25 + write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) + writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16); + writeValue(buf + 4, gotPltEntryAddr, 16, 0); + writeValue(buf + 12, gotPltEntryAddr, 16, 0); +} + +template +bool MIPS::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + // Any MIPS PIC code function is invoked with its address in register $t9. + // So if we have a branch instruction from non-PIC code to the PIC one + // we cannot make the jump directly and need to create a small stubs + // to save the target function address. + // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 && + type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1) + return false; + auto *f = dyn_cast_or_null>(file); + if (!f) + return false; + // If current file has PIC code, LA25 stub is not required. + if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC) + return false; + auto *d = dyn_cast(&s); + // LA25 is required if target file has PIC code + // or target symbol is a PIC symbol. + return d && isMipsPIC(d); +} + +template +int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { + const endianness e = ELFT::TargetEndianness; + switch (type) { + case R_MIPS_32: + case R_MIPS_GPREL32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + return SignExtend64<32>(read32(buf)); + case R_MIPS_26: + // FIXME (simon): If the relocation target symbol is not a PLT entry + // we should use another expression for calculation: + // ((A << 2) | (P & 0xf0000000)) >> 2 + return SignExtend64<28>(read32(buf) << 2); + case R_MIPS_GOT16: + case R_MIPS_HI16: + case R_MIPS_PCHI16: + return SignExtend64<16>(read32(buf)) << 16; + case R_MIPS_GPREL16: + case R_MIPS_LO16: + case R_MIPS_PCLO16: + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + return SignExtend64<16>(read32(buf)); + case R_MICROMIPS_GOT16: + case R_MICROMIPS_HI16: + return SignExtend64<16>(readShuffle(buf)) << 16; + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_LO16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + return SignExtend64<16>(readShuffle(buf)); + case R_MICROMIPS_GPREL7_S2: + return SignExtend64<9>(readShuffle(buf) << 2); + case R_MIPS_PC16: + return SignExtend64<18>(read32(buf) << 2); + case R_MIPS_PC19_S2: + return SignExtend64<21>(read32(buf) << 2); + case R_MIPS_PC21_S2: + return SignExtend64<23>(read32(buf) << 2); + case R_MIPS_PC26_S2: + return SignExtend64<28>(read32(buf) << 2); + case R_MIPS_PC32: + return SignExtend64<32>(read32(buf)); + case R_MICROMIPS_26_S1: + return SignExtend64<27>(readShuffle(buf) << 1); + case R_MICROMIPS_PC7_S1: + return SignExtend64<8>(read16(buf) << 1); + case R_MICROMIPS_PC10_S1: + return SignExtend64<11>(read16(buf) << 1); + case R_MICROMIPS_PC16_S1: + return SignExtend64<17>(readShuffle(buf) << 1); + case R_MICROMIPS_PC18_S3: + return SignExtend64<21>(readShuffle(buf) << 3); + case R_MICROMIPS_PC19_S2: + return SignExtend64<21>(readShuffle(buf) << 2); + case R_MICROMIPS_PC21_S1: + return SignExtend64<22>(readShuffle(buf) << 1); + case R_MICROMIPS_PC23_S2: + return SignExtend64<25>(readShuffle(buf) << 2); + case R_MICROMIPS_PC26_S1: + return SignExtend64<27>(readShuffle(buf) << 1); + default: + return 0; + } +} + +static std::pair +calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) { + // MIPS N64 ABI packs multiple relocations into the single relocation + // record. In general, all up to three relocations can have arbitrary + // types. In fact, Clang and GCC uses only a few combinations. For now, + // we support two of them. That is allow to pass at least all LLVM + // test suite cases. + // / R_MIPS_SUB / R_MIPS_HI16 | R_MIPS_LO16 + // / R_MIPS_64 / R_MIPS_NONE + // The first relocation is a 'real' relocation which is calculated + // using the corresponding symbol's value. The second and the third + // relocations used to modify result of the first one: extend it to + // 64-bit, extract high or low part etc. For details, see part 2.9 Relocation + // at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf + RelType type2 = (type >> 8) & 0xff; + RelType type3 = (type >> 16) & 0xff; + if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE) + return std::make_pair(type, val); + if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE) + return std::make_pair(type2, val); + if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16)) + return std::make_pair(type3, -val); + error(getErrorLocation(loc) + "unsupported relocations combination " + + Twine(type)); + return std::make_pair(type & 0xff, val); +} + +static bool isBranchReloc(RelType type) { + return type == R_MIPS_26 || type == R_MIPS_PC26_S2 || + type == R_MIPS_PC21_S2 || type == R_MIPS_PC16; +} + +static bool isMicroBranchReloc(RelType type) { + return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 || + type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1; +} + +template +static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) { + // Here we need to detect jump/branch from regular MIPS code + // to a microMIPS target and vice versa. In that cases jump + // instructions need to be replaced by their "cross-mode" + // equivalents. + const endianness e = ELFT::TargetEndianness; + bool isMicroTgt = val & 0x1; + bool isCrossJump = (isMicroTgt && isBranchReloc(type)) || + (!isMicroTgt && isMicroBranchReloc(type)); + if (!isCrossJump) + return val; + + switch (type) { + case R_MIPS_26: { + uint32_t inst = read32(loc) >> 26; + if (inst == 0x3 || inst == 0x1d) { // JAL or JALX + writeValue(loc, 0x1d << 26, 32, 0); + return val; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t inst = readShuffle(loc) >> 26; + if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32 + val >>= 1; + writeShuffleValue(loc, 0x3c << 26, 32, 0); + return val; + } + break; + } + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + // FIXME (simon): Support valid branch relocations. + break; + default: + llvm_unreachable("unexpected jump/branch relocation"); + } + + error(getErrorLocation(loc) + + "unsupported jump/branch instruction between ISA modes referenced by " + + toString(type) + " relocation"); + return val; +} + +template +void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + const endianness e = ELFT::TargetEndianness; + + if (ELFT::Is64Bits || config->mipsN32Abi) + std::tie(type, val) = calculateMipsRelChain(loc, type, val); + + // Detect cross-mode jump/branch and fix instruction. + val = fixupCrossModeJump(loc, type, val); + + // Thread pointer and DRP offsets from the start of TLS data area. + // https://www.linux-mips.org/wiki/NPTL + if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 || + type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 || + type == R_MICROMIPS_TLS_DTPREL_HI16 || + type == R_MICROMIPS_TLS_DTPREL_LO16) { + val -= 0x8000; + } else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 || + type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 || + type == R_MICROMIPS_TLS_TPREL_HI16 || + type == R_MICROMIPS_TLS_TPREL_LO16) { + val -= 0x7000; + } + + switch (type) { + case R_MIPS_32: + case R_MIPS_GPREL32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + write32(loc, val); + break; + case R_MIPS_64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + write64(loc, val); + break; + case R_MIPS_26: + writeValue(loc, val, 26, 2); + break; + case R_MIPS_GOT16: + // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode + // is updated addend (not a GOT index). In that case write high 16 bits + // to store a correct addend value. + if (config->relocatable) { + writeValue(loc, val + 0x8000, 16, 16); + } else { + checkInt(loc, val, 16, type); + writeValue(loc, val, 16, 0); + } + break; + case R_MICROMIPS_GOT16: + if (config->relocatable) { + writeShuffleValue(loc, val + 0x8000, 16, 16); + } else { + checkInt(loc, val, 16, type); + writeShuffleValue(loc, val, 16, 0); + } + break; + case R_MIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MIPS_GPREL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_GOTTPREL: + case R_MIPS_TLS_LDM: + checkInt(loc, val, 16, type); + LLVM_FALLTHROUGH; + case R_MIPS_CALL_LO16: + case R_MIPS_GOT_LO16: + case R_MIPS_GOT_OFST: + case R_MIPS_LO16: + case R_MIPS_PCLO16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_LO16: + writeValue(loc, val, 16, 0); + break; + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + checkInt(loc, val, 16, type); + writeShuffleValue(loc, val, 16, 0); + break; + case R_MICROMIPS_CALL16: + case R_MICROMIPS_CALL_LO16: + case R_MICROMIPS_LO16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_TPREL_LO16: + writeShuffleValue(loc, val, 16, 0); + break; + case R_MICROMIPS_GPREL7_S2: + checkInt(loc, val, 7, type); + writeShuffleValue(loc, val, 7, 2); + break; + case R_MIPS_CALL_HI16: + case R_MIPS_GOT_HI16: + case R_MIPS_HI16: + case R_MIPS_PCHI16: + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_TPREL_HI16: + writeValue(loc, val + 0x8000, 16, 16); + break; + case R_MICROMIPS_CALL_HI16: + case R_MICROMIPS_GOT_HI16: + case R_MICROMIPS_HI16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_TPREL_HI16: + writeShuffleValue(loc, val + 0x8000, 16, 16); + break; + case R_MIPS_HIGHER: + writeValue(loc, val + 0x80008000, 16, 32); + break; + case R_MIPS_HIGHEST: + writeValue(loc, val + 0x800080008000, 16, 48); + break; + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + // Ignore this optimization relocation for now + break; + case R_MIPS_PC16: + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 18, type); + writeValue(loc, val, 16, 2); + break; + case R_MIPS_PC19_S2: + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + writeValue(loc, val, 19, 2); + break; + case R_MIPS_PC21_S2: + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 23, type); + writeValue(loc, val, 21, 2); + break; + case R_MIPS_PC26_S2: + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 28, type); + writeValue(loc, val, 26, 2); + break; + case R_MIPS_PC32: + writeValue(loc, val, 32, 0); + break; + case R_MICROMIPS_26_S1: + case R_MICROMIPS_PC26_S1: + checkInt(loc, val, 27, type); + writeShuffleValue(loc, val, 26, 1); + break; + case R_MICROMIPS_PC7_S1: + checkInt(loc, val, 8, type); + writeMicroRelocation16(loc, val, 7, 1); + break; + case R_MICROMIPS_PC10_S1: + checkInt(loc, val, 11, type); + writeMicroRelocation16(loc, val, 10, 1); + break; + case R_MICROMIPS_PC16_S1: + checkInt(loc, val, 17, type); + writeShuffleValue(loc, val, 16, 1); + break; + case R_MICROMIPS_PC18_S3: + checkInt(loc, val, 21, type); + writeShuffleValue(loc, val, 18, 3); + break; + case R_MICROMIPS_PC19_S2: + checkInt(loc, val, 21, type); + writeShuffleValue(loc, val, 19, 2); + break; + case R_MICROMIPS_PC21_S1: + checkInt(loc, val, 22, type); + writeShuffleValue(loc, val, 21, 1); + break; + case R_MICROMIPS_PC23_S2: + checkInt(loc, val, 25, type); + writeShuffleValue(loc, val, 23, 2); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +template bool MIPS::usesOnlyLowPageBits(RelType type) const { + return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST || + type == R_MICROMIPS_LO16; +} + +// Return true if the symbol is a PIC function. +template bool elf::isMipsPIC(const Defined *sym) { + if (!sym->isFunc()) + return false; + + if (sym->stOther & STO_MIPS_PIC) + return true; + + if (!sym->section) + return false; + + ObjFile *file = + cast(sym->section)->template getFile(); + if (!file) + return false; + + return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; +} + +template TargetInfo *elf::getMipsTargetInfo() { + static MIPS target; + return ⌖ +} + +template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); + +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/Mips.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/MipsArchTree.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/MipsArchTree.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/MipsArchTree.cpp (revision 352529) @@ -0,0 +1,389 @@ +//===- MipsArchTree.cpp --------------------------------------------------===// +// +// 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 +// +//===---------------------------------------------------------------------===// +// +// This file contains a helper function for the Writer. +// +//===---------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Writer.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/MipsABIFlags.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf; + +namespace { +struct ArchTreeEdge { + uint32_t child; + uint32_t parent; +}; + +struct FileFlags { + InputFile *file; + uint32_t flags; +}; +} // namespace + +static StringRef getAbiName(uint32_t flags) { + switch (flags) { + case 0: + return "n64"; + case EF_MIPS_ABI2: + return "n32"; + case EF_MIPS_ABI_O32: + return "o32"; + case EF_MIPS_ABI_O64: + return "o64"; + case EF_MIPS_ABI_EABI32: + return "eabi32"; + case EF_MIPS_ABI_EABI64: + return "eabi64"; + default: + return "unknown"; + } +} + +static StringRef getNanName(bool isNan2008) { + return isNan2008 ? "2008" : "legacy"; +} + +static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; } + +static void checkFlags(ArrayRef files) { + assert(!files.empty() && "expected non-empty file list"); + + uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + bool nan = files[0].flags & EF_MIPS_NAN2008; + bool fp = files[0].flags & EF_MIPS_FP64; + + for (const FileFlags &f : files) { + if (config->is64 && f.flags & EF_MIPS_MICROMIPS) + error(toString(f.file) + ": microMIPS 64-bit is not supported"); + + uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + if (abi != abi2) + error(toString(f.file) + ": ABI '" + getAbiName(abi2) + + "' is incompatible with target ABI '" + getAbiName(abi) + "'"); + + bool nan2 = f.flags & EF_MIPS_NAN2008; + if (nan != nan2) + error(toString(f.file) + ": -mnan=" + getNanName(nan2) + + " is incompatible with target -mnan=" + getNanName(nan)); + + bool fp2 = f.flags & EF_MIPS_FP64; + if (fp != fp2) + error(toString(f.file) + ": -mfp" + getFpName(fp2) + + " is incompatible with target -mfp" + getFpName(fp)); + } +} + +static uint32_t getMiscFlags(ArrayRef files) { + uint32_t ret = 0; + for (const FileFlags &f : files) + ret |= f.flags & + (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER | + EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE); + return ret; +} + +static uint32_t getPicFlags(ArrayRef files) { + // Check PIC/non-PIC compatibility. + bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) { + bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + if (isPic && !isPic2) + warn(toString(f.file) + + ": linking non-abicalls code with abicalls code " + + toString(files[0].file)); + if (!isPic && isPic2) + warn(toString(f.file) + + ": linking abicalls code with non-abicalls code " + + toString(files[0].file)); + } + + // Compute the result PIC/non-PIC flag. + uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) + ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + if (ret & EF_MIPS_PIC) + ret |= EF_MIPS_CPIC; + return ret; +} + +static ArchTreeEdge archTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + // MIPS64R2 extensions. + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2}, + // MIPS64 extensions. + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64}, + {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, + // MIPS V extensions. + {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, + // R5000 extensions. + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400}, + // MIPS IV extensions. + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4}, + {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, + // VR4100 extensions. + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, + // MIPS III extensions. + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3}, + // MIPS32 extensions. + {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32}, + // MIPS II extensions. + {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2}, + {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2}, + // MIPS I extensions. + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1}, + {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, +}; + +static bool isArchMatched(uint32_t New, uint32_t res) { + if (New == res) + return true; + if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) + return true; + if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) + return true; + for (const auto &edge : archTree) { + if (res == edge.child) { + res = edge.parent; + if (res == New) + return true; + } + } + return false; +} + +static StringRef getMachName(uint32_t flags) { + switch (flags & EF_MIPS_MACH) { + case EF_MIPS_MACH_NONE: + return ""; + case EF_MIPS_MACH_3900: + return "r3900"; + case EF_MIPS_MACH_4010: + return "r4010"; + case EF_MIPS_MACH_4100: + return "r4100"; + case EF_MIPS_MACH_4650: + return "r4650"; + case EF_MIPS_MACH_4120: + return "r4120"; + case EF_MIPS_MACH_4111: + return "r4111"; + case EF_MIPS_MACH_5400: + return "vr5400"; + case EF_MIPS_MACH_5900: + return "vr5900"; + case EF_MIPS_MACH_5500: + return "vr5500"; + case EF_MIPS_MACH_9000: + return "rm9000"; + case EF_MIPS_MACH_LS2E: + return "loongson2e"; + case EF_MIPS_MACH_LS2F: + return "loongson2f"; + case EF_MIPS_MACH_LS3A: + return "loongson3a"; + case EF_MIPS_MACH_OCTEON: + return "octeon"; + case EF_MIPS_MACH_OCTEON2: + return "octeon2"; + case EF_MIPS_MACH_OCTEON3: + return "octeon3"; + case EF_MIPS_MACH_SB1: + return "sb1"; + case EF_MIPS_MACH_XLR: + return "xlr"; + default: + return "unknown machine"; + } +} + +static StringRef getArchName(uint32_t flags) { + switch (flags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_1: + return "mips1"; + case EF_MIPS_ARCH_2: + return "mips2"; + case EF_MIPS_ARCH_3: + return "mips3"; + case EF_MIPS_ARCH_4: + return "mips4"; + case EF_MIPS_ARCH_5: + return "mips5"; + case EF_MIPS_ARCH_32: + return "mips32"; + case EF_MIPS_ARCH_64: + return "mips64"; + case EF_MIPS_ARCH_32R2: + return "mips32r2"; + case EF_MIPS_ARCH_64R2: + return "mips64r2"; + case EF_MIPS_ARCH_32R6: + return "mips32r6"; + case EF_MIPS_ARCH_64R6: + return "mips64r6"; + default: + return "unknown arch"; + } +} + +static std::string getFullArchName(uint32_t flags) { + StringRef arch = getArchName(flags); + StringRef mach = getMachName(flags); + if (mach.empty()) + return arch.str(); + return (arch + " (" + mach + ")").str(); +} + +// There are (arguably too) many MIPS ISAs out there. Their relationships +// can be represented as a forest. If all input files have ISAs which +// reachable by repeated proceeding from the single child to the parent, +// these input files are compatible. In that case we need to return "highest" +// ISA. If there are incompatible input files, we show an error. +// For example, mips1 is a "parent" of mips2 and such files are compatible. +// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32 +// are incompatible because nor mips3 is a parent for misp32, nor mips32 +// is a parent for mips3. +static uint32_t getArchFlags(ArrayRef files) { + uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + + for (const FileFlags &f : files.slice(1)) { + uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + + // Check ISA compatibility. + if (isArchMatched(New, ret)) + continue; + if (!isArchMatched(ret, New)) { + error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + + getFullArchName(New)); + return 0; + } + ret = New; + } + return ret; +} + +template uint32_t elf::calcMipsEFlags() { + std::vector v; + for (InputFile *f : objectFiles) + v.push_back({f, cast>(f)->getObj().getHeader()->e_flags}); + if (v.empty()) + return 0; + checkFlags(v); + return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); +} + +static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) { + if (fpA == fpB) + return 0; + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) + return 1; + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A && + fpA == Mips::Val_GNU_MIPS_ABI_FP_64) + return 1; + if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX) + return -1; + if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64 || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64A) + return 1; + return -1; +} + +static StringRef getMipsFpAbiName(uint8_t fpAbi) { + switch (fpAbi) { + case Mips::Val_GNU_MIPS_ABI_FP_ANY: + return "any"; + case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: + return "-mdouble-float"; + case Mips::Val_GNU_MIPS_ABI_FP_SINGLE: + return "-msingle-float"; + case Mips::Val_GNU_MIPS_ABI_FP_SOFT: + return "-msoft-float"; + case Mips::Val_GNU_MIPS_ABI_FP_OLD_64: + return "-mgp32 -mfp64 (old)"; + case Mips::Val_GNU_MIPS_ABI_FP_XX: + return "-mfpxx"; + case Mips::Val_GNU_MIPS_ABI_FP_64: + return "-mgp32 -mfp64"; + case Mips::Val_GNU_MIPS_ABI_FP_64A: + return "-mgp32 -mfp64 -mno-odd-spreg"; + default: + return "unknown"; + } +} + +uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, + StringRef fileName) { + if (compareMipsFpAbi(newFlag, oldFlag) >= 0) + return newFlag; + if (compareMipsFpAbi(oldFlag, newFlag) < 0) + error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) + + "' is incompatible with target floating point ABI '" + + getMipsFpAbiName(oldFlag) + "'"); + return oldFlag; +} + +template static bool isN32Abi(const InputFile *f) { + if (auto *ef = dyn_cast(f)) + return ef->template getObj().getHeader()->e_flags & EF_MIPS_ABI2; + return false; +} + +bool elf::isMipsN32Abi(const InputFile *f) { + switch (config->ekind) { + case ELF32LEKind: + return isN32Abi(f); + case ELF32BEKind: + return isN32Abi(f); + case ELF64LEKind: + return isN32Abi(f); + case ELF64BEKind: + return isN32Abi(f); + default: + llvm_unreachable("unknown Config->EKind"); + } +} + +bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } + +bool elf::isMipsR6() { + uint32_t arch = config->eflags & EF_MIPS_ARCH; + return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; +} + +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/MipsArchTree.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/RISCV.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/RISCV.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/RISCV.cpp (revision 352529) @@ -0,0 +1,442 @@ +//===- RISCV.cpp ----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "SyntheticSections.h" +#include "Target.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 RISCV final : public TargetInfo { +public: + RISCV(); + uint32_t calcEFlags() const override; + void writeGotHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + RelType getDynRel(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; +}; + +} // end anonymous namespace + +const uint64_t dtpOffset = 0x800; + +enum Op { + ADDI = 0x13, + AUIPC = 0x17, + JALR = 0x67, + LD = 0x3003, + LW = 0x2003, + SRLI = 0x5013, + SUB = 0x40000033, +}; + +enum Reg { + X_RA = 1, + X_T0 = 5, + X_T1 = 6, + X_T2 = 7, + X_T3 = 28, +}; + +static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } +static uint32_t lo12(uint32_t val) { return val & 4095; } + +static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) { + return op | (rd << 7) | (rs1 << 15) | (imm << 20); +} +static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) { + return op | (rd << 7) | (rs1 << 15) | (rs2 << 20); +} +static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) { + return op | (rd << 7) | (imm << 12); +} + +RISCV::RISCV() { + copyRel = R_RISCV_COPY; + noneRel = R_RISCV_NONE; + pltRel = R_RISCV_JUMP_SLOT; + relativeRel = R_RISCV_RELATIVE; + if (config->is64) { + symbolicRel = R_RISCV_64; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64; + tlsOffsetRel = R_RISCV_TLS_DTPREL64; + tlsGotRel = R_RISCV_TLS_TPREL64; + } else { + symbolicRel = R_RISCV_32; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32; + tlsOffsetRel = R_RISCV_TLS_DTPREL32; + tlsGotRel = R_RISCV_TLS_TPREL32; + } + gotRel = symbolicRel; + + // .got[0] = _DYNAMIC + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + + // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map + gotPltHeaderEntriesNum = 2; + + pltEntrySize = 16; + pltHeaderSize = 32; +} + +static uint32_t getEFlags(InputFile *f) { + if (config->is64) + return cast>(f)->getObj().getHeader()->e_flags; + return cast>(f)->getObj().getHeader()->e_flags; +} + +uint32_t RISCV::calcEFlags() const { + assert(!objectFiles.empty()); + + uint32_t target = getEFlags(objectFiles.front()); + + for (InputFile *f : objectFiles) { + uint32_t eflags = getEFlags(f); + if (eflags & EF_RISCV_RVC) + target |= EF_RISCV_RVC; + + if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI)) + error(toString(f) + + ": cannot link object files with different floating-point ABI"); + + if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE)) + error(toString(f) + + ": cannot link object files with different EF_RISCV_RVE"); + } + + return target; +} + +void RISCV::writeGotHeader(uint8_t *buf) const { + if (config->is64) + write64le(buf, mainPart->dynamic->getVA()); + else + write32le(buf, mainPart->dynamic->getVA()); +} + +void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const { + if (config->is64) + write64le(buf, in.plt->getVA()); + else + write32le(buf, in.plt->getVA()); +} + +void RISCV::writePltHeader(uint8_t *buf) const { + // 1: auipc t2, %pcrel_hi(.got.plt) + // sub t1, t1, t3 + // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve + // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0] + // addi t0, t2, %pcrel_lo(1b) + // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0] + // l[wd] t0, Wordsize(t0); t0 = link_map + // jr t3 + uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); + uint32_t load = config->is64 ? LD : LW; + write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset))); + write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3)); + write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset))); + write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12)); + write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset))); + write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2)); + write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize)); + write32le(buf + 28, itype(JALR, 0, X_T3, 0)); +} + +void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // 1: auipc t3, %pcrel_hi(f@.got.plt) + // l[wd] t3, %pcrel_lo(1b)(t3) + // jalr t1, t3 + // nop + uint32_t offset = gotPltEntryAddr - pltEntryAddr; + write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); + write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset))); + write32le(buf + 8, itype(JALR, X_T1, X_T3, 0)); + write32le(buf + 12, itype(ADDI, 0, 0, 0)); +} + +RelType RISCV::getDynRel(RelType type) const { + return type == target->symbolicRel ? type + : static_cast(R_RISCV_NONE); +} + +RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_RISCV_ADD8: + case R_RISCV_ADD16: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SET32: + case R_RISCV_SUB6: + case R_RISCV_SUB8: + case R_RISCV_SUB16: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + return R_RISCV_ADD; + case R_RISCV_JAL: + case R_RISCV_BRANCH: + case R_RISCV_PCREL_HI20: + case R_RISCV_RVC_BRANCH: + case R_RISCV_RVC_JUMP: + case R_RISCV_32_PCREL: + return R_PC; + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + return R_PLT_PC; + case R_RISCV_GOT_HI20: + return R_GOT_PC; + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + return R_RISCV_PC_INDIRECT; + case R_RISCV_TLS_GD_HI20: + return R_TLSGD_PC; + case R_RISCV_TLS_GOT_HI20: + config->hasStaticTlsModel = true; + return R_GOT_PC; + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_LO12_S: + return R_TLS; + case R_RISCV_RELAX: + case R_RISCV_ALIGN: + case R_RISCV_TPREL_ADD: + return R_HINT; + default: + return R_ABS; + } +} + +// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. +static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { + return (v & ((1ULL << (begin + 1)) - 1)) >> end; +} + +void RISCV::relocateOne(uint8_t *loc, const RelType type, + const uint64_t val) const { + const unsigned bits = config->wordsize * 8; + + switch (type) { + case R_RISCV_32: + write32le(loc, val); + return; + case R_RISCV_64: + write64le(loc, val); + return; + + case R_RISCV_RVC_BRANCH: { + checkInt(loc, static_cast(val) >> 1, 8, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE383; + uint16_t imm8 = extractBits(val, 8, 8) << 12; + uint16_t imm4_3 = extractBits(val, 4, 3) << 10; + uint16_t imm7_6 = extractBits(val, 7, 6) << 5; + uint16_t imm2_1 = extractBits(val, 2, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5; + + write16le(loc, insn); + return; + } + + case R_RISCV_RVC_JUMP: { + checkInt(loc, static_cast(val) >> 1, 11, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE003; + uint16_t imm11 = extractBits(val, 11, 11) << 12; + uint16_t imm4 = extractBits(val, 4, 4) << 11; + uint16_t imm9_8 = extractBits(val, 9, 8) << 9; + uint16_t imm10 = extractBits(val, 10, 10) << 8; + uint16_t imm6 = extractBits(val, 6, 6) << 7; + uint16_t imm7 = extractBits(val, 7, 7) << 6; + uint16_t imm3_1 = extractBits(val, 3, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5; + + write16le(loc, insn); + return; + } + + case R_RISCV_RVC_LUI: { + int64_t imm = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, imm, 6, type); + if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` + write16le(loc, (read16le(loc) & 0x0F83) | 0x4000); + } else { + uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12; + uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2; + write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12); + } + return; + } + + case R_RISCV_JAL: { + checkInt(loc, static_cast(val) >> 1, 20, type); + checkAlignment(loc, val, 2, type); + + uint32_t insn = read32le(loc) & 0xFFF; + uint32_t imm20 = extractBits(val, 20, 20) << 31; + uint32_t imm10_1 = extractBits(val, 10, 1) << 21; + uint32_t imm11 = extractBits(val, 11, 11) << 20; + uint32_t imm19_12 = extractBits(val, 19, 12) << 12; + insn |= imm20 | imm10_1 | imm11 | imm19_12; + + write32le(loc, insn); + return; + } + + case R_RISCV_BRANCH: { + checkInt(loc, static_cast(val) >> 1, 12, type); + checkAlignment(loc, val, 2, type); + + uint32_t insn = read32le(loc) & 0x1FFF07F; + uint32_t imm12 = extractBits(val, 12, 12) << 31; + uint32_t imm10_5 = extractBits(val, 10, 5) << 25; + uint32_t imm4_1 = extractBits(val, 4, 1) << 8; + uint32_t imm11 = extractBits(val, 11, 11) << 7; + insn |= imm12 | imm10_5 | imm4_1 | imm11; + + write32le(loc, insn); + return; + } + + // auipc + jalr pair + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: { + int64_t hi = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, hi, 20, type); + if (isInt<20>(hi)) { + relocateOne(loc, R_RISCV_PCREL_HI20, val); + relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val); + } + return; + } + + case R_RISCV_GOT_HI20: + case R_RISCV_PCREL_HI20: + case R_RISCV_TLS_GD_HI20: + case R_RISCV_TLS_GOT_HI20: + case R_RISCV_TPREL_HI20: + case R_RISCV_HI20: { + uint64_t hi = val + 0x800; + checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type); + write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000)); + return; + } + + case R_RISCV_PCREL_LO12_I: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_LO12_I: { + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20)); + return; + } + + case R_RISCV_PCREL_LO12_S: + case R_RISCV_TPREL_LO12_S: + case R_RISCV_LO12_S: { + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + uint32_t imm11_5 = extractBits(lo, 11, 5) << 25; + uint32_t imm4_0 = extractBits(lo, 4, 0) << 7; + write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0); + return; + } + + case R_RISCV_ADD8: + *loc += val; + return; + case R_RISCV_ADD16: + write16le(loc, read16le(loc) + val); + return; + case R_RISCV_ADD32: + write32le(loc, read32le(loc) + val); + return; + case R_RISCV_ADD64: + write64le(loc, read64le(loc) + val); + return; + case R_RISCV_SUB6: + *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f); + return; + case R_RISCV_SUB8: + *loc -= val; + return; + case R_RISCV_SUB16: + write16le(loc, read16le(loc) - val); + return; + case R_RISCV_SUB32: + write32le(loc, read32le(loc) - val); + return; + case R_RISCV_SUB64: + write64le(loc, read64le(loc) - val); + return; + case R_RISCV_SET6: + *loc = (*loc & 0xc0) | (val & 0x3f); + return; + case R_RISCV_SET8: + *loc = val; + return; + case R_RISCV_SET16: + write16le(loc, val); + return; + case R_RISCV_SET32: + case R_RISCV_32_PCREL: + write32le(loc, val); + return; + + case R_RISCV_TLS_DTPREL32: + write32le(loc, val - dtpOffset); + break; + case R_RISCV_TLS_DTPREL64: + write64le(loc, val - dtpOffset); + break; + + case R_RISCV_ALIGN: + case R_RISCV_RELAX: + return; // Ignored (for now) + case R_RISCV_NONE: + return; // Do nothing + + // These are handled by the dynamic linker + case R_RISCV_RELATIVE: + case R_RISCV_COPY: + case R_RISCV_JUMP_SLOT: + // GP-relative relocations are only produced after relaxation, which + // we don't support for now + case R_RISCV_GPREL_I: + case R_RISCV_GPREL_S: + default: + error(getErrorLocation(loc) + + "unimplemented relocation: " + toString(type)); + return; + } +} + +TargetInfo *elf::getRISCVTargetInfo() { + static RISCV target; + return ⌖ +} Index: vendor/lld/lld-release_900-r372316/ELF/Arch/SPARCV9.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/SPARCV9.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/SPARCV9.cpp (revision 352529) @@ -0,0 +1,149 @@ +//===- SPARCV9.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class SPARCV9 final : public TargetInfo { +public: + SPARCV9(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +SPARCV9::SPARCV9() { + copyRel = R_SPARC_COPY; + gotRel = R_SPARC_GLOB_DAT; + noneRel = R_SPARC_NONE; + pltRel = R_SPARC_JMP_SLOT; + relativeRel = R_SPARC_RELATIVE; + symbolicRel = R_SPARC_64; + pltEntrySize = 32; + pltHeaderSize = 4 * pltEntrySize; + + defaultCommonPageSize = 8192; + defaultMaxPageSize = 0x100000; + defaultImageBase = 0x100000; +} + +RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_SPARC_32: + case R_SPARC_UA32: + case R_SPARC_64: + case R_SPARC_UA64: + return R_ABS; + case R_SPARC_PC10: + case R_SPARC_PC22: + case R_SPARC_DISP32: + case R_SPARC_WDISP30: + return R_PC; + case R_SPARC_GOT10: + return R_GOT_OFF; + case R_SPARC_GOT22: + return R_GOT_OFF; + case R_SPARC_WPLT30: + return R_PLT_PC; + case R_SPARC_NONE: + return R_NONE; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_SPARC_32: + case R_SPARC_UA32: + // V-word32 + checkUInt(loc, val, 32, type); + write32be(loc, val); + break; + case R_SPARC_DISP32: + // V-disp32 + checkInt(loc, val, 32, type); + write32be(loc, val); + break; + case R_SPARC_WDISP30: + case R_SPARC_WPLT30: + // V-disp30 + checkInt(loc, val, 32, type); + write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); + break; + case R_SPARC_22: + // V-imm22 + checkUInt(loc, val, 22, type); + write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); + break; + case R_SPARC_GOT22: + case R_SPARC_PC22: + // T-imm22 + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); + break; + case R_SPARC_WDISP19: + // V-disp19 + checkInt(loc, val, 21, type); + write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); + break; + case R_SPARC_GOT10: + case R_SPARC_PC10: + // T-simm10 + write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); + break; + case R_SPARC_64: + case R_SPARC_UA64: + // V-xword64 + write64be(loc, val); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t pltData[] = { + 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 + 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1 + 0x01, 0x00, 0x00, 0x00, // nop + 0x01, 0x00, 0x00, 0x00, // nop + 0x01, 0x00, 0x00, 0x00, // nop + 0x01, 0x00, 0x00, 0x00, // nop + 0x01, 0x00, 0x00, 0x00, // nop + 0x01, 0x00, 0x00, 0x00 // nop + }; + memcpy(buf, pltData, sizeof(pltData)); + + uint64_t off = pltHeaderSize + pltEntrySize * index; + relocateOne(buf, R_SPARC_22, off); + relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); +} + +TargetInfo *elf::getSPARCV9TargetInfo() { + static SPARCV9 target; + return ⌖ +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/SPARCV9.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/X86.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/X86.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/X86.cpp (revision 352529) @@ -0,0 +1,554 @@ +//===- X86.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class X86 : public TargetInfo { +public: + X86(); + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + RelType getDynRel(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, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; +}; +} // namespace + +X86::X86() { + copyRel = R_386_COPY; + gotRel = R_386_GLOB_DAT; + noneRel = R_386_NONE; + pltRel = R_386_JUMP_SLOT; + iRelativeRel = R_386_IRELATIVE; + relativeRel = R_386_RELATIVE; + symbolicRel = R_386_32; + tlsGotRel = R_386_TLS_TPOFF; + tlsModuleIndexRel = R_386_TLS_DTPMOD32; + tlsOffsetRel = R_386_TLS_DTPOFF32; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + + // Align to the non-PAE large page size (known as a superpage or huge page). + // FreeBSD automatically promotes large, superpage-aligned allocations. + defaultImageBase = 0x400000; +} + +int X86::getTlsGdRelaxSkip(RelType type) const { + return 2; +} + +RelExpr X86::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + // There are 4 different TLS variable models with varying degrees of + // flexibility and performance. LocalExec and InitialExec models are fast but + // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the + // dynamic section to let runtime know about that. + if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE || + type == R_386_TLS_GOTIE) + config->hasStaticTlsModel = true; + + switch (type) { + case R_386_8: + case R_386_16: + case R_386_32: + return R_ABS; + case R_386_TLS_LDO_32: + return R_DTPREL; + case R_386_TLS_GD: + return R_TLSGD_GOTPLT; + case R_386_TLS_LDM: + return R_TLSLD_GOTPLT; + case R_386_PLT32: + return R_PLT_PC; + case R_386_PC8: + case R_386_PC16: + case R_386_PC32: + return R_PC; + case R_386_GOTPC: + return R_GOTPLTONLY_PC; + case R_386_TLS_IE: + return R_GOT; + case R_386_GOT32: + case R_386_GOT32X: + // These relocations are arguably mis-designed because their calculations + // depend on the instructions they are applied to. This is bad because we + // usually don't care about whether the target section contains valid + // machine instructions or not. But this is part of the documented ABI, so + // we had to implement as the standard requires. + // + // x86 does not support PC-relative data access. Therefore, in order to + // access GOT contents, a GOT address needs to be known at link-time + // (which means non-PIC) or compilers have to emit code to get a GOT + // address at runtime (which means code is position-independent but + // compilers need to emit extra code for each GOT access.) This decision + // is made at compile-time. In the latter case, compilers emit code to + // load an GOT address to a register, which is usually %ebx. + // + // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or + // foo@GOT(%ebx). + // + // foo@GOT is not usable in PIC. If we are creating a PIC output and if we + // find such relocation, we should report an error. foo@GOT is resolved to + // an *absolute* address of foo's GOT entry, because both GOT address and + // foo's offset are known. In other words, it's G + A. + // + // foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to + // foo's GOT entry in the table, because GOT address is not known but foo's + // offset in the table is known. It's G + A - GOT. + // + // It's unfortunate that compilers emit the same relocation for these + // different use cases. In order to distinguish them, we have to read a + // machine instruction. + // + // The following code implements it. We assume that Loc[0] is the first byte + // of a displacement or an immediate field of a valid machine + // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at + // the byte, we can determine whether the instruction uses the operand as an + // absolute address (R_GOT) or a register-relative address (R_GOTPLT). + return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT; + case R_386_TLS_GOTIE: + return R_GOTPLT; + case R_386_GOTOFF: + return R_GOTPLTREL; + case R_386_TLS_LE: + return R_TLS; + case R_386_TLS_LE_32: + return R_NEG_TLS; + case R_386_NONE: + return R_NONE; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + switch (expr) { + default: + return expr; + case R_RELAX_TLS_GD_TO_IE: + return R_RELAX_TLS_GD_TO_IE_GOTPLT; + case R_RELAX_TLS_GD_TO_LE: + return R_RELAX_TLS_GD_TO_LE_NEG; + } +} + +void X86::writeGotPltHeader(uint8_t *buf) const { + write32le(buf, mainPart->dynamic->getVA()); +} + +void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const { + // Entries in .got.plt initially points back to the corresponding + // PLT entries with a fixed offset to skip the first instruction. + write32le(buf, s.getPltVA() + 6); +} + +void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + // An x86 entry is the address of the ifunc resolver function. + write32le(buf, s.getVA()); +} + +RelType X86::getDynRel(RelType type) const { + if (type == R_386_TLS_LE) + return R_386_TLS_TPOFF; + if (type == R_386_TLS_LE_32) + return R_386_TLS_TPOFF32; + return type; +} + +void X86::writePltHeader(uint8_t *buf) const { + if (config->isPic) { + const uint8_t v[] = { + 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx) + 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx) + 0x90, 0x90, 0x90, 0x90 // nop + }; + memcpy(buf, v, sizeof(v)); + return; + } + + const uint8_t pltData[] = { + 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4) + 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8) + 0x90, 0x90, 0x90, 0x90, // nop + }; + memcpy(buf, pltData, sizeof(pltData)); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); +} + +void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + if (config->isPic) { + const uint8_t inst[] = { + 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx) + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA()); + } else { + const uint8_t inst[] = { + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr); + } + + write32le(buf + 7, relOff); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); +} + +int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_386_8: + case R_386_PC8: + return SignExtend64<8>(*buf); + case R_386_16: + case R_386_PC16: + return SignExtend64<16>(read16le(buf)); + case R_386_32: + case R_386_GOT32: + case R_386_GOT32X: + case R_386_GOTOFF: + case R_386_GOTPC: + case R_386_PC32: + case R_386_PLT32: + case R_386_TLS_LDO_32: + case R_386_TLS_LE: + return SignExtend64<32>(read32le(buf)); + default: + return 0; + } +} + +void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_386_8: + // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are + // being used for some 16-bit programs such as boot loaders, so + // we want to support them. + checkIntUInt(loc, val, 8, type); + *loc = val; + break; + case R_386_PC8: + checkInt(loc, val, 8, type); + *loc = val; + break; + case R_386_16: + checkIntUInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_386_PC16: + // R_386_PC16 is normally used with 16 bit code. In that situation + // the PC is 16 bits, just like the addend. This means that it can + // point from any 16 bit address to any other if the possibility + // of wrapping is included. + // The only restriction we have to check then is that the destination + // address fits in 16 bits. That is impossible to do here. The problem is + // that we are passed the final value, which already had the + // current location subtracted from it. + // We just check that Val fits in 17 bits. This misses some cases, but + // should have no false positives. + checkInt(loc, val, 17, type); + write16le(loc, val); + break; + case R_386_32: + case R_386_GOT32: + case R_386_GOT32X: + case R_386_GOTOFF: + case R_386_GOTPC: + case R_386_PC32: + case R_386_PLT32: + case R_386_RELATIVE: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_GD: + case R_386_TLS_GOTIE: + case R_386_TLS_IE: + case R_386_TLS_LDM: + case R_386_TLS_LDO_32: + case R_386_TLS_LE: + case R_386_TLS_LE_32: + case R_386_TLS_TPOFF: + case R_386_TLS_TPOFF32: + checkInt(loc, val, 32, type); + write32le(loc, val); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + // Convert + // leal x@tlsgd(, %ebx, 1), + // call __tls_get_addr@plt + // to + // movl %gs:0,%eax + // subl $x@ntpoff,%eax + const uint8_t inst[] = { + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax + 0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax + }; + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); +} + +void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + // Convert + // leal x@tlsgd(, %ebx, 1), + // call __tls_get_addr@plt + // to + // movl %gs:0, %eax + // addl x@gotntpoff(%ebx), %eax + const uint8_t inst[] = { + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax + 0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax + }; + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); +} + +// In some conditions, relocations can be optimized to avoid using GOT. +// This function does that for Initial Exec to Local Exec case. +void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + // Ulrich's document section 6.2 says that @gotntpoff can + // be used with MOVL or ADDL instructions. + // @indntpoff is similar to @gotntpoff, but for use in + // position dependent code. + uint8_t reg = (loc[-1] >> 3) & 7; + + if (type == R_386_TLS_IE) { + if (loc[-1] == 0xa1) { + // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" + // This case is different from the generic case below because + // this is a 5 byte instruction while below is 6 bytes. + loc[-1] = 0xb8; + } else if (loc[-2] == 0x8b) { + // "movl foo@indntpoff,%reg" -> "movl $foo,%reg" + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; + } else { + // "addl foo@indntpoff,%reg" -> "addl $foo,%reg" + loc[-2] = 0x81; + loc[-1] = 0xc0 | reg; + } + } else { + assert(type == R_386_TLS_GOTIE); + if (loc[-2] == 0x8b) { + // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; + } else { + // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg" + loc[-2] = 0x8d; + loc[-1] = 0x80 | (reg << 3) | reg; + } + } + write32le(loc, val); +} + +void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_386_TLS_LDO_32) { + write32le(loc, val); + return; + } + + // Convert + // leal foo(%reg),%eax + // call ___tls_get_addr + // to + // movl %gs:0,%eax + // nop + // leal 0(%esi,1),%esi + const uint8_t inst[] = { + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax + 0x90, // nop + 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi + }; + memcpy(loc - 2, inst, sizeof(inst)); +} + +namespace { +class RetpolinePic : public X86 { +public: + RetpolinePic(); + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; +}; + +class RetpolineNoPic : public X86 { +public: + RetpolineNoPic(); + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; +}; +} // namespace + +RetpolinePic::RetpolinePic() { + pltHeaderSize = 48; + pltEntrySize = 32; +} + +void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 17); +} + +void RetpolinePic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx) + 0x50, // 6: pushl %eax + 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax + 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next + 0xf3, 0x90, // 12: loop: pause + 0x0f, 0xae, 0xe8, // 14: lfence + 0xeb, 0xf9, // 17: jmp loop + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 + 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp) + 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx + 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp) + 0x89, 0xc8, // 2b: mov %ecx, %eax + 0x59, // 2d: pop %ecx + 0xc3, // 2e: ret + 0xcc, // 2f: int3; padding + }; + memcpy(buf, insn, sizeof(insn)); +} + +void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { + 0x50, // pushl %eax + 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax + 0xe8, 0, 0, 0, 0, // call plt+0x20 + 0xe9, 0, 0, 0, 0, // jmp plt+0x12 + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding + }; + memcpy(buf, insn, sizeof(insn)); + + uint32_t ebx = in.gotPlt->getVA(); + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 3, gotPltEntryAddr - ebx); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, relOff); + write32le(buf + 23, -off - 27); +} + +RetpolineNoPic::RetpolineNoPic() { + pltHeaderSize = 48; + pltEntrySize = 32; +} + +void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 16); +} + +void RetpolineNoPic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4 + 0x50, // 6: pushl %eax + 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax + 0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next + 0xf3, 0x90, // 11: loop: pause + 0x0f, 0xae, 0xe8, // 13: lfence + 0xeb, 0xf9, // 16: jmp loop + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3 + 0xcc, 0xcc, 0xcc, // 1f: int3; .align 16 + 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp) + 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx + 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp) + 0x89, 0xc8, // 2b: mov %ecx, %eax + 0x59, // 2d: pop %ecx + 0xc3, // 2e: ret + 0xcc, // 2f: int3; padding + }; + memcpy(buf, insn, sizeof(insn)); + + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); +} + +void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { + 0x50, // 0: pushl %eax + 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax + 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 + 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11 + 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // 15: jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding + 0xcc, // 1f: int3; padding + }; + memcpy(buf, insn, sizeof(insn)); + + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 2, gotPltEntryAddr); + write32le(buf + 7, -off - 11 + 32); + write32le(buf + 12, -off - 16 + 17); + write32le(buf + 17, relOff); + write32le(buf + 22, -off - 26); +} + +TargetInfo *elf::getX86TargetInfo() { + if (config->zRetpolineplt) { + if (config->isPic) { + static RetpolinePic t; + return &t; + } + static RetpolineNoPic t; + return &t; + } + + static X86 t; + return &t; +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/X86.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Arch/X86_64.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Arch/X86_64.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Arch/X86_64.cpp (revision 352529) @@ -0,0 +1,701 @@ +//===- X86_64.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.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 X86_64 : public TargetInfo { +public: + X86_64(); + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; +}; +} // namespace + +X86_64::X86_64() { + copyRel = R_X86_64_COPY; + gotRel = R_X86_64_GLOB_DAT; + noneRel = R_X86_64_NONE; + pltRel = R_X86_64_JUMP_SLOT; + relativeRel = R_X86_64_RELATIVE; + iRelativeRel = R_X86_64_IRELATIVE; + symbolicRel = R_X86_64_64; + tlsDescRel = R_X86_64_TLSDESC; + tlsGotRel = R_X86_64_TPOFF64; + tlsModuleIndexRel = R_X86_64_DTPMOD64; + tlsOffsetRel = R_X86_64_DTPOFF64; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + + // Align to the large page size (known as a superpage or huge page). + // FreeBSD automatically promotes large, superpage-aligned allocations. + defaultImageBase = 0x200000; +} + +int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } + +RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + if (type == R_X86_64_GOTTPOFF) + config->hasStaticTlsModel = true; + + switch (type) { + case R_X86_64_8: + case R_X86_64_16: + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_64: + return R_ABS; + case R_X86_64_DTPOFF32: + case R_X86_64_DTPOFF64: + return R_DTPREL; + case R_X86_64_TPOFF32: + return R_TLS; + case R_X86_64_TLSDESC_CALL: + return R_TLSDESC_CALL; + case R_X86_64_TLSLD: + return R_TLSLD_PC; + case R_X86_64_TLSGD: + return R_TLSGD_PC; + case R_X86_64_SIZE32: + case R_X86_64_SIZE64: + return R_SIZE; + case R_X86_64_PLT32: + return R_PLT_PC; + case R_X86_64_PC8: + case R_X86_64_PC16: + case R_X86_64_PC32: + case R_X86_64_PC64: + return R_PC; + case R_X86_64_GOT32: + case R_X86_64_GOT64: + return R_GOTPLT; + case R_X86_64_GOTPC32_TLSDESC: + return R_TLSDESC_PC; + case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + case R_X86_64_GOTTPOFF: + return R_GOT_PC; + case R_X86_64_GOTOFF64: + return R_GOTPLTREL; + case R_X86_64_GOTPC32: + case R_X86_64_GOTPC64: + return R_GOTPLTONLY_PC; + case R_X86_64_NONE: + return R_NONE; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +void X86_64::writeGotPltHeader(uint8_t *buf) const { + // The first entry holds the value of _DYNAMIC. It is not clear why that is + // required, but it is documented in the psabi and the glibc dynamic linker + // seems to use it (note that this is relevant for linking ld.so, not any + // other program). + write64le(buf, mainPart->dynamic->getVA()); +} + +void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { + // See comments in X86::writeGotPlt. + write64le(buf, s.getPltVA() + 6); +} + +void X86_64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { + 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) + 0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip) + 0x0f, 0x1f, 0x40, 0x00, // nop + }; + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8 + write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16 +} + +void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { + 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) + 0x68, 0, 0, 0, 0, // pushq + 0xe9, 0, 0, 0, 0, // jmpq plt[0] + }; + memcpy(buf, inst, sizeof(inst)); + + write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6); + write32le(buf + 7, index); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); +} + +RelType X86_64::getDynRel(RelType type) const { + if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 || + type == R_X86_64_SIZE64) + return type; + return R_X86_64_NONE; +} + +void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); + + // The original code used a pc relative relocation and so we have to + // compensate for the -4 in had in the addend. + write32le(loc + 8, val + 4); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq $x@tpoff(%rip),%rax + loc[-2] = 0xc7; + loc[-1] = 0xc0; + write32le(loc, val + 4); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } +} + +void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); + + // Both code sequences are PC relatives, but since we are moving the + // constant forward by 8 bytes we have to subtract the value by 8. + write32le(loc + 8, val - 8); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq x@gottpoff(%rip),%rax + loc[-2] = 0x8b; + write32le(loc, val); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } +} + +// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to +// R_X86_64_TPOFF32 so that it does not use GOT. +void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + uint8_t *inst = loc - 3; + uint8_t reg = loc[-1] >> 3; + uint8_t *regSlot = loc - 1; + + // Note that ADD with RSP or R12 is converted to ADD instead of LEA + // because LEA with these registers needs 4 bytes to encode and thus + // wouldn't fit the space. + + if (memcmp(inst, "\x48\x03\x25", 3) == 0) { + // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp" + memcpy(inst, "\x48\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) { + // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12" + memcpy(inst, "\x49\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03", 2) == 0) { + // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]" + memcpy(inst, "\x4d\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x48\x03", 2) == 0) { + // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg" + memcpy(inst, "\x48\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x4c\x8b", 2) == 0) { + // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]" + memcpy(inst, "\x49\xc7", 2); + *regSlot = 0xc0 | reg; + } else if (memcmp(inst, "\x48\x8b", 2) == 0) { + // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg" + memcpy(inst, "\x48\xc7", 2); + *regSlot = 0xc0 | reg; + } else { + error(getErrorLocation(loc - 3) + + "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only"); + } + + // The original code used a PC relative relocation. + // Need to compensate for the -4 it had in the addend. + write32le(loc, val + 4); +} + +void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_DTPOFF64) { + write64le(loc, val); + return; + } + if (type == R_X86_64_DTPOFF32) { + write32le(loc, val); + return; + } + + const uint8_t inst[] = { + 0x66, 0x66, // .word 0x6666 + 0x66, // .byte 0x66 + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax + }; + + if (loc[4] == 0xe8) { + // Convert + // leaq bar@tlsld(%rip), %rdi # 48 8d 3d + // callq __tls_get_addr@PLT # e8 + // leaq bar@dtpoff(%rax), %rcx + // to + // .word 0x6666 + // .byte 0x66 + // mov %fs:0,%rax + // leaq bar@tpoff(%rax), %rcx + memcpy(loc - 3, inst, sizeof(inst)); + return; + } + + if (loc[4] == 0xff && loc[5] == 0x15) { + // Convert + // leaq x@tlsld(%rip),%rdi # 48 8d 3d + // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 + // to + // .long 0x66666666 + // movq %fs:0,%rax + // See "Table 11.9: LD -> LE Code Transition (LP64)" in + // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf + loc[-3] = 0x66; + memcpy(loc - 2, inst, sizeof(inst)); + return; + } + + error(getErrorLocation(loc - 3) + + "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); +} + +void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_X86_64_8: + checkIntUInt(loc, val, 8, type); + *loc = val; + break; + case R_X86_64_PC8: + checkInt(loc, val, 8, type); + *loc = val; + break; + case R_X86_64_16: + checkIntUInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_X86_64_PC16: + checkInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_X86_64_32: + checkUInt(loc, val, 32, type); + write32le(loc, val); + break; + case R_X86_64_32S: + case R_X86_64_TPOFF32: + case R_X86_64_GOT32: + case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: + case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + case R_X86_64_PC32: + case R_X86_64_GOTTPOFF: + case R_X86_64_PLT32: + case R_X86_64_TLSGD: + case R_X86_64_TLSLD: + case R_X86_64_DTPOFF32: + case R_X86_64_SIZE32: + checkInt(loc, val, 32, type); + write32le(loc, val); + break; + case R_X86_64_64: + case R_X86_64_DTPOFF64: + case R_X86_64_PC64: + case R_X86_64_SIZE64: + case R_X86_64_GOT64: + case R_X86_64_GOTOFF64: + case R_X86_64_GOTPC64: + write64le(loc, val); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr relExpr) const { + if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) + return relExpr; + const uint8_t op = data[-2]; + const uint8_t modRm = data[-1]; + + // FIXME: When PIC is disabled and foo is defined locally in the + // lower 32 bit address space, memory operand in mov can be converted into + // immediate operand. Otherwise, mov must be changed to lea. We support only + // latter relaxation at this moment. + if (op == 0x8b) + return R_RELAX_GOT_PC; + + // Relax call and jmp. + if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) + return R_RELAX_GOT_PC; + + // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. + // If PIC then no relaxation is available. + // We also don't relax test/binop instructions without REX byte, + // they are 32bit operations and not common to have. + assert(type == R_X86_64_REX_GOTPCRELX); + return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; +} + +// A subset of relaxations can only be applied for no-PIC. This method +// handles such relaxations. Instructions encoding information was taken from: +// "Intel 64 and IA-32 Architectures Software Developer's Manual V2" +// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ +// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) +static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, + uint8_t modRm) { + const uint8_t rex = loc[-3]; + // Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg". + if (op == 0x85) { + // See "TEST-Logical Compare" (4-428 Vol. 2B), + // TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension). + + // ModR/M byte has form XX YYY ZZZ, where + // YYY is MODRM.reg(register 2), ZZZ is MODRM.rm(register 1). + // XX has different meanings: + // 00: The operand's memory address is in reg1. + // 01: The operand's memory address is reg1 + a byte-sized displacement. + // 10: The operand's memory address is reg1 + a word-sized displacement. + // 11: The operand is reg1 itself. + // If an instruction requires only one operand, the unused reg2 field + // holds extra opcode bits rather than a register code + // 0xC0 == 11 000 000 binary. + // 0x38 == 00 111 000 binary. + // We transfer reg2 to reg1 here as operand. + // See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3). + loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte. + + // Change opcode from TEST r/m64, r64 to TEST r/m64, imm32 + // See "TEST-Logical Compare" (4-428 Vol. 2B). + loc[-2] = 0xf7; + + // Move R bit to the B bit in REX byte. + // REX byte is encoded as 0100WRXB, where + // 0100 is 4bit fixed pattern. + // REX.W When 1, a 64-bit operand size is used. Otherwise, when 0, the + // default operand size is used (which is 32-bit for most but not all + // instructions). + // REX.R This 1-bit value is an extension to the MODRM.reg field. + // REX.X This 1-bit value is an extension to the SIB.index field. + // REX.B This 1-bit value is an extension to the MODRM.rm field or the + // SIB.base field. + // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); + return; + } + + // If we are here then we need to relax the adc, add, and, cmp, or, sbb, sub + // or xor operations. + + // Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg". + // Logic is close to one for test instruction above, but we also + // write opcode extension here, see below for details. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte. + + // Primary opcode is 0x81, opcode extension is one of: + // 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB, + // 100b is AND, 101b is SUB, 110b is XOR, 111b is CMP. + // This value was wrote to MODRM.reg in a line above. + // See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15), + // "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for + // descriptions about each operation. + loc[-2] = 0x81; + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); +} + +void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + const uint8_t op = loc[-2]; + const uint8_t modRm = loc[-1]; + + // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". + if (op == 0x8b) { + loc[-2] = 0x8d; + write32le(loc, val); + return; + } + + if (op != 0xff) { + // We are relaxing a rip relative to an absolute, so compensate + // for the old -4 addend. + assert(!config->isPic); + relaxGotNoPic(loc, val + 4, op, modRm); + return; + } + + // Convert call/jmp instructions. + if (modRm == 0x15) { + // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo". + // Instead we convert to "addr32 call foo" where addr32 is an instruction + // prefix. That makes result expression to be a single instruction. + loc[-2] = 0x67; // addr32 prefix + loc[-1] = 0xe8; // call + write32le(loc, val); + return; + } + + // Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop". + // jmp doesn't return, so it is fine to use nop here, it is just a stub. + assert(modRm == 0x25); + loc[-2] = 0xe9; // jmp + loc[3] = 0x90; // nop + write32le(loc - 1, val + 1); +} + +// A split-stack prologue starts by checking the amount of stack remaining +// in one of two ways: +// A) Comparing of the stack pointer to a field in the tcb. +// B) Or a load of a stack pointer offset with an lea to r10 or r11. +bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { + if (!config->is64) { + error("Target doesn't support split stacks."); + return false; + } + + if (loc + 8 >= end) + return false; + + // Replace "cmp %fs:0x70,%rsp" and subsequent branch + // with "stc, nopl 0x0(%rax,%rax,1)" + if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) { + memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); + return true; + } + + // Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could + // be r10 or r11. The lea instruction feeds a subsequent compare which checks + // if there is X available stack space. Making X larger effectively reserves + // that much additional space. The stack grows downward so subtract the value. + if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 || + memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) { + // The offset bytes are encoded four bytes after the start of the + // instruction. + write32le(loc + 4, read32le(loc + 4) - 0x4000); + return true; + } + return false; +} + +// These nonstandard PLT entries are to migtigate Spectre v2 security +// vulnerability. In order to mitigate Spectre v2, we want to avoid indirect +// branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT +// entries, we use a CALL followed by MOV and RET to do the same thing as an +// indirect jump. That instruction sequence is so-called "retpoline". +// +// We have two types of retpoline PLTs as a size optimization. If `-z now` +// is specified, all dynamic symbols are resolved at load-time. Thus, when +// that option is given, we can omit code for symbol lazy resolution. +namespace { +class Retpoline : public X86_64 { +public: + Retpoline(); + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; +}; + +class RetpolineZNow : public X86_64 { +public: + RetpolineZNow(); + void writeGotPlt(uint8_t *buf, const Symbol &s) const override {} + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; +}; +} // namespace + +Retpoline::Retpoline() { + pltHeaderSize = 48; + pltEntrySize = 32; +} + +void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write64le(buf, s.getPltVA() + 17); +} + +void Retpoline::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip) + 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11 + 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next + 0xf3, 0x90, // 12: loop: pause + 0x0f, 0xae, 0xe8, // 14: lfence + 0xeb, 0xf9, // 17: jmp loop + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 + 0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp) + 0xc3, // 24: ret + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding + 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding + }; + memcpy(buf, insn, sizeof(insn)); + + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt - 6 + 8); + write32le(buf + 9, gotPlt - plt - 13 + 16); +} + +void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { + 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 + 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 + 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 + 0x68, 0, 0, 0, 0, // 11: pushq + 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding + }; + memcpy(buf, insn, sizeof(insn)); + + uint64_t off = pltHeaderSize + pltEntrySize * index; + + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, index); + write32le(buf + 23, -off - 27); +} + +RetpolineZNow::RetpolineZNow() { + pltHeaderSize = 32; + pltEntrySize = 16; +} + +void RetpolineZNow::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next + 0xf3, 0x90, // 5: loop: pause + 0x0f, 0xae, 0xe8, // 7: lfence + 0xeb, 0xf9, // a: jmp loop + 0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16 + 0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp) + 0xc3, // 14: ret + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding + 0xcc, // 1f: int3; padding + }; + memcpy(buf, insn, sizeof(insn)); +} + +void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { + 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 + 0xe9, 0, 0, 0, 0, // jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding + }; + memcpy(buf, insn, sizeof(insn)); + + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12); +} + +static TargetInfo *getTargetInfo() { + if (config->zRetpolineplt) { + if (config->zNow) { + static RetpolineZNow t; + return &t; + } + static Retpoline t; + return &t; + } + + static X86_64 t; + return &t; +} + +TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } Property changes on: vendor/lld/lld-release_900-r372316/ELF/Arch/X86_64.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/InputFiles.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/InputFiles.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/InputFiles.cpp (revision 352529) @@ -0,0 +1,1645 @@ +//===- InputFiles.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Driver.h" +#include "InputSection.h" +#include "LinkerScript.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/LTO/LTO.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/ARMAttributeParser.h" +#include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TarWriter.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::sys; +using namespace llvm::sys::fs; +using namespace llvm::support::endian; + +using namespace lld; +using namespace lld::elf; + +bool InputFile::isInGroup; +uint32_t InputFile::nextGroupId; +std::vector elf::binaryFiles; +std::vector elf::bitcodeFiles; +std::vector elf::lazyObjFiles; +std::vector elf::objectFiles; +std::vector elf::sharedFiles; + +std::unique_ptr elf::tar; + +static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) { + unsigned char size; + unsigned char endian; + std::tie(size, endian) = getElfArchType(mb.getBuffer()); + + auto report = [&](StringRef msg) { + StringRef filename = mb.getBufferIdentifier(); + if (archiveName.empty()) + fatal(filename + ": " + msg); + else + fatal(archiveName + "(" + filename + "): " + msg); + }; + + if (!mb.getBuffer().startswith(ElfMagic)) + report("not an ELF file"); + if (endian != ELFDATA2LSB && endian != ELFDATA2MSB) + report("corrupted ELF file: invalid data encoding"); + if (size != ELFCLASS32 && size != ELFCLASS64) + report("corrupted ELF file: invalid file class"); + + size_t bufSize = mb.getBuffer().size(); + if ((size == ELFCLASS32 && bufSize < sizeof(Elf32_Ehdr)) || + (size == ELFCLASS64 && bufSize < sizeof(Elf64_Ehdr))) + report("corrupted ELF file: file is too short"); + + if (size == ELFCLASS32) + return (endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; + return (endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; +} + +InputFile::InputFile(Kind k, MemoryBufferRef m) + : mb(m), groupId(nextGroupId), fileKind(k) { + // All files within the same --{start,end}-group get the same group ID. + // Otherwise, a new file will get a new group ID. + if (!isInGroup) + ++nextGroupId; +} + +Optional elf::readFile(StringRef path) { + // The --chroot option changes our virtual root directory. + // This is useful when you are dealing with files created by --reproduce. + if (!config->chroot.empty() && path.startswith("/")) + path = saver.save(config->chroot + path); + + log(path); + + auto mbOrErr = MemoryBuffer::getFile(path, -1, false); + if (auto ec = mbOrErr.getError()) { + error("cannot open " + path + ": " + ec.message()); + return None; + } + + std::unique_ptr &mb = *mbOrErr; + MemoryBufferRef mbref = mb->getMemBufferRef(); + make>(std::move(mb)); // take MB ownership + + if (tar) + tar->append(relativeToRoot(path), mbref.getBuffer()); + return mbref; +} + +// All input object files must be for the same architecture +// (e.g. it does not make sense to link x86 object files with +// MIPS object files.) This function checks for that error. +static bool isCompatible(InputFile *file) { + if (!file->isElf() && !isa(file)) + return true; + + if (file->ekind == config->ekind && file->emachine == config->emachine) { + if (config->emachine != EM_MIPS) + return true; + if (isMipsN32Abi(file) == config->mipsN32Abi) + return true; + } + + if (!config->emulation.empty()) { + error(toString(file) + " is incompatible with " + config->emulation); + } else { + InputFile *existing; + if (!objectFiles.empty()) + existing = objectFiles[0]; + else if (!sharedFiles.empty()) + existing = sharedFiles[0]; + else + existing = bitcodeFiles[0]; + + error(toString(file) + " is incompatible with " + toString(existing)); + } + + return false; +} + +template static void doParseFile(InputFile *file) { + if (!isCompatible(file)) + return; + + // Binary file + if (auto *f = dyn_cast(file)) { + binaryFiles.push_back(f); + f->parse(); + return; + } + + // .a file + if (auto *f = dyn_cast(file)) { + f->parse(); + return; + } + + // Lazy object file + if (auto *f = dyn_cast(file)) { + lazyObjFiles.push_back(f); + f->parse(); + return; + } + + if (config->trace) + message(toString(file)); + + // .so file + if (auto *f = dyn_cast(file)) { + f->parse(); + return; + } + + // LLVM bitcode file + if (auto *f = dyn_cast(file)) { + bitcodeFiles.push_back(f); + f->parse(); + return; + } + + // Regular object file + objectFiles.push_back(file); + cast>(file)->parse(); +} + +// Add symbols in File to the symbol table. +void elf::parseFile(InputFile *file) { + switch (config->ekind) { + case ELF32LEKind: + doParseFile(file); + return; + case ELF32BEKind: + doParseFile(file); + return; + case ELF64LEKind: + doParseFile(file); + return; + case ELF64BEKind: + doParseFile(file); + return; + default: + llvm_unreachable("unknown ELFT"); + } +} + +// Concatenates arguments to construct a string representing an error location. +static std::string createFileLineMsg(StringRef path, unsigned line) { + std::string filename = path::filename(path); + std::string lineno = ":" + std::to_string(line); + if (filename == path) + return filename + lineno; + return filename + lineno + " (" + path.str() + lineno + ")"; +} + +template +static std::string getSrcMsgAux(ObjFile &file, const Symbol &sym, + InputSectionBase &sec, uint64_t offset) { + // In DWARF, functions and variables are stored to different places. + // First, lookup a function for a given offset. + if (Optional info = file.getDILineInfo(&sec, offset)) + return createFileLineMsg(info->FileName, info->Line); + + // If it failed, lookup again as a variable. + if (Optional> fileLine = + file.getVariableLoc(sym.getName())) + return createFileLineMsg(fileLine->first, fileLine->second); + + // File.sourceFile contains STT_FILE symbol, and that is a last resort. + return file.sourceFile; +} + +std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec, + uint64_t offset) { + if (kind() != ObjKind) + return ""; + switch (config->ekind) { + default: + llvm_unreachable("Invalid kind"); + case ELF32LEKind: + return getSrcMsgAux(cast>(*this), sym, sec, offset); + case ELF32BEKind: + return getSrcMsgAux(cast>(*this), sym, sec, offset); + case ELF64LEKind: + return getSrcMsgAux(cast>(*this), sym, sec, offset); + case ELF64BEKind: + return getSrcMsgAux(cast>(*this), sym, sec, offset); + } +} + +template void ObjFile::initializeDwarf() { + dwarf = llvm::make_unique(make_unique>(this)); + for (std::unique_ptr &cu : dwarf->compile_units()) { + auto report = [](Error err) { + handleAllErrors(std::move(err), + [](ErrorInfoBase &info) { warn(info.message()); }); + }; + Expected expectedLT = + dwarf->getLineTableForUnit(cu.get(), report); + const DWARFDebugLine::LineTable *lt = nullptr; + if (expectedLT) + lt = *expectedLT; + else + report(expectedLT.takeError()); + if (!lt) + continue; + lineTables.push_back(lt); + + // Loop over variable records and insert them to variableLoc. + for (const auto &entry : cu->dies()) { + DWARFDie die(cu.get(), &entry); + // Skip all tags that are not variables. + if (die.getTag() != dwarf::DW_TAG_variable) + continue; + + // Skip if a local variable because we don't need them for generating + // error messages. In general, only non-local symbols can fail to be + // linked. + if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0)) + continue; + + // Get the source filename index for the variable. + unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0); + if (!lt->hasFileAtIndex(file)) + continue; + + // Get the line number on which the variable is declared. + unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0); + + // Here we want to take the variable name to add it into variableLoc. + // Variable can have regular and linkage name associated. At first, we try + // to get linkage name as it can be different, for example when we have + // two variables in different namespaces of the same object. Use common + // name otherwise, but handle the case when it also absent in case if the + // input object file lacks some debug info. + StringRef name = + dwarf::toString(die.find(dwarf::DW_AT_linkage_name), + dwarf::toString(die.find(dwarf::DW_AT_name), "")); + if (!name.empty()) + variableLoc.insert({name, {lt, file, line}}); + } + } +} + +// Returns the pair of file name and line number describing location of data +// object (variable, array, etc) definition. +template +Optional> +ObjFile::getVariableLoc(StringRef name) { + llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); + + // Return if we have no debug information about data object. + auto it = variableLoc.find(name); + if (it == variableLoc.end()) + return None; + + // Take file name string from line table. + std::string fileName; + if (!it->second.lt->getFileNameByIndex( + it->second.file, {}, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName)) + return None; + + return std::make_pair(fileName, it->second.line); +} + +// Returns source line information for a given offset +// using DWARF debug info. +template +Optional ObjFile::getDILineInfo(InputSectionBase *s, + uint64_t offset) { + llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); + + // Detect SectionIndex for specified section. + uint64_t sectionIndex = object::SectionedAddress::UndefSection; + ArrayRef sections = s->file->getSections(); + for (uint64_t curIndex = 0; curIndex < sections.size(); ++curIndex) { + if (s == sections[curIndex]) { + sectionIndex = curIndex; + break; + } + } + + // Use fake address calcuated by adding section file offset and offset in + // section. See comments for ObjectInfo class. + DILineInfo info; + for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) { + if (lt->getFileLineInfoForAddress( + {s->getOffsetInFile() + offset, sectionIndex}, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info)) + return info; + } + return None; +} + +// Returns "", "foo.a(bar.o)" or "baz.o". +std::string lld::toString(const InputFile *f) { + if (!f) + return ""; + + if (f->toStringCache.empty()) { + if (f->archiveName.empty()) + f->toStringCache = f->getName(); + else + f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str(); + } + return f->toStringCache; +} + +ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) { + ekind = getELFKind(mb, ""); + + switch (ekind) { + case ELF32LEKind: + init(); + break; + case ELF32BEKind: + init(); + break; + case ELF64LEKind: + init(); + break; + case ELF64BEKind: + init(); + break; + default: + llvm_unreachable("getELFKind"); + } +} + +template +static const Elf_Shdr *findSection(ArrayRef sections, uint32_t type) { + for (const Elf_Shdr &sec : sections) + if (sec.sh_type == type) + return &sec; + return nullptr; +} + +template void ELFFileBase::init() { + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + + // Initialize trivial attributes. + const ELFFile &obj = getObj(); + emachine = obj.getHeader()->e_machine; + osabi = obj.getHeader()->e_ident[llvm::ELF::EI_OSABI]; + abiVersion = obj.getHeader()->e_ident[llvm::ELF::EI_ABIVERSION]; + + ArrayRef sections = CHECK(obj.sections(), this); + + // Find a symbol table. + bool isDSO = + (identify_magic(mb.getBuffer()) == file_magic::elf_shared_object); + const Elf_Shdr *symtabSec = + findSection(sections, isDSO ? SHT_DYNSYM : SHT_SYMTAB); + + if (!symtabSec) + return; + + // Initialize members corresponding to a symbol table. + firstGlobal = symtabSec->sh_info; + + ArrayRef eSyms = CHECK(obj.symbols(symtabSec), this); + if (firstGlobal == 0 || firstGlobal > eSyms.size()) + fatal(toString(this) + ": invalid sh_info in symbol table"); + + elfSyms = reinterpret_cast(eSyms.data()); + numELFSyms = eSyms.size(); + stringTable = CHECK(obj.getStringTableForSymtab(*symtabSec, sections), this); +} + +template +uint32_t ObjFile::getSectionIndex(const Elf_Sym &sym) const { + return CHECK( + this->getObj().getSectionIndex(&sym, getELFSyms(), shndxTable), + this); +} + +template ArrayRef ObjFile::getLocalSymbols() { + if (this->symbols.empty()) + return {}; + return makeArrayRef(this->symbols).slice(1, this->firstGlobal - 1); +} + +template ArrayRef ObjFile::getGlobalSymbols() { + return makeArrayRef(this->symbols).slice(this->firstGlobal); +} + +template void ObjFile::parse(bool ignoreComdats) { + // Read a section table. justSymbols is usually false. + if (this->justSymbols) + initializeJustSymbols(); + else + initializeSections(ignoreComdats); + + // Read a symbol table. + initializeSymbols(); +} + +// Sections with SHT_GROUP and comdat bits define comdat section groups. +// They are identified and deduplicated by group name. This function +// returns a group name. +template +StringRef ObjFile::getShtGroupSignature(ArrayRef sections, + const Elf_Shdr &sec) { + typename ELFT::SymRange symbols = this->getELFSyms(); + if (sec.sh_info >= symbols.size()) + fatal(toString(this) + ": invalid symbol index"); + const typename ELFT::Sym &sym = symbols[sec.sh_info]; + StringRef signature = CHECK(sym.getName(this->stringTable), this); + + // As a special case, if a symbol is a section symbol and has no name, + // we use a section name as a signature. + // + // Such SHT_GROUP sections are invalid from the perspective of the ELF + // standard, but GNU gold 1.14 (the newest version as of July 2017) or + // older produce such sections as outputs for the -r option, so we need + // a bug-compatibility. + if (signature.empty() && sym.getType() == STT_SECTION) + return getSectionName(sec); + return signature; +} + +template bool ObjFile::shouldMerge(const Elf_Shdr &sec) { + // On a regular link we don't merge sections if -O0 (default is -O1). This + // sometimes makes the linker significantly faster, although the output will + // be bigger. + // + // Doing the same for -r would create a problem as it would combine sections + // with different sh_entsize. One option would be to just copy every SHF_MERGE + // section as is to the output. While this would produce a valid ELF file with + // usable SHF_MERGE sections, tools like (llvm-)?dwarfdump get confused when + // they see two .debug_str. We could have separate logic for combining + // SHF_MERGE sections based both on their name and sh_entsize, but that seems + // to be more trouble than it is worth. Instead, we just use the regular (-O1) + // logic for -r. + if (config->optimize == 0 && !config->relocatable) + return false; + + // A mergeable section with size 0 is useless because they don't have + // any data to merge. A mergeable string section with size 0 can be + // argued as invalid because it doesn't end with a null character. + // We'll avoid a mess by handling them as if they were non-mergeable. + if (sec.sh_size == 0) + return false; + + // Check for sh_entsize. The ELF spec is not clear about the zero + // sh_entsize. It says that "the member [sh_entsize] contains 0 if + // the section does not hold a table of fixed-size entries". We know + // that Rust 1.13 produces a string mergeable section with a zero + // sh_entsize. Here we just accept it rather than being picky about it. + uint64_t entSize = sec.sh_entsize; + if (entSize == 0) + return false; + if (sec.sh_size % entSize) + fatal(toString(this) + + ": SHF_MERGE section size must be a multiple of sh_entsize"); + + uint64_t flags = sec.sh_flags; + if (!(flags & SHF_MERGE)) + return false; + if (flags & SHF_WRITE) + fatal(toString(this) + ": writable SHF_MERGE section is not supported"); + + return true; +} + +// This is for --just-symbols. +// +// --just-symbols is a very minor feature that allows you to link your +// output against other existing program, so that if you load both your +// program and the other program into memory, your output can refer the +// other program's symbols. +// +// When the option is given, we link "just symbols". The section table is +// initialized with null pointers. +template void ObjFile::initializeJustSymbols() { + ArrayRef sections = CHECK(this->getObj().sections(), this); + this->sections.resize(sections.size()); +} + +// An ELF object file may contain a `.deplibs` section. If it exists, the +// section contains a list of library specifiers such as `m` for libm. This +// function resolves a given name by finding the first matching library checking +// the various ways that a library can be specified to LLD. This ELF extension +// is a form of autolinking and is called `dependent libraries`. It is currently +// unique to LLVM and lld. +static void addDependentLibrary(StringRef specifier, const InputFile *f) { + if (!config->dependentLibraries) + return; + if (fs::exists(specifier)) + driver->addFile(specifier, /*withLOption=*/false); + else if (Optional s = findFromSearchPaths(specifier)) + driver->addFile(*s, /*withLOption=*/true); + else if (Optional s = searchLibraryBaseName(specifier)) + driver->addFile(*s, /*withLOption=*/true); + else + error(toString(f) + + ": unable to find library from dependent library specifier: " + + specifier); +} + +template +void ObjFile::initializeSections(bool ignoreComdats) { + const ELFFile &obj = this->getObj(); + + ArrayRef objSections = CHECK(obj.sections(), this); + uint64_t size = objSections.size(); + this->sections.resize(size); + this->sectionStringTable = + CHECK(obj.getSectionStringTable(objSections), this); + + for (size_t i = 0, e = objSections.size(); i < e; i++) { + if (this->sections[i] == &InputSection::discarded) + continue; + const Elf_Shdr &sec = objSections[i]; + + if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) + cgProfile = + check(obj.template getSectionContentsAsArray(&sec)); + + // SHF_EXCLUDE'ed sections are discarded by the linker. However, + // if -r is given, we'll let the final link discard such sections. + // This is compatible with GNU. + if ((sec.sh_flags & SHF_EXCLUDE) && !config->relocatable) { + if (sec.sh_type == SHT_LLVM_ADDRSIG) { + // We ignore the address-significance table if we know that the object + // file was created by objcopy or ld -r. This is because these tools + // will reorder the symbols in the symbol table, invalidating the data + // in the address-significance table, which refers to symbols by index. + if (sec.sh_link != 0) + this->addrsigSec = &sec; + else if (config->icf == ICFLevel::Safe) + warn(toString(this) + ": --icf=safe is incompatible with object " + "files created using objcopy or ld -r"); + } + this->sections[i] = &InputSection::discarded; + continue; + } + + switch (sec.sh_type) { + case SHT_GROUP: { + // De-duplicate section groups by their signatures. + StringRef signature = getShtGroupSignature(objSections, sec); + this->sections[i] = &InputSection::discarded; + + + ArrayRef entries = + CHECK(obj.template getSectionContentsAsArray(&sec), this); + if (entries.empty()) + fatal(toString(this) + ": empty SHT_GROUP"); + + // The first word of a SHT_GROUP section contains flags. Currently, + // the standard defines only "GRP_COMDAT" flag for the COMDAT group. + // An group with the empty flag doesn't define anything; such sections + // are just skipped. + if (entries[0] == 0) + continue; + + if (entries[0] != GRP_COMDAT) + fatal(toString(this) + ": unsupported SHT_GROUP format"); + + bool isNew = + ignoreComdats || + symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this) + .second; + if (isNew) { + if (config->relocatable) + this->sections[i] = createInputSection(sec); + continue; + } + + // Otherwise, discard group members. + for (uint32_t secIndex : entries.slice(1)) { + if (secIndex >= size) + fatal(toString(this) + + ": invalid section index in group: " + Twine(secIndex)); + this->sections[secIndex] = &InputSection::discarded; + } + break; + } + case SHT_SYMTAB_SHNDX: + shndxTable = CHECK(obj.getSHNDXTable(sec, objSections), this); + break; + case SHT_SYMTAB: + case SHT_STRTAB: + case SHT_NULL: + break; + default: + this->sections[i] = createInputSection(sec); + } + + // .ARM.exidx sections have a reverse dependency on the InputSection they + // have a SHF_LINK_ORDER dependency, this is identified by the sh_link. + if (sec.sh_flags & SHF_LINK_ORDER) { + InputSectionBase *linkSec = nullptr; + if (sec.sh_link < this->sections.size()) + linkSec = this->sections[sec.sh_link]; + if (!linkSec) + fatal(toString(this) + + ": invalid sh_link index: " + Twine(sec.sh_link)); + + InputSection *isec = cast(this->sections[i]); + linkSec->dependentSections.push_back(isec); + if (!isa(linkSec)) + error("a section " + isec->name + + " with SHF_LINK_ORDER should not refer a non-regular " + "section: " + + toString(linkSec)); + } + } +} + +// For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD +// flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how +// the input objects have been compiled. +static void updateARMVFPArgs(const ARMAttributeParser &attributes, + const InputFile *f) { + if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) + // If an ABI tag isn't present then it is implicitly given the value of 0 + // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files, + // including some in glibc that don't use FP args (and should have value 3) + // don't have the attribute so we do not consider an implicit value of 0 + // as a clash. + return; + + unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + ARMVFPArgKind arg; + switch (vfpArgs) { + case ARMBuildAttrs::BaseAAPCS: + arg = ARMVFPArgKind::Base; + break; + case ARMBuildAttrs::HardFPAAPCS: + arg = ARMVFPArgKind::VFP; + break; + case ARMBuildAttrs::ToolChainFPPCS: + // Tool chain specific convention that conforms to neither AAPCS variant. + arg = ARMVFPArgKind::ToolChain; + break; + case ARMBuildAttrs::CompatibleFPAAPCS: + // Object compatible with all conventions. + return; + default: + error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs)); + return; + } + // Follow ld.bfd and error if there is a mix of calling conventions. + if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default) + error(toString(f) + ": incompatible Tag_ABI_VFP_args"); + else + config->armVFPArgs = arg; +} + +// The ARM support in lld makes some use of instructions that are not available +// on all ARM architectures. Namely: +// - Use of BLX instruction for interworking between ARM and Thumb state. +// - Use of the extended Thumb branch encoding in relocation. +// - Use of the MOVT/MOVW instructions in Thumb Thunks. +// The ARM Attributes section contains information about the architecture chosen +// at compile time. We follow the convention that if at least one input object +// is compiled with an architecture that supports these features then lld is +// permitted to use them. +static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) { + if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) + return; + auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + switch (arch) { + case ARMBuildAttrs::Pre_v4: + case ARMBuildAttrs::v4: + case ARMBuildAttrs::v4T: + // Architectures prior to v5 do not support BLX instruction + break; + case ARMBuildAttrs::v5T: + case ARMBuildAttrs::v5TE: + case ARMBuildAttrs::v5TEJ: + case ARMBuildAttrs::v6: + case ARMBuildAttrs::v6KZ: + case ARMBuildAttrs::v6K: + config->armHasBlx = true; + // Architectures used in pre-Cortex processors do not support + // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception + // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do. + break; + default: + // All other Architectures have BLX and extended branch encoding + config->armHasBlx = true; + config->armJ1J2BranchEncoding = true; + if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M) + // All Architectures used in Cortex processors with the exception + // of v6-M and v6S-M have the MOVT and MOVW instructions. + config->armHasMovtMovw = true; + break; + } +} + +// If a source file is compiled with x86 hardware-assisted call flow control +// enabled, the generated object file contains feature flags indicating that +// fact. This function reads the feature flags and returns it. +// +// Essentially we want to read a single 32-bit value in this function, but this +// function is rather complicated because the value is buried deep inside a +// .note.gnu.property section. +// +// The section consists of one or more NOTE records. Each NOTE record consists +// of zero or more type-length-value fields. We want to find a field of a +// certain type. It seems a bit too much to just store a 32-bit value, perhaps +// the ABI is unnecessarily complicated. +template +static uint32_t readAndFeatures(ObjFile *obj, ArrayRef data) { + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::Note; + + uint32_t featuresSet = 0; + while (!data.empty()) { + // Read one NOTE record. + if (data.size() < sizeof(Elf_Nhdr)) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + auto *nhdr = reinterpret_cast(data.data()); + if (data.size() < nhdr->getSize()) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + Elf_Note note(*nhdr); + if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") { + data = data.slice(nhdr->getSize()); + continue; + } + + uint32_t featureAndType = config->emachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + + // Read a body of a NOTE record, which consists of type-length-value fields. + ArrayRef desc = note.getDesc(); + while (!desc.empty()) { + if (desc.size() < 8) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + uint32_t type = read32le(desc.data()); + uint32_t size = read32le(desc.data() + 4); + + if (type == featureAndType) { + // We found a FEATURE_1_AND field. There may be more than one of these + // in a .note.gnu.propery section, for a relocatable object we + // accumulate the bits set. + featuresSet |= read32le(desc.data() + 8); + } + + // On 64-bit, a payload may be followed by a 4-byte padding to make its + // size a multiple of 8. + if (ELFT::Is64Bits) + size = alignTo(size, 8); + + desc = desc.slice(size + 8); // +8 for Type and Size + } + + // Go to next NOTE record to look for more FEATURE_1_AND descriptions. + data = data.slice(nhdr->getSize()); + } + + return featuresSet; +} + +template +InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &sec) { + uint32_t idx = sec.sh_info; + if (idx >= this->sections.size()) + fatal(toString(this) + ": invalid relocated section index: " + Twine(idx)); + InputSectionBase *target = this->sections[idx]; + + // Strictly speaking, a relocation section must be included in the + // group of the section it relocates. However, LLVM 3.3 and earlier + // would fail to do so, so we gracefully handle that case. + if (target == &InputSection::discarded) + return nullptr; + + if (!target) + fatal(toString(this) + ": unsupported relocation reference"); + return target; +} + +// Create a regular InputSection class that has the same contents +// as a given section. +static InputSection *toRegularSection(MergeInputSection *sec) { + return make(sec->file, sec->flags, sec->type, sec->alignment, + sec->data(), sec->name); +} + +template +InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { + StringRef name = getSectionName(sec); + + switch (sec.sh_type) { + case SHT_ARM_ATTRIBUTES: { + if (config->emachine != EM_ARM) + break; + ARMAttributeParser attributes; + ArrayRef contents = check(this->getObj().getSectionContents(&sec)); + attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind); + updateSupportedARMFeatures(attributes); + updateARMVFPArgs(attributes, this); + + // FIXME: Retain the first attribute section we see. The eglibc ARM + // dynamic loaders require the presence of an attribute section for dlopen + // to work. In a full implementation we would merge all attribute sections. + if (in.armAttributes == nullptr) { + in.armAttributes = make(*this, sec, name); + return in.armAttributes; + } + return &InputSection::discarded; + } + case SHT_LLVM_DEPENDENT_LIBRARIES: { + if (config->relocatable) + break; + ArrayRef data = + CHECK(this->getObj().template getSectionContentsAsArray(&sec), this); + if (!data.empty() && data.back() != '\0') { + error(toString(this) + + ": corrupted dependent libraries section (unterminated string): " + + name); + return &InputSection::discarded; + } + for (const char *d = data.begin(), *e = data.end(); d < e;) { + StringRef s(d); + addDependentLibrary(s, this); + d += s.size() + 1; + } + return &InputSection::discarded; + } + case SHT_RELA: + case SHT_REL: { + // Find a relocation target section and associate this section with that. + // Target may have been discarded if it is in a different section group + // and the group is discarded, even though it's a violation of the + // spec. We handle that situation gracefully by discarding dangling + // relocation sections. + InputSectionBase *target = getRelocTarget(sec); + if (!target) + return nullptr; + + // This section contains relocation information. + // If -r is given, we do not interpret or apply relocation + // but just copy relocation sections to output. + if (config->relocatable) { + InputSection *relocSec = make(*this, sec, name); + // We want to add a dependency to target, similar like we do for + // -emit-relocs below. This is useful for the case when linker script + // contains the "/DISCARD/". It is perhaps uncommon to use a script with + // -r, but we faced it in the Linux kernel and have to handle such case + // and not to crash. + target->dependentSections.push_back(relocSec); + return relocSec; + } + + if (target->firstRelocation) + fatal(toString(this) + + ": multiple relocation sections to one section are not supported"); + + // ELF spec allows mergeable sections with relocations, but they are + // rare, and it is in practice hard to merge such sections by contents, + // because applying relocations at end of linking changes section + // contents. So, we simply handle such sections as non-mergeable ones. + // Degrading like this is acceptable because section merging is optional. + if (auto *ms = dyn_cast(target)) { + target = toRegularSection(ms); + this->sections[sec.sh_info] = target; + } + + if (sec.sh_type == SHT_RELA) { + ArrayRef rels = CHECK(getObj().relas(&sec), this); + target->firstRelocation = rels.begin(); + target->numRelocations = rels.size(); + target->areRelocsRela = true; + } else { + ArrayRef rels = CHECK(getObj().rels(&sec), this); + target->firstRelocation = rels.begin(); + target->numRelocations = rels.size(); + target->areRelocsRela = false; + } + assert(isUInt<31>(target->numRelocations)); + + // Relocation sections processed by the linker are usually removed + // from the output, so returning `nullptr` for the normal case. + // However, if -emit-relocs is given, we need to leave them in the output. + // (Some post link analysis tools need this information.) + if (config->emitRelocs) { + InputSection *relocSec = make(*this, sec, name); + // We will not emit relocation section if target was discarded. + target->dependentSections.push_back(relocSec); + return relocSec; + } + return nullptr; + } + } + + // The GNU linker uses .note.GNU-stack section as a marker indicating + // that the code in the object file does not expect that the stack is + // executable (in terms of NX bit). If all input files have the marker, + // the GNU linker adds a PT_GNU_STACK segment to tells the loader to + // make the stack non-executable. Most object files have this section as + // of 2017. + // + // But making the stack non-executable is a norm today for security + // reasons. Failure to do so may result in a serious security issue. + // Therefore, we make LLD always add PT_GNU_STACK unless it is + // explicitly told to do otherwise (by -z execstack). Because the stack + // executable-ness is controlled solely by command line options, + // .note.GNU-stack sections are simply ignored. + if (name == ".note.GNU-stack") + return &InputSection::discarded; + + // Object files that use processor features such as Intel Control-Flow + // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a + // .note.gnu.property section containing a bitfield of feature bits like the + // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag. + // + // Since we merge bitmaps from multiple object files to create a new + // .note.gnu.property containing a single AND'ed bitmap, we discard an input + // file's .note.gnu.property section. + if (name == ".note.gnu.property") { + ArrayRef contents = check(this->getObj().getSectionContents(&sec)); + this->andFeatures = readAndFeatures(this, contents); + return &InputSection::discarded; + } + + // Split stacks is a feature to support a discontiguous stack, + // commonly used in the programming language Go. For the details, + // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled + // for split stack will include a .note.GNU-split-stack section. + if (name == ".note.GNU-split-stack") { + if (config->relocatable) { + error("cannot mix split-stack and non-split-stack in a relocatable link"); + return &InputSection::discarded; + } + this->splitStack = true; + return &InputSection::discarded; + } + + // An object file cmpiled for split stack, but where some of the + // functions were compiled with the no_split_stack_attribute will + // include a .note.GNU-no-split-stack section. + if (name == ".note.GNU-no-split-stack") { + this->someNoSplitStack = true; + return &InputSection::discarded; + } + + // The linkonce feature is a sort of proto-comdat. Some glibc i386 object + // files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce + // sections. Drop those sections to avoid duplicate symbol errors. + // FIXME: This is glibc PR20543, we should remove this hack once that has been + // fixed for a while. + if (name == ".gnu.linkonce.t.__x86.get_pc_thunk.bx" || + name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx") + return &InputSection::discarded; + + // If we are creating a new .build-id section, strip existing .build-id + // sections so that the output won't have more than one .build-id. + // This is not usually a problem because input object files normally don't + // have .build-id sections, but you can create such files by + // "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it. + if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None) + return &InputSection::discarded; + + // The linker merges EH (exception handling) frames and creates a + // .eh_frame_hdr section for runtime. So we handle them with a special + // class. For relocatable outputs, they are just passed through. + if (name == ".eh_frame" && !config->relocatable) + return make(*this, sec, name); + + if (shouldMerge(sec)) + return make(*this, sec, name); + return make(*this, sec, name); +} + +template +StringRef ObjFile::getSectionName(const Elf_Shdr &sec) { + return CHECK(getObj().getSectionName(&sec, sectionStringTable), this); +} + +// Initialize this->Symbols. this->Symbols is a parallel array as +// its corresponding ELF symbol table. +template void ObjFile::initializeSymbols() { + ArrayRef eSyms = this->getELFSyms(); + this->symbols.resize(eSyms.size()); + + // Our symbol table may have already been partially initialized + // because of LazyObjFile. + for (size_t i = 0, end = eSyms.size(); i != end; ++i) + if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL) + this->symbols[i] = + symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this)); + + // Fill this->Symbols. A symbol is either local or global. + for (size_t i = 0, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + + // Read symbol attributes. + uint32_t secIdx = getSectionIndex(eSym); + if (secIdx >= this->sections.size()) + fatal(toString(this) + ": invalid section index: " + Twine(secIdx)); + + InputSectionBase *sec = this->sections[secIdx]; + uint8_t binding = eSym.getBinding(); + uint8_t stOther = eSym.st_other; + uint8_t type = eSym.getType(); + uint64_t value = eSym.st_value; + uint64_t size = eSym.st_size; + StringRefZ name = this->stringTable.data() + eSym.st_name; + + // Handle local symbols. Local symbols are not added to the symbol + // table because they are not visible from other object files. We + // allocate symbol instances and add their pointers to Symbols. + if (binding == STB_LOCAL) { + if (eSym.getType() == STT_FILE) + sourceFile = CHECK(eSym.getName(this->stringTable), this); + + if (this->stringTable.size() <= eSym.st_name) + fatal(toString(this) + ": invalid symbol name offset"); + + if (eSym.st_shndx == SHN_UNDEF) + this->symbols[i] = make(this, name, binding, stOther, type); + else if (sec == &InputSection::discarded) + this->symbols[i] = make(this, name, binding, stOther, type, + /*DiscardedSecIdx=*/secIdx); + else + this->symbols[i] = + make(this, name, binding, stOther, type, value, size, sec); + continue; + } + + // Handle global undefined symbols. + if (eSym.st_shndx == SHN_UNDEF) { + this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type}); + continue; + } + + // Handle global common symbols. + if (eSym.st_shndx == SHN_COMMON) { + if (value == 0 || value >= UINT32_MAX) + fatal(toString(this) + ": common symbol '" + StringRef(name.data) + + "' has invalid alignment: " + Twine(value)); + this->symbols[i]->resolve( + CommonSymbol{this, name, binding, stOther, type, value, size}); + continue; + } + + // If a defined symbol is in a discarded section, handle it as if it + // were an undefined symbol. Such symbol doesn't comply with the + // standard, but in practice, a .eh_frame often directly refer + // COMDAT member sections, and if a comdat group is discarded, some + // defined symbol in a .eh_frame becomes dangling symbols. + if (sec == &InputSection::discarded) { + this->symbols[i]->resolve( + Undefined{this, name, binding, stOther, type, secIdx}); + continue; + } + + // Handle global defined symbols. + if (binding == STB_GLOBAL || binding == STB_WEAK || + binding == STB_GNU_UNIQUE) { + this->symbols[i]->resolve( + Defined{this, name, binding, stOther, type, value, size, sec}); + continue; + } + + fatal(toString(this) + ": unexpected binding: " + Twine((int)binding)); + } +} + +ArchiveFile::ArchiveFile(std::unique_ptr &&file) + : InputFile(ArchiveKind, file->getMemoryBufferRef()), + file(std::move(file)) {} + +void ArchiveFile::parse() { + for (const Archive::Symbol &sym : file->symbols()) + symtab->addSymbol(LazyArchive{*this, sym}); +} + +// Returns a buffer pointing to a member file containing a given symbol. +void ArchiveFile::fetch(const Archive::Symbol &sym) { + Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member for symbol " + + toELFString(sym)); + + if (!seen.insert(c.getChildOffset()).second) + return; + + MemoryBufferRef mb = + CHECK(c.getMemoryBufferRef(), + toString(this) + + ": could not get the buffer for the member defining symbol " + + toELFString(sym)); + + if (tar && c.getParent()->isThin()) + tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); + + InputFile *file = createObjectFile( + mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset()); + file->groupId = groupId; + parseFile(file); +} + +unsigned SharedFile::vernauxNum; + +// Parse the version definitions in the object file if present, and return a +// vector whose nth element contains a pointer to the Elf_Verdef for version +// identifier n. Version identifiers that are not definitions map to nullptr. +template +static std::vector parseVerdefs(const uint8_t *base, + const typename ELFT::Shdr *sec) { + if (!sec) + return {}; + + // We cannot determine the largest verdef identifier without inspecting + // every Elf_Verdef, but both bfd and gold assign verdef identifiers + // sequentially starting from 1, so we predict that the largest identifier + // will be verdefCount. + unsigned verdefCount = sec->sh_info; + std::vector verdefs(verdefCount + 1); + + // Build the Verdefs array by following the chain of Elf_Verdef objects + // from the start of the .gnu.version_d section. + const uint8_t *verdef = base + sec->sh_offset; + for (unsigned i = 0; i != verdefCount; ++i) { + auto *curVerdef = reinterpret_cast(verdef); + verdef += curVerdef->vd_next; + unsigned verdefIndex = curVerdef->vd_ndx; + verdefs.resize(verdefIndex + 1); + verdefs[verdefIndex] = curVerdef; + } + return verdefs; +} + +// 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 +// the original alignment requirements. We infer it in this function. +template +static uint64_t getAlignment(ArrayRef sections, + const typename ELFT::Sym &sym) { + uint64_t ret = UINT64_MAX; + if (sym.st_value) + ret = 1ULL << countTrailingZeros((uint64_t)sym.st_value); + if (0 < sym.st_shndx && sym.st_shndx < sections.size()) + ret = std::min(ret, sections[sym.st_shndx].sh_addralign); + return (ret > UINT32_MAX) ? 0 : ret; +} + +// Fully parse the shared object file. +// +// This function parses symbol versions. If a DSO has version information, +// the file has a ".gnu.version_d" section which contains symbol version +// definitions. Each symbol is associated to one version through a table in +// ".gnu.version" section. That table is a parallel array for the symbol +// table, and each table entry contains an index in ".gnu.version_d". +// +// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for +// VER_NDX_GLOBAL. There's no table entry for these special versions in +// ".gnu.version_d". +// +// The file format for symbol versioning is perhaps a bit more complicated +// than necessary, but you can easily understand the code if you wrap your +// head around the data structure described above. +template void SharedFile::parse() { + using Elf_Dyn = typename ELFT::Dyn; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Verdef = typename ELFT::Verdef; + using Elf_Versym = typename ELFT::Versym; + + ArrayRef dynamicTags; + const ELFFile obj = this->getObj(); + ArrayRef sections = CHECK(obj.sections(), this); + + const Elf_Shdr *versymSec = nullptr; + const Elf_Shdr *verdefSec = nullptr; + + // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. + for (const Elf_Shdr &sec : sections) { + switch (sec.sh_type) { + default: + continue; + case SHT_DYNAMIC: + dynamicTags = + CHECK(obj.template getSectionContentsAsArray(&sec), this); + break; + case SHT_GNU_versym: + versymSec = &sec; + break; + case SHT_GNU_verdef: + verdefSec = &sec; + break; + } + } + + if (versymSec && 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; + } + } + + // DSOs are uniquified not by filename but by soname. + DenseMap::iterator it; + bool wasInserted; + std::tie(it, wasInserted) = symtab->soNames.try_emplace(soName, this); + + // If a DSO appears more than once on the command line with and without + // --as-needed, --no-as-needed takes precedence over --as-needed because a + // user can add an extra DSO with --no-as-needed to force it to be added to + // the dependency list. + it->second->isNeeded |= isNeeded; + if (!wasInserted) + return; + + sharedFiles.push_back(this); + + verdefs = parseVerdefs(obj.base(), verdefSec); + + // 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 = numELFSyms - firstGlobal; + std::vector versyms(size, VER_NDX_GLOBAL); + if (versymSec) { + ArrayRef versym = + CHECK(obj.template getSectionContentsAsArray(versymSec), + this) + .slice(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. + ArrayRef syms = this->getGlobalELFSyms(); + for (size_t i = 0; i < syms.size(); ++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; + } + + if (sym.isUndefined()) { + Symbol *s = symtab->addSymbol( + Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); + s->exportDynamic = true; + 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. + uint32_t idx = versyms[i] & ~VERSYM_HIDDEN; + if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL && + name == "_gp_disp") + continue; + + uint32_t alignment = getAlignment(sections, sym); + if (!(versyms[i] & VERSYM_HIDDEN)) { + symtab->addSymbol(SharedSymbol{*this, name, sym.getBinding(), + sym.st_other, sym.getType(), sym.st_value, + sym.st_size, alignment, 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); + symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(), + sym.st_other, sym.getType(), sym.st_value, + sym.st_size, alignment, idx}); + } +} + +static ELFKind getBitcodeELFKind(const Triple &t) { + if (t.isLittleEndian()) + return t.isArch64Bit() ? ELF64LEKind : ELF32LEKind; + return t.isArch64Bit() ? ELF64BEKind : ELF32BEKind; +} + +static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { + switch (t.getArch()) { + case Triple::aarch64: + return EM_AARCH64; + case Triple::amdgcn: + case Triple::r600: + return EM_AMDGPU; + case Triple::arm: + case Triple::thumb: + return EM_ARM; + case Triple::avr: + return EM_AVR; + case Triple::mips: + case Triple::mipsel: + case Triple::mips64: + case Triple::mips64el: + return EM_MIPS; + case Triple::msp430: + return EM_MSP430; + case Triple::ppc: + return EM_PPC; + case Triple::ppc64: + case Triple::ppc64le: + return EM_PPC64; + case Triple::riscv32: + case Triple::riscv64: + return EM_RISCV; + case Triple::x86: + return t.isOSIAMCU() ? EM_IAMCU : EM_386; + case Triple::x86_64: + return EM_X86_64; + default: + error(path + ": could not infer e_machine from bitcode target triple " + + t.str()); + return EM_NONE; + } +} + +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + this->archiveName = archiveName; + + std::string path = mb.getBufferIdentifier().str(); + if (config->thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + + // ThinLTO assumes that all MemoryBufferRefs given to it have a unique + // name. If two archives define two members with the same name, this + // causes a collision which result in only one of the objects being taken + // into consideration at LTO time (which very likely causes undefined + // symbols later in the link stage). So we append file offset to make + // filename unique. + StringRef name = archiveName.empty() + ? saver.save(path) + : saver.save(archiveName + "(" + path + " at " + + utostr(offsetInArchive) + ")"); + MemoryBufferRef mbref(mb.getBuffer(), name); + + obj = CHECK(lto::InputFile::create(mbref), this); + + Triple t(obj->getTargetTriple()); + ekind = getBitcodeELFKind(t); + emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t); +} + +static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { + switch (gvVisibility) { + case GlobalValue::DefaultVisibility: + return STV_DEFAULT; + case GlobalValue::HiddenVisibility: + return STV_HIDDEN; + case GlobalValue::ProtectedVisibility: + return STV_PROTECTED; + } + llvm_unreachable("unknown visibility"); +} + +template +static Symbol *createBitcodeSymbol(const std::vector &keptComdats, + const lto::InputFile::Symbol &objSym, + BitcodeFile &f) { + StringRef name = saver.save(objSym.getName()); + uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL; + uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE; + uint8_t visibility = mapVisibility(objSym.getVisibility()); + bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable(); + + int c = objSym.getComdatIndex(); + if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) { + Undefined New(&f, name, binding, visibility, type); + if (canOmitFromDynSym) + New.exportDynamic = false; + return symtab->addSymbol(New); + } + + if (objSym.isCommon()) + return symtab->addSymbol( + CommonSymbol{&f, name, binding, visibility, STT_OBJECT, + objSym.getCommonAlignment(), objSym.getCommonSize()}); + + Defined New(&f, name, binding, visibility, type, 0, 0, nullptr); + if (canOmitFromDynSym) + New.exportDynamic = false; + return symtab->addSymbol(New); +} + +template void BitcodeFile::parse() { + std::vector keptComdats; + for (StringRef s : obj->getComdatTable()) + keptComdats.push_back( + symtab->comdatGroups.try_emplace(CachedHashStringRef(s), this).second); + + for (const lto::InputFile::Symbol &objSym : obj->symbols()) + symbols.push_back(createBitcodeSymbol(keptComdats, objSym, *this)); + + for (auto l : obj->getDependentLibraries()) + addDependentLibrary(l, this); +} + +void BinaryFile::parse() { + ArrayRef data = arrayRefFromStringRef(mb.getBuffer()); + auto *section = make(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, + 8, data, ".data"); + sections.push_back(section); + + // For each input file foo that is embedded to a result as a binary + // blob, we define _binary_foo_{start,end,size} symbols, so that + // user programs can access blobs by name. Non-alphanumeric + // characters in a filename are replaced with underscore. + std::string s = "_binary_" + mb.getBufferIdentifier().str(); + for (size_t i = 0; i < s.size(); ++i) + if (!isAlnum(s[i])) + s[i] = '_'; + + symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, 0, 0, section}); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, data.size(), 0, section}); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr}); +} + +InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) { + if (isBitcode(mb)) + return make(mb, archiveName, offsetInArchive); + + switch (getELFKind(mb, archiveName)) { + case ELF32LEKind: + return make>(mb, archiveName); + case ELF32BEKind: + return make>(mb, archiveName); + case ELF64LEKind: + return make>(mb, archiveName); + case ELF64BEKind: + return make>(mb, archiveName); + default: + llvm_unreachable("getELFKind"); + } +} + +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; + + InputFile *file = createObjectFile(mb, archiveName, offsetInArchive); + file->groupId = groupId; + + mb = {}; + + // Copy symbol vector so that the new InputFile doesn't have to + // insert the same defined symbols to the symbol table again. + file->symbols = std::move(symbols); + + parseFile(file); +} + +template void LazyObjFile::parse() { + using Elf_Sym = typename ELFT::Sym; + + // A lazy object file wraps either a bitcode file or an ELF file. + if (isBitcode(this->mb)) { + std::unique_ptr obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (sym.isUndefined()) + continue; + symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())}); + } + return; + } + + if (getELFKind(this->mb, archiveName) != config->ekind) { + error("incompatible file: " + this->mb.getBufferIdentifier()); + return; + } + + // Find a symbol table. + ELFFile obj = check(ELFFile::create(mb.getBuffer())); + ArrayRef sections = CHECK(obj.sections(), this); + + for (const typename ELFT::Shdr &sec : sections) { + if (sec.sh_type != SHT_SYMTAB) + continue; + + // A symbol table is found. + ArrayRef eSyms = CHECK(obj.symbols(&sec), this); + uint32_t firstGlobal = sec.sh_info; + StringRef strtab = CHECK(obj.getStringTableForSymtab(sec, sections), this); + this->symbols.resize(eSyms.size()); + + // Get existing symbols or insert placeholder symbols. + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) + if (eSyms[i].st_shndx != SHN_UNDEF) + this->symbols[i] = symtab->insert(CHECK(eSyms[i].getName(strtab), this)); + + // Replace existing symbols with LazyObject symbols. + // + // resolve() may trigger this->fetch() if an existing symbol is an + // undefined symbol. If that happens, this LazyObjFile has served + // its purpose, and we can exit from the loop early. + for (Symbol *sym : this->symbols) { + if (!sym) + continue; + sym->resolve(LazyObject{*this, sym->getName()}); + + // MemoryBuffer is emptied if this file is instantiated as ObjFile. + if (mb.getBuffer().empty()) + return; + } + return; + } +} + +std::string elf::replaceThinLTOSuffix(StringRef path) { + StringRef suffix = config->thinLTOObjectSuffixReplace.first; + StringRef repl = config->thinLTOObjectSuffixReplace.second; + + if (path.consume_back(suffix)) + return (path + repl).str(); + return path; +} + +template void BitcodeFile::parse(); +template void BitcodeFile::parse(); +template void BitcodeFile::parse(); +template void BitcodeFile::parse(); + +template void LazyObjFile::parse(); +template void LazyObjFile::parse(); +template void LazyObjFile::parse(); +template void LazyObjFile::parse(); + +template class elf::ObjFile; +template class elf::ObjFile; +template class elf::ObjFile; +template class elf::ObjFile; + +template void SharedFile::parse(); +template void SharedFile::parse(); +template void SharedFile::parse(); +template void SharedFile::parse(); Property changes on: vendor/lld/lld-release_900-r372316/ELF/InputFiles.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Symbols.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Symbols.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Symbols.cpp (revision 352529) @@ -0,0 +1,663 @@ +//===- Symbols.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Symbols.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "OutputSections.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "Writer.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Strings.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Path.h" +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf; + +Defined *ElfSym::bss; +Defined *ElfSym::etext1; +Defined *ElfSym::etext2; +Defined *ElfSym::edata1; +Defined *ElfSym::edata2; +Defined *ElfSym::end1; +Defined *ElfSym::end2; +Defined *ElfSym::globalOffsetTable; +Defined *ElfSym::mipsGp; +Defined *ElfSym::mipsGpDisp; +Defined *ElfSym::mipsLocalGp; +Defined *ElfSym::relaIpltStart; +Defined *ElfSym::relaIpltEnd; +Defined *ElfSym::riscvGlobalPointer; +Defined *ElfSym::tlsModuleBase; + +// Returns a symbol for an error message. +static std::string demangle(StringRef symName) { + if (config->demangle) + if (Optional s = demangleItanium(symName)) + return *s; + return symName; +} +namespace lld { +std::string toString(const Symbol &b) { return demangle(b.getName()); } +std::string toELFString(const Archive::Symbol &b) { + return demangle(b.getName()); +} +} // namespace lld + +static uint64_t getSymVA(const Symbol &sym, int64_t &addend) { + switch (sym.kind()) { + case Symbol::DefinedKind: { + auto &d = cast(sym); + SectionBase *isec = d.section; + + // This is an absolute symbol. + if (!isec) + return d.value; + + assert(isec != &InputSection::discarded); + isec = isec->repl; + + uint64_t offset = d.value; + + // An object in an SHF_MERGE section might be referenced via a + // section symbol (as a hack for reducing the number of local + // symbols). + // Depending on the addend, the reference via a section symbol + // refers to a different object in the merge section. + // Since the objects in the merge section are not necessarily + // contiguous in the output, the addend can thus affect the final + // VA in a non-linear way. + // To make this work, we incorporate the addend into the section + // offset (and zero out the addend for later processing) so that + // we find the right object in the section. + if (d.isSection()) { + offset += addend; + addend = 0; + } + + // In the typical case, this is actually very simple and boils + // down to adding together 3 numbers: + // 1. The address of the output section. + // 2. The offset of the input section within the output section. + // 3. The offset within the input section (this addition happens + // inside InputSection::getOffset). + // + // If you understand the data structures involved with this next + // line (and how they get built), then you have a pretty good + // understanding of the linker. + uint64_t va = isec->getVA(offset); + + // MIPS relocatable files can mix regular and microMIPS code. + // Linker needs to distinguish such code. To do so microMIPS + // symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other` + // field. Unfortunately, the `MIPS::relocateOne()` method has + // a symbol value only. To pass type of the symbol (regular/microMIPS) + // to that routine as well as other places where we write + // a symbol value as-is (.dynamic section, `Elf_Ehdr::e_entry` + // field etc) do the same trick as compiler uses to mark microMIPS + // for CPU - set the less-significant bit. + if (config->emachine == EM_MIPS && isMicroMips() && + ((sym.stOther & STO_MIPS_MICROMIPS) || sym.needsPltAddr)) + va |= 1; + + if (d.isTls() && !config->relocatable) { + // Use the address of the TLS segment's first section rather than the + // segment's address, because segment addresses aren't initialized until + // after sections are finalized. (e.g. Measuring the size of .rela.dyn + // for Android relocation packing requires knowing TLS symbol addresses + // during section finalization.) + if (!Out::tlsPhdr || !Out::tlsPhdr->firstSec) + fatal(toString(d.file) + + " has an STT_TLS symbol but doesn't have an SHF_TLS section"); + return va - Out::tlsPhdr->firstSec->addr; + } + return va; + } + case Symbol::SharedKind: + case Symbol::UndefinedKind: + return 0; + case Symbol::LazyArchiveKind: + case Symbol::LazyObjectKind: + assert(sym.isUsedInRegularObj && "lazy symbol reached writer"); + return 0; + case Symbol::CommonKind: + llvm_unreachable("common symbol reached writer"); + case Symbol::PlaceholderKind: + llvm_unreachable("placeholder symbol reached writer"); + } + llvm_unreachable("invalid symbol kind"); +} + +uint64_t Symbol::getVA(int64_t addend) const { + uint64_t outVA = getSymVA(*this, addend); + return outVA + addend; +} + +uint64_t Symbol::getGotVA() const { + if (gotInIgot) + return in.igotPlt->getVA() + getGotPltOffset(); + return in.got->getVA() + getGotOffset(); +} + +uint64_t Symbol::getGotOffset() const { return gotIndex * config->wordsize; } + +uint64_t Symbol::getGotPltVA() const { + if (isInIplt) + return in.igotPlt->getVA() + getGotPltOffset(); + return in.gotPlt->getVA() + getGotPltOffset(); +} + +uint64_t Symbol::getGotPltOffset() const { + if (isInIplt) + return pltIndex * config->wordsize; + return (pltIndex + target->gotPltHeaderEntriesNum) * config->wordsize; +} + +uint64_t Symbol::getPPC64LongBranchOffset() const { + assert(ppc64BranchltIndex != 0xffff); + return ppc64BranchltIndex * config->wordsize; +} + +uint64_t Symbol::getPltVA() const { + PltSection *plt = isInIplt ? in.iplt : in.plt; + uint64_t outVA = + plt->getVA() + plt->headerSize + pltIndex * target->pltEntrySize; + // While linking microMIPS code PLT code are always microMIPS + // code. Set the less-significant bit to track that fact. + // See detailed comment in the `getSymVA` function. + if (config->emachine == EM_MIPS && isMicroMips()) + outVA |= 1; + return outVA; +} + +uint64_t Symbol::getPPC64LongBranchTableVA() const { + assert(ppc64BranchltIndex != 0xffff); + return in.ppc64LongBranchTarget->getVA() + + ppc64BranchltIndex * config->wordsize; +} + +uint64_t Symbol::getSize() const { + if (const auto *dr = dyn_cast(this)) + return dr->size; + return cast(this)->size; +} + +OutputSection *Symbol::getOutputSection() const { + if (auto *s = dyn_cast(this)) { + if (auto *sec = s->section) + return sec->repl->getOutputSection(); + return nullptr; + } + return nullptr; +} + +// If a symbol name contains '@', the characters after that is +// a symbol version name. This function parses that. +void Symbol::parseSymbolVersion() { + StringRef s = getName(); + size_t pos = s.find('@'); + if (pos == 0 || pos == StringRef::npos) + return; + StringRef verstr = s.substr(pos + 1); + if (verstr.empty()) + return; + + // Truncate the symbol name so that it doesn't include the version string. + nameSize = pos; + + // If this is not in this DSO, it is not a definition. + if (!isDefined()) + return; + + // '@@' in a symbol name means the default version. + // It is usually the most recent one. + bool isDefault = (verstr[0] == '@'); + if (isDefault) + verstr = verstr.substr(1); + + for (VersionDefinition &ver : config->versionDefinitions) { + if (ver.name != verstr) + continue; + + if (isDefault) + versionId = ver.id; + else + versionId = ver.id | VERSYM_HIDDEN; + return; + } + + // It is an error if the specified version is not defined. + // Usually version script is not provided when linking executable, + // but we may still want to override a versioned symbol from DSO, + // so we do not report error in this case. We also do not error + // if the symbol has a local version as it won't be in the dynamic + // symbol table. + if (config->shared && versionId != VER_NDX_LOCAL) + error(toString(file) + ": symbol " + s + " has undefined version " + + verstr); +} + +void Symbol::fetch() const { + if (auto *sym = dyn_cast(this)) { + cast(sym->file)->fetch(sym->sym); + return; + } + + if (auto *sym = dyn_cast(this)) { + dyn_cast(sym->file)->fetch(); + return; + } + + llvm_unreachable("Symbol::fetch() is called on a non-lazy symbol"); +} + +MemoryBufferRef LazyArchive::getMemberBuffer() { + Archive::Child c = + CHECK(sym.getMember(), + "could not get the member for symbol " + toELFString(sym)); + + return CHECK(c.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + toELFString(sym)); +} + +uint8_t Symbol::computeBinding() const { + if (config->relocatable) + return binding; + if (visibility != STV_DEFAULT && visibility != STV_PROTECTED) + return STB_LOCAL; + if (versionId == VER_NDX_LOCAL && isDefined() && !isPreemptible) + return STB_LOCAL; + if (!config->gnuUnique && binding == STB_GNU_UNIQUE) + return STB_GLOBAL; + return binding; +} + +bool Symbol::includeInDynsym() const { + if (!config->hasDynSymTab) + return false; + if (computeBinding() == STB_LOCAL) + return false; + + // If a PIE binary was not linked against any shared libraries, then we can + // safely drop weak undef symbols from .dynsym. + if (isUndefWeak() && config->pie && sharedFiles.empty()) + return false; + + return isUndefined() || isShared() || exportDynamic; +} + +// Print out a log message for --trace-symbol. +void elf::printTraceSymbol(const Symbol *sym) { + std::string s; + if (sym->isUndefined()) + s = ": reference to "; + else if (sym->isLazy()) + s = ": lazy definition of "; + else if (sym->isShared()) + s = ": shared definition of "; + else if (sym->isCommon()) + s = ": common definition of "; + else + s = ": definition of "; + + message(toString(sym->file) + s + sym->getName()); +} + +void elf::maybeWarnUnorderableSymbol(const Symbol *sym) { + if (!config->warnSymbolOrdering) + return; + + // If UnresolvedPolicy::Ignore is used, no "undefined symbol" error/warning + // is emitted. It makes sense to not warn on undefined symbols. + // + // Note, ld.bfd --symbol-ordering-file= does not warn on undefined symbols, + // but we don't have to be compatible here. + if (sym->isUndefined() && + config->unresolvedSymbols == UnresolvedPolicy::Ignore) + return; + + const InputFile *file = sym->file; + auto *d = dyn_cast(sym); + + auto report = [&](StringRef s) { warn(toString(file) + s + sym->getName()); }; + + if (sym->isUndefined()) + report(": unable to order undefined symbol: "); + else if (sym->isShared()) + report(": unable to order shared symbol: "); + else if (d && !d->section) + report(": unable to order absolute symbol: "); + else if (d && isa(d->section)) + report(": unable to order synthetic symbol: "); + else if (d && !d->section->repl->isLive()) + report(": unable to order discarded symbol: "); +} + +static uint8_t getMinVisibility(uint8_t va, uint8_t vb) { + if (va == STV_DEFAULT) + return vb; + if (vb == STV_DEFAULT) + return va; + return std::min(va, vb); +} + +// Merge symbol properties. +// +// When we have many symbols of the same name, we choose one of them, +// and that's the result of symbol resolution. However, symbols that +// were not chosen still affect some symbol properties. +void Symbol::mergeProperties(const Symbol &other) { + if (other.exportDynamic) + exportDynamic = true; + if (other.isUsedInRegularObj) + isUsedInRegularObj = true; + + // DSO symbols do not affect visibility in the output. + if (!other.isShared()) + visibility = getMinVisibility(visibility, other.visibility); +} + +void Symbol::resolve(const Symbol &other) { + mergeProperties(other); + + if (isPlaceholder()) { + replace(other); + return; + } + + switch (other.kind()) { + case Symbol::UndefinedKind: + resolveUndefined(cast(other)); + break; + case Symbol::CommonKind: + resolveCommon(cast(other)); + break; + case Symbol::DefinedKind: + resolveDefined(cast(other)); + break; + case Symbol::LazyArchiveKind: + resolveLazy(cast(other)); + break; + case Symbol::LazyObjectKind: + resolveLazy(cast(other)); + break; + case Symbol::SharedKind: + resolveShared(cast(other)); + break; + case Symbol::PlaceholderKind: + llvm_unreachable("bad symbol kind"); + } +} + +void Symbol::resolveUndefined(const Undefined &other) { + // An undefined symbol with non default visibility must be satisfied + // in the same DSO. + // + // If this is a non-weak defined symbol in a discarded section, override the + // existing undefined symbol for better error message later. + if ((isShared() && other.visibility != STV_DEFAULT) || + (isUndefined() && other.binding != STB_WEAK && other.discardedSecIdx)) { + replace(other); + return; + } + + if (traced) + printTraceSymbol(&other); + + if (isLazy()) { + // An undefined weak will not fetch archive members. See comment on Lazy in + // Symbols.h for the details. + if (other.binding == STB_WEAK) { + binding = STB_WEAK; + type = other.type; + return; + } + + // Do extra check for --warn-backrefs. + // + // --warn-backrefs is an option to prevent an undefined reference from + // fetching an archive member written earlier in the command line. It can be + // used to keep compatibility with GNU linkers to some degree. + // I'll explain the feature and why you may find it useful in this comment. + // + // lld's symbol resolution semantics is more relaxed than traditional Unix + // linkers. For example, + // + // ld.lld foo.a bar.o + // + // succeeds even if bar.o contains an undefined symbol that has to be + // resolved by some object file in foo.a. Traditional Unix linkers don't + // allow this kind of backward reference, as they visit each file only once + // from left to right in the command line while resolving all undefined + // symbols at the moment of visiting. + // + // In the above case, since there's no undefined symbol when a linker visits + // foo.a, no files are pulled out from foo.a, and because the linker forgets + // about foo.a after visiting, it can't resolve undefined symbols in bar.o + // that could have been resolved otherwise. + // + // That lld accepts more relaxed form means that (besides it'd make more + // sense) you can accidentally write a command line or a build file that + // works only with lld, even if you have a plan to distribute it to wider + // users who may be using GNU linkers. With --warn-backrefs, you can detect + // a library order that doesn't work with other Unix linkers. + // + // The option is also useful to detect cyclic dependencies between static + // archives. Again, lld accepts + // + // ld.lld foo.a bar.a + // + // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is + // handled as an error. + // + // Here is how the option works. We assign a group ID to each file. A file + // with a smaller group ID can pull out object files from an archive file + // with an equal or greater group ID. Otherwise, it is a reverse dependency + // and an error. + // + // A file outside --{start,end}-group gets a fresh ID when instantiated. All + // files within the same --{start,end}-group get the same group ID. E.g. + // + // ld.lld A B --start-group C D --end-group E + // + // A forms group 0. B form group 1. C and D (including their member object + // files) form group 2. E forms group 3. I think that you can see how this + // group assignment rule simulates the traditional linker's semantics. + bool backref = config->warnBackrefs && other.file && + file->groupId < other.file->groupId; + fetch(); + + // We don't report backward references to weak symbols as they can be + // overridden later. + if (backref && !isWeak()) + warn("backward reference detected: " + other.getName() + " in " + + toString(other.file) + " refers to " + toString(file)); + return; + } + + // Undefined symbols in a SharedFile do not change the binding. + if (dyn_cast_or_null(other.file)) + return; + + if (isUndefined()) { + // The binding may "upgrade" from weak to non-weak. + if (other.binding != STB_WEAK) + binding = other.binding; + } else if (auto *s = dyn_cast(this)) { + // The binding of a SharedSymbol will be weak if there is at least one + // reference and all are weak. The binding has one opportunity to change to + // weak: if the first reference is weak. + if (other.binding != STB_WEAK || !s->referenced) + binding = other.binding; + s->referenced = true; + } +} + +// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and +// foo@@VER. We want to effectively ignore foo, so give precedence to +// foo@@VER. +// FIXME: If users can transition to using +// .symver foo,foo@@@VER +// we can delete this hack. +static int compareVersion(StringRef a, StringRef b) { + bool x = a.contains("@@"); + bool y = b.contains("@@"); + if (!x && y) + return 1; + if (x && !y) + return -1; + return 0; +} + +// Compare two symbols. Return 1 if the new symbol should win, -1 if +// the new symbol should lose, or 0 if there is a conflict. +int Symbol::compare(const Symbol *other) const { + assert(other->isDefined() || other->isCommon()); + + if (!isDefined() && !isCommon()) + return 1; + + if (int cmp = compareVersion(getName(), other->getName())) + return cmp; + + if (other->isWeak()) + return -1; + + if (isWeak()) + return 1; + + if (isCommon() && other->isCommon()) { + if (config->warnCommon) + warn("multiple common of " + getName()); + return 0; + } + + if (isCommon()) { + if (config->warnCommon) + warn("common " + getName() + " is overridden"); + return 1; + } + + if (other->isCommon()) { + if (config->warnCommon) + warn("common " + getName() + " is overridden"); + return -1; + } + + auto *oldSym = cast(this); + auto *newSym = cast(other); + + if (other->file && isa(other->file)) + return 0; + + if (!oldSym->section && !newSym->section && oldSym->value == newSym->value && + newSym->binding == STB_GLOBAL) + return -1; + + return 0; +} + +static void reportDuplicate(Symbol *sym, InputFile *newFile, + InputSectionBase *errSec, uint64_t errOffset) { + if (config->allowMultipleDefinition) + return; + + Defined *d = cast(sym); + if (!d->section || !errSec) { + error("duplicate symbol: " + toString(*sym) + "\n>>> defined in " + + toString(sym->file) + "\n>>> defined in " + toString(newFile)); + return; + } + + // Construct and print an error message in the form of: + // + // ld.lld: error: duplicate symbol: foo + // >>> defined at bar.c:30 + // >>> bar.o (/home/alice/src/bar.o) + // >>> defined at baz.c:563 + // >>> baz.o in archive libbaz.a + auto *sec1 = cast(d->section); + std::string src1 = sec1->getSrcMsg(*sym, d->value); + std::string obj1 = sec1->getObjMsg(d->value); + std::string src2 = errSec->getSrcMsg(*sym, errOffset); + std::string obj2 = errSec->getObjMsg(errOffset); + + std::string msg = "duplicate symbol: " + toString(*sym) + "\n>>> defined at "; + if (!src1.empty()) + msg += src1 + "\n>>> "; + msg += obj1 + "\n>>> defined at "; + if (!src2.empty()) + msg += src2 + "\n>>> "; + msg += obj2; + error(msg); +} + +void Symbol::resolveCommon(const CommonSymbol &other) { + int cmp = compare(&other); + if (cmp < 0) + return; + + if (cmp > 0) { + replace(other); + return; + } + + CommonSymbol *oldSym = cast(this); + + oldSym->alignment = std::max(oldSym->alignment, other.alignment); + if (oldSym->size < other.size) { + oldSym->file = other.file; + oldSym->size = other.size; + } +} + +void Symbol::resolveDefined(const Defined &other) { + int cmp = compare(&other); + if (cmp > 0) + replace(other); + else if (cmp == 0) + reportDuplicate(this, other.file, + dyn_cast_or_null(other.section), + other.value); +} + +template void Symbol::resolveLazy(const LazyT &other) { + if (!isUndefined()) + return; + + // An undefined weak will not fetch archive members. See comment on Lazy in + // Symbols.h for the details. + if (isWeak()) { + uint8_t ty = type; + replace(other); + type = ty; + binding = STB_WEAK; + return; + } + + other.fetch(); +} + +void Symbol::resolveShared(const SharedSymbol &other) { + if (visibility == STV_DEFAULT && (isUndefined() || isLazy())) { + // An undefined symbol with non default visibility must be satisfied + // in the same DSO. + uint8_t bind = binding; + replace(other); + binding = bind; + cast(this)->referenced = true; + } +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/Symbols.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Symbols.h =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Symbols.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Symbols.h (revision 352529) @@ -0,0 +1,558 @@ +//===- Symbols.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 +// +//===----------------------------------------------------------------------===// +// +// This file defines various types of Symbols. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_SYMBOLS_H +#define LLD_ELF_SYMBOLS_H + +#include "InputFiles.h" +#include "InputSection.h" +#include "lld/Common/LLVM.h" +#include "lld/Common/Strings.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ELF.h" + +namespace lld { +namespace elf { +class CommonSymbol; +class Defined; +class InputFile; +class LazyArchive; +class LazyObject; +class SharedSymbol; +class Symbol; +class Undefined; +} // namespace elf + +std::string toString(const elf::Symbol &); + +// There are two different ways to convert an Archive::Symbol to a string: +// One for Microsoft name mangling and one for Itanium name mangling. +// Call the functions toCOFFString and toELFString, not just toString. +std::string toELFString(const elf::Archive::Symbol &); + +namespace elf { + +// This is a StringRef-like container that doesn't run strlen(). +// +// ELF string tables contain a lot of null-terminated strings. Most of them +// are not necessary for the linker because they are names of local symbols, +// and the linker doesn't use local symbol names for name resolution. So, we +// use this class to represents strings read from string tables. +struct StringRefZ { + StringRefZ(const char *s) : data(s), size(-1) {} + StringRefZ(StringRef s) : data(s.data()), size(s.size()) {} + + const char *data; + const uint32_t size; +}; + +// The base class for real symbol classes. +class Symbol { +public: + enum Kind { + PlaceholderKind, + DefinedKind, + CommonKind, + SharedKind, + UndefinedKind, + LazyArchiveKind, + LazyObjectKind, + }; + + Kind kind() const { return static_cast(symbolKind); } + + // The file from which this symbol was created. + InputFile *file; + +protected: + const char *nameData; + mutable uint32_t nameSize; + +public: + uint32_t dynsymIndex = 0; + uint32_t gotIndex = -1; + uint32_t pltIndex = -1; + + uint32_t globalDynIndex = -1; + + // This field is a index to the symbol's version definition. + uint32_t verdefIndex = -1; + + // Version definition index. + uint16_t versionId; + + // An index into the .branch_lt section on PPC64. + uint16_t ppc64BranchltIndex = -1; + + // Symbol binding. This is not overwritten by replace() to track + // changes during resolution. In particular: + // - An undefined weak is still weak when it resolves to a shared library. + // - An undefined weak will not fetch archive members, but we have to + // remember it is weak. + uint8_t binding; + + // The following fields have the same meaning as the ELF symbol attributes. + uint8_t type; // symbol type + uint8_t stOther; // st_other field value + + uint8_t symbolKind; + + // Symbol visibility. This is the computed minimum visibility of all + // observed non-DSO symbols. + unsigned visibility : 2; + + // True if the symbol was used for linking and thus need to be added to the + // output file's symbol table. This is true for all symbols except for + // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that + // are unreferenced except by other bitcode objects. + unsigned isUsedInRegularObj : 1; + + // If this flag is true and the symbol has protected or default visibility, it + // will appear in .dynsym. This flag is set by interposable DSO symbols in + // executables, by most symbols in DSOs and executables built with + // --export-dynamic, and by dynamic lists. + unsigned exportDynamic : 1; + + // False if LTO shouldn't inline whatever this symbol points to. If a symbol + // is overwritten after LTO, LTO shouldn't inline the symbol because it + // doesn't know the final contents of the symbol. + unsigned canInline : 1; + + // True if this symbol is specified by --trace-symbol option. + unsigned traced : 1; + + inline void replace(const Symbol &New); + + bool includeInDynsym() const; + uint8_t computeBinding() const; + bool isWeak() const { return binding == llvm::ELF::STB_WEAK; } + + bool isUndefined() const { return symbolKind == UndefinedKind; } + bool isCommon() const { return symbolKind == CommonKind; } + bool isDefined() const { return symbolKind == DefinedKind; } + bool isShared() const { return symbolKind == SharedKind; } + bool isPlaceholder() const { return symbolKind == PlaceholderKind; } + + bool isLocal() const { return binding == llvm::ELF::STB_LOCAL; } + + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } + + // True if this is an undefined weak symbol. This only works once + // all input files have been added. + bool isUndefWeak() const { + // See comment on lazy symbols for details. + return isWeak() && (isUndefined() || isLazy()); + } + + StringRef getName() const { + if (nameSize == (uint32_t)-1) + nameSize = strlen(nameData); + return {nameData, nameSize}; + } + + void setName(StringRef s) { + nameData = s.data(); + nameSize = s.size(); + } + + void parseSymbolVersion(); + + bool isInGot() const { return gotIndex != -1U; } + bool isInPlt() const { return pltIndex != -1U; } + bool isInPPC64Branchlt() const { return ppc64BranchltIndex != 0xffff; } + + uint64_t getVA(int64_t addend = 0) const; + + uint64_t getGotOffset() const; + uint64_t getGotVA() const; + uint64_t getGotPltOffset() const; + uint64_t getGotPltVA() const; + uint64_t getPltVA() const; + uint64_t getPPC64LongBranchTableVA() const; + uint64_t getPPC64LongBranchOffset() const; + uint64_t getSize() const; + OutputSection *getOutputSection() const; + + // The following two functions are used for symbol resolution. + // + // You are expected to call mergeProperties for all symbols in input + // files so that attributes that are attached to names rather than + // indivisual symbol (such as visibility) are merged together. + // + // Every time you read a new symbol from an input, you are supposed + // to call resolve() with the new symbol. That function replaces + // "this" object as a result of name resolution if the new symbol is + // more appropriate to be included in the output. + // + // For example, if "this" is an undefined symbol and a new symbol is + // a defined symbol, "this" is replaced with the new symbol. + void mergeProperties(const Symbol &other); + void resolve(const Symbol &other); + + // If this is a lazy symbol, fetch an input file and add the symbol + // in the file to the symbol table. Calling this function on + // non-lazy object causes a runtime error. + void fetch() const; + +private: + static bool isExportDynamic(Kind k, uint8_t visibility) { + if (k == SharedKind) + return visibility == llvm::ELF::STV_DEFAULT; + return config->shared || config->exportDynamic; + } + + void resolveUndefined(const Undefined &other); + void resolveCommon(const CommonSymbol &other); + void resolveDefined(const Defined &other); + template void resolveLazy(const LazyT &other); + void resolveShared(const SharedSymbol &other); + + int compare(const Symbol *other) const; + + inline size_t getSymbolSize() const; + +protected: + Symbol(Kind k, InputFile *file, StringRefZ name, uint8_t binding, + uint8_t stOther, uint8_t type) + : file(file), nameData(name.data), nameSize(name.size), binding(binding), + type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3), + isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind), + exportDynamic(isExportDynamic(k, visibility)), canInline(false), + traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false), + isPreemptible(false), used(!config->gcSections), needsTocRestore(false), + scriptDefined(false) {} + +public: + // True the symbol should point to its PLT entry. + // For SharedSymbol only. + unsigned needsPltAddr : 1; + + // True if this symbol is in the Iplt sub-section of the Plt and the Igot + // sub-section of the .got.plt or .got. + unsigned isInIplt : 1; + + // True if this symbol needs a GOT entry and its GOT entry is actually in + // Igot. This will be true only for certain non-preemptible ifuncs. + unsigned gotInIgot : 1; + + // True if this symbol is preemptible at load time. + unsigned isPreemptible : 1; + + // True if an undefined or shared symbol is used from a live section. + unsigned used : 1; + + // True if a call to this symbol needs to be followed by a restore of the + // PPC64 toc pointer. + unsigned needsTocRestore : 1; + + // True if this symbol is defined by a linker script. + unsigned scriptDefined : 1; + + // The partition whose dynamic symbol table contains this symbol's definition. + uint8_t partition = 1; + + bool isSection() const { return type == llvm::ELF::STT_SECTION; } + bool isTls() const { return type == llvm::ELF::STT_TLS; } + bool isFunc() const { return type == llvm::ELF::STT_FUNC; } + bool isGnuIFunc() const { return type == llvm::ELF::STT_GNU_IFUNC; } + bool isObject() const { return type == llvm::ELF::STT_OBJECT; } + bool isFile() const { return type == llvm::ELF::STT_FILE; } +}; + +// Represents a symbol that is defined in the current output file. +class Defined : public Symbol { +public: + Defined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther, + uint8_t type, uint64_t value, uint64_t size, SectionBase *section) + : Symbol(DefinedKind, file, name, binding, stOther, type), value(value), + size(size), section(section) {} + + static bool classof(const Symbol *s) { return s->isDefined(); } + + uint64_t value; + uint64_t size; + SectionBase *section; +}; + +// Represents a common symbol. +// +// On Unix, it is traditionally allowed to write variable definitions +// without initialization expressions (such as "int foo;") to header +// files. Such definition is called "tentative definition". +// +// Using tentative definition is usually considered a bad practice +// because you should write only declarations (such as "extern int +// foo;") to header files. Nevertheless, the linker and the compiler +// have to do something to support bad code by allowing duplicate +// definitions for this particular case. +// +// Common symbols represent variable definitions without initializations. +// The compiler creates common symbols when it sees varaible definitions +// without initialization (you can suppress this behavior and let the +// compiler create a regular defined symbol by -fno-common). +// +// The linker allows common symbols to be replaced by regular defined +// symbols. If there are remaining common symbols after name resolution is +// complete, they are converted to regular defined symbols in a .bss +// section. (Therefore, the later passes don't see any CommonSymbols.) +class CommonSymbol : public Symbol { +public: + CommonSymbol(InputFile *file, StringRefZ name, uint8_t binding, + uint8_t stOther, uint8_t type, uint64_t alignment, uint64_t size) + : Symbol(CommonKind, file, name, binding, stOther, type), + alignment(alignment), size(size) {} + + static bool classof(const Symbol *s) { return s->isCommon(); } + + uint32_t alignment; + uint64_t size; +}; + +class Undefined : public Symbol { +public: + Undefined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther, + uint8_t type, uint32_t discardedSecIdx = 0) + : Symbol(UndefinedKind, file, name, binding, stOther, type), + discardedSecIdx(discardedSecIdx) {} + + static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; } + + // The section index if in a discarded section, 0 otherwise. + uint32_t discardedSecIdx; +}; + +class SharedSymbol : public Symbol { +public: + static bool classof(const Symbol *s) { return s->kind() == SharedKind; } + + SharedSymbol(InputFile &file, StringRef name, uint8_t binding, + uint8_t stOther, uint8_t type, uint64_t value, uint64_t size, + uint32_t alignment, uint32_t verdefIndex) + : Symbol(SharedKind, &file, name, binding, stOther, type), value(value), + size(size), alignment(alignment) { + this->verdefIndex = verdefIndex; + // GNU ifunc is a mechanism to allow user-supplied functions to + // resolve PLT slot values at load-time. This is contrary to the + // regular symbol resolution scheme in which symbols are resolved just + // by name. Using this hook, you can program how symbols are solved + // for you program. For example, you can make "memcpy" to be resolved + // to a SSE-enabled version of memcpy only when a machine running the + // program supports the SSE instruction set. + // + // Naturally, such symbols should always be called through their PLT + // slots. What GNU ifunc symbols point to are resolver functions, and + // calling them directly doesn't make sense (unless you are writing a + // loader). + // + // For DSO symbols, we always call them through PLT slots anyway. + // So there's no difference between GNU ifunc and regular function + // symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC. + if (this->type == llvm::ELF::STT_GNU_IFUNC) + this->type = llvm::ELF::STT_FUNC; + } + + SharedFile &getFile() const { return *cast(file); } + + uint64_t value; // st_value + uint64_t size; // st_size + uint32_t alignment; + + // This is true if there has been at least one undefined reference to the + // symbol. The binding may change to STB_WEAK if the first undefined reference + // is weak. + bool referenced = false; +}; + +// LazyArchive and LazyObject represent a symbols that is not yet in the link, +// but we know where to find it if needed. If the resolver finds both Undefined +// and Lazy for the same name, it will ask the Lazy to load a file. +// +// A special complication is the handling of weak undefined symbols. They should +// not load a file, but we have to remember we have seen both the weak undefined +// and the lazy. We represent that with a lazy symbol with a weak binding. This +// means that code looking for undefined symbols normally also has to take lazy +// symbols into consideration. + +// This class represents a symbol defined in an archive file. It is +// created from an archive file header, and it knows how to load an +// object file from an archive to replace itself with a defined +// symbol. +class LazyArchive : public Symbol { +public: + LazyArchive(InputFile &file, const llvm::object::Archive::Symbol s) + : Symbol(LazyArchiveKind, &file, s.getName(), llvm::ELF::STB_GLOBAL, + llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE), + sym(s) {} + + static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; } + + MemoryBufferRef getMemberBuffer(); + + const llvm::object::Archive::Symbol sym; +}; + +// LazyObject symbols represents symbols in object files between +// --start-lib and --end-lib options. +class LazyObject : public Symbol { +public: + LazyObject(InputFile &file, StringRef name) + : Symbol(LazyObjectKind, &file, name, llvm::ELF::STB_GLOBAL, + llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE) {} + + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } +}; + +// Some linker-generated symbols need to be created as +// Defined symbols. +struct ElfSym { + // __bss_start + static Defined *bss; + + // etext and _etext + static Defined *etext1; + static Defined *etext2; + + // edata and _edata + static Defined *edata1; + static Defined *edata2; + + // end and _end + static Defined *end1; + static Defined *end2; + + // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to + // be at some offset from the base of the .got section, usually 0 or + // the end of the .got. + static Defined *globalOffsetTable; + + // _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS. + static Defined *mipsGp; + static Defined *mipsGpDisp; + static Defined *mipsLocalGp; + + // __rel{,a}_iplt_{start,end} symbols. + static Defined *relaIpltStart; + static Defined *relaIpltEnd; + + // __global_pointer$ for RISC-V. + static Defined *riscvGlobalPointer; + + // _TLS_MODULE_BASE_ on targets that support TLSDESC. + static Defined *tlsModuleBase; +}; + +// A buffer class that is large enough to hold any Symbol-derived +// object. We allocate memory using this class and instantiate a symbol +// using the placement new. +union SymbolUnion { + alignas(Defined) char a[sizeof(Defined)]; + alignas(CommonSymbol) char b[sizeof(CommonSymbol)]; + alignas(Undefined) char c[sizeof(Undefined)]; + alignas(SharedSymbol) char d[sizeof(SharedSymbol)]; + alignas(LazyArchive) char e[sizeof(LazyArchive)]; + alignas(LazyObject) char f[sizeof(LazyObject)]; +}; + +// It is important to keep the size of SymbolUnion small for performance and +// memory usage reasons. 80 bytes is a soft limit based on the size of Defined +// on a 64-bit system. +static_assert(sizeof(SymbolUnion) <= 80, "SymbolUnion too large"); + +template struct AssertSymbol { + static_assert(std::is_trivially_destructible(), + "Symbol types must be trivially destructible"); + static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small"); + static_assert(alignof(T) <= alignof(SymbolUnion), + "SymbolUnion not aligned enough"); +}; + +static inline void assertSymbols() { + AssertSymbol(); + AssertSymbol(); + AssertSymbol(); + AssertSymbol(); + AssertSymbol(); + AssertSymbol(); +} + +void printTraceSymbol(const Symbol *sym); + +size_t Symbol::getSymbolSize() const { + switch (kind()) { + case CommonKind: + return sizeof(CommonSymbol); + case DefinedKind: + return sizeof(Defined); + case LazyArchiveKind: + return sizeof(LazyArchive); + case LazyObjectKind: + return sizeof(LazyObject); + case SharedKind: + return sizeof(SharedSymbol); + case UndefinedKind: + return sizeof(Undefined); + case PlaceholderKind: + return sizeof(Symbol); + } + llvm_unreachable("unknown symbol kind"); +} + +// replace() replaces "this" object with a given symbol by memcpy'ing +// it over to "this". This function is called as a result of name +// resolution, e.g. to replace an undefind symbol with a defined symbol. +void Symbol::replace(const Symbol &New) { + using llvm::ELF::STT_TLS; + + // Symbols representing thread-local variables must be referenced by + // TLS-aware relocations, and non-TLS symbols must be reference by + // non-TLS relocations, so there's a clear distinction between TLS + // and non-TLS symbols. It is an error if the same symbol is defined + // as a TLS symbol in one file and as a non-TLS symbol in other file. + if (symbolKind != PlaceholderKind && !isLazy() && !New.isLazy()) { + bool tlsMismatch = (type == STT_TLS && New.type != STT_TLS) || + (type != STT_TLS && New.type == STT_TLS); + if (tlsMismatch) + error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " + + toString(New.file) + "\n>>> defined in " + toString(file)); + } + + Symbol old = *this; + memcpy(this, &New, New.getSymbolSize()); + + versionId = old.versionId; + visibility = old.visibility; + isUsedInRegularObj = old.isUsedInRegularObj; + exportDynamic = old.exportDynamic; + canInline = old.canInline; + traced = old.traced; + isPreemptible = old.isPreemptible; + scriptDefined = old.scriptDefined; + partition = old.partition; + + // Symbol length is computed lazily. If we already know a symbol length, + // propagate it. + if (nameData == old.nameData && nameSize == 0 && old.nameSize != 0) + nameSize = old.nameSize; + + // Print out a log message if --trace-symbol was specified. + // This is for debugging. + if (traced) + printTraceSymbol(this); +} + +void maybeWarnUnorderableSymbol(const Symbol *sym); +} // namespace elf +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/ELF/Symbols.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/SyntheticSections.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/SyntheticSections.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/SyntheticSections.cpp (revision 352529) @@ -0,0 +1,3630 @@ +//===- SyntheticSections.cpp ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains linker-synthesized sections. Currently, +// synthetic sections are created either output sections or input sections, +// but we are rewriting code so that all synthetic sections are created as +// input sections. +// +//===----------------------------------------------------------------------===// + +#include "SyntheticSections.h" +#include "Config.h" +#include "InputFiles.h" +#include "LinkerScript.h" +#include "OutputSections.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Target.h" +#include "Writer.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "lld/Common/Threads.h" +#include "lld/Common/Version.h" +#include "llvm/ADT/SetOperations.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MD5.h" +#include +#include + +using namespace llvm; +using namespace llvm::dwarf; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::support; + +using namespace lld; +using namespace lld::elf; + +using llvm::support::endian::read32le; +using llvm::support::endian::write32le; +using llvm::support::endian::write64le; + +constexpr size_t MergeNoTailSection::numShards; + +static uint64_t readUint(uint8_t *buf) { + return config->is64 ? read64(buf) : read32(buf); +} + +static void writeUint(uint8_t *buf, uint64_t val) { + if (config->is64) + write64(buf, val); + else + write32(buf, val); +} + +// Returns an LLD version string. +static ArrayRef getVersion() { + // Check LLD_VERSION first for ease of testing. + // You can get consistent output by using the environment variable. + // This is only for testing. + StringRef s = getenv("LLD_VERSION"); + if (s.empty()) + s = saver.save(Twine("Linker: ") + getLLDVersion()); + + // +1 to include the terminating '\0'. + return {(const uint8_t *)s.data(), s.size() + 1}; +} + +// Creates a .comment section containing LLD version info. +// With this feature, you can identify LLD-generated binaries easily +// by "readelf --string-dump .comment ". +// The returned object is a mergeable string section. +MergeInputSection *elf::createCommentSection() { + return make(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1, + getVersion(), ".comment"); +} + +// .MIPS.abiflags section. +template +MipsAbiFlagsSection::MipsAbiFlagsSection(Elf_Mips_ABIFlags flags) + : SyntheticSection(SHF_ALLOC, SHT_MIPS_ABIFLAGS, 8, ".MIPS.abiflags"), + flags(flags) { + this->entsize = sizeof(Elf_Mips_ABIFlags); +} + +template void MipsAbiFlagsSection::writeTo(uint8_t *buf) { + memcpy(buf, &flags, sizeof(flags)); +} + +template +MipsAbiFlagsSection *MipsAbiFlagsSection::create() { + Elf_Mips_ABIFlags flags = {}; + bool create = false; + + for (InputSectionBase *sec : inputSections) { + if (sec->type != SHT_MIPS_ABIFLAGS) + continue; + sec->markDead(); + create = true; + + std::string filename = toString(sec->file); + const size_t size = sec->data().size(); + // Older version of BFD (such as the default FreeBSD linker) concatenate + // .MIPS.abiflags instead of merging. To allow for this case (or potential + // zero padding) we ignore everything after the first Elf_Mips_ABIFlags + if (size < sizeof(Elf_Mips_ABIFlags)) { + error(filename + ": invalid size of .MIPS.abiflags section: got " + + Twine(size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags))); + return nullptr; + } + auto *s = reinterpret_cast(sec->data().data()); + if (s->version != 0) { + error(filename + ": unexpected .MIPS.abiflags version " + + Twine(s->version)); + return nullptr; + } + + // LLD checks ISA compatibility in calcMipsEFlags(). Here we just + // select the highest number of ISA/Rev/Ext. + flags.isa_level = std::max(flags.isa_level, s->isa_level); + flags.isa_rev = std::max(flags.isa_rev, s->isa_rev); + flags.isa_ext = std::max(flags.isa_ext, s->isa_ext); + flags.gpr_size = std::max(flags.gpr_size, s->gpr_size); + flags.cpr1_size = std::max(flags.cpr1_size, s->cpr1_size); + flags.cpr2_size = std::max(flags.cpr2_size, s->cpr2_size); + flags.ases |= s->ases; + flags.flags1 |= s->flags1; + flags.flags2 |= s->flags2; + flags.fp_abi = elf::getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename); + }; + + if (create) + return make>(flags); + return nullptr; +} + +// .MIPS.options section. +template +MipsOptionsSection::MipsOptionsSection(Elf_Mips_RegInfo reginfo) + : SyntheticSection(SHF_ALLOC, SHT_MIPS_OPTIONS, 8, ".MIPS.options"), + reginfo(reginfo) { + this->entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo); +} + +template void MipsOptionsSection::writeTo(uint8_t *buf) { + auto *options = reinterpret_cast(buf); + options->kind = ODK_REGINFO; + options->size = getSize(); + + if (!config->relocatable) + reginfo.ri_gp_value = in.mipsGot->getGp(); + memcpy(buf + sizeof(Elf_Mips_Options), ®info, sizeof(reginfo)); +} + +template +MipsOptionsSection *MipsOptionsSection::create() { + // N64 ABI only. + if (!ELFT::Is64Bits) + return nullptr; + + std::vector sections; + for (InputSectionBase *sec : inputSections) + if (sec->type == SHT_MIPS_OPTIONS) + sections.push_back(sec); + + if (sections.empty()) + return nullptr; + + Elf_Mips_RegInfo reginfo = {}; + for (InputSectionBase *sec : sections) { + sec->markDead(); + + std::string filename = toString(sec->file); + ArrayRef d = sec->data(); + + while (!d.empty()) { + if (d.size() < sizeof(Elf_Mips_Options)) { + error(filename + ": invalid size of .MIPS.options section"); + break; + } + + auto *opt = reinterpret_cast(d.data()); + if (opt->kind == ODK_REGINFO) { + reginfo.ri_gprmask |= opt->getRegInfo().ri_gprmask; + sec->getFile()->mipsGp0 = opt->getRegInfo().ri_gp_value; + break; + } + + if (!opt->size) + fatal(filename + ": zero option descriptor size"); + d = d.slice(opt->size); + } + }; + + return make>(reginfo); +} + +// MIPS .reginfo section. +template +MipsReginfoSection::MipsReginfoSection(Elf_Mips_RegInfo reginfo) + : SyntheticSection(SHF_ALLOC, SHT_MIPS_REGINFO, 4, ".reginfo"), + reginfo(reginfo) { + this->entsize = sizeof(Elf_Mips_RegInfo); +} + +template void MipsReginfoSection::writeTo(uint8_t *buf) { + if (!config->relocatable) + reginfo.ri_gp_value = in.mipsGot->getGp(); + memcpy(buf, ®info, sizeof(reginfo)); +} + +template +MipsReginfoSection *MipsReginfoSection::create() { + // Section should be alive for O32 and N32 ABIs only. + if (ELFT::Is64Bits) + return nullptr; + + std::vector sections; + for (InputSectionBase *sec : inputSections) + if (sec->type == SHT_MIPS_REGINFO) + sections.push_back(sec); + + if (sections.empty()) + return nullptr; + + Elf_Mips_RegInfo reginfo = {}; + for (InputSectionBase *sec : sections) { + sec->markDead(); + + if (sec->data().size() != sizeof(Elf_Mips_RegInfo)) { + error(toString(sec->file) + ": invalid size of .reginfo section"); + return nullptr; + } + + auto *r = reinterpret_cast(sec->data().data()); + reginfo.ri_gprmask |= r->ri_gprmask; + sec->getFile()->mipsGp0 = r->ri_gp_value; + }; + + return make>(reginfo); +} + +InputSection *elf::createInterpSection() { + // StringSaver guarantees that the returned string ends with '\0'. + StringRef s = saver.save(config->dynamicLinker); + ArrayRef contents = {(const uint8_t *)s.data(), s.size() + 1}; + + auto *sec = make(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents, + ".interp"); + sec->markLive(); + return sec; +} + +Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value, + uint64_t size, InputSectionBase §ion) { + auto *s = make(section.file, name, STB_LOCAL, STV_DEFAULT, type, + value, size, §ion); + if (in.symTab) + in.symTab->addSymbol(s); + return s; +} + +static size_t getHashSize() { + switch (config->buildId) { + case BuildIdKind::Fast: + return 8; + case BuildIdKind::Md5: + case BuildIdKind::Uuid: + return 16; + case BuildIdKind::Sha1: + return 20; + case BuildIdKind::Hexstring: + return config->buildIdVector.size(); + default: + llvm_unreachable("unknown BuildIdKind"); + } +} + +// This class represents a linker-synthesized .note.gnu.property section. +// +// In x86 and AArch64, object files may contain feature flags indicating the +// features that they have used. The flags are stored in a .note.gnu.property +// section. +// +// lld reads the sections from input files and merges them by computing AND of +// the flags. The result is written as a new .note.gnu.property section. +// +// If the flag is zero (which indicates that the intersection of the feature +// sets is empty, or some input files didn't have .note.gnu.property sections), +// we don't create this section. +GnuPropertySection::GnuPropertySection() + : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, 4, + ".note.gnu.property") {} + +void GnuPropertySection::writeTo(uint8_t *buf) { + uint32_t featureAndType = config->emachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + + write32(buf, 4); // Name size + write32(buf + 4, config->is64 ? 16 : 12); // Content size + write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type + memcpy(buf + 12, "GNU", 4); // Name string + write32(buf + 16, featureAndType); // Feature type + write32(buf + 20, 4); // Feature size + write32(buf + 24, config->andFeatures); // Feature flags + if (config->is64) + write32(buf + 28, 0); // Padding +} + +size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; } + +BuildIdSection::BuildIdSection() + : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), + hashSize(getHashSize()) {} + +void BuildIdSection::writeTo(uint8_t *buf) { + write32(buf, 4); // Name size + write32(buf + 4, hashSize); // Content size + write32(buf + 8, NT_GNU_BUILD_ID); // Type + memcpy(buf + 12, "GNU", 4); // Name string + hashBuf = buf + 16; +} + +void BuildIdSection::writeBuildId(ArrayRef buf) { + assert(buf.size() == hashSize); + memcpy(hashBuf, buf.data(), hashSize); +} + +BssSection::BssSection(StringRef name, uint64_t size, uint32_t alignment) + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, alignment, name) { + this->bss = true; + this->size = size; +} + +EhFrameSection::EhFrameSection() + : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame") {} + +// Search for an existing CIE record or create a new one. +// CIE records from input object files are uniquified by their contents +// and where their relocations point to. +template +CieRecord *EhFrameSection::addCie(EhSectionPiece &cie, ArrayRef rels) { + Symbol *personality = nullptr; + unsigned firstRelI = cie.firstRelocation; + if (firstRelI != (unsigned)-1) + personality = + &cie.sec->template getFile()->getRelocTargetSym(rels[firstRelI]); + + // Search for an existing CIE by CIE contents/relocation target pair. + CieRecord *&rec = cieMap[{cie.data(), personality}]; + + // If not found, create a new one. + if (!rec) { + rec = make(); + rec->cie = &cie; + cieRecords.push_back(rec); + } + return rec; +} + +// There is one FDE per function. Returns true if a given FDE +// points to a live function. +template +bool EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef rels) { + auto *sec = cast(fde.sec); + unsigned firstRelI = fde.firstRelocation; + + // An FDE should point to some function because FDEs are to describe + // functions. That's however not always the case due to an issue of + // ld.gold with -r. ld.gold may discard only functions and leave their + // corresponding FDEs, which results in creating bad .eh_frame sections. + // To deal with that, we ignore such FDEs. + if (firstRelI == (unsigned)-1) + return false; + + const RelTy &rel = rels[firstRelI]; + Symbol &b = sec->template getFile()->getRelocTargetSym(rel); + + // FDEs for garbage-collected or merged-by-ICF sections, or sections in + // another partition, are dead. + if (auto *d = dyn_cast(&b)) + if (SectionBase *sec = d->section) + return sec->partition == partition; + return false; +} + +// .eh_frame is a sequence of CIE or FDE records. In general, there +// is one CIE record per input object file which is followed by +// a list of FDEs. This function searches an existing CIE or create a new +// one and associates FDEs to the CIE. +template +void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef rels) { + offsetToCie.clear(); + for (EhSectionPiece &piece : sec->pieces) { + // The empty record is the end marker. + if (piece.size == 4) + return; + + size_t offset = piece.inputOff; + uint32_t id = read32(piece.data().data() + 4); + if (id == 0) { + offsetToCie[offset] = addCie(piece, rels); + continue; + } + + uint32_t cieOffset = offset + 4 - id; + CieRecord *rec = offsetToCie[cieOffset]; + if (!rec) + fatal(toString(sec) + ": invalid CIE reference"); + + if (!isFdeLive(piece, rels)) + continue; + rec->fdes.push_back(&piece); + numFdes++; + } +} + +template void EhFrameSection::addSection(InputSectionBase *c) { + auto *sec = cast(c); + sec->parent = this; + + alignment = std::max(alignment, sec->alignment); + sections.push_back(sec); + + for (auto *ds : sec->dependentSections) + dependentSections.push_back(ds); + + if (sec->pieces.empty()) + return; + + if (sec->areRelocsRela) + addSectionAux(sec, sec->template relas()); + else + addSectionAux(sec, sec->template rels()); +} + +static void writeCieFde(uint8_t *buf, ArrayRef d) { + memcpy(buf, d.data(), d.size()); + + size_t aligned = alignTo(d.size(), config->wordsize); + + // Zero-clear trailing padding if it exists. + memset(buf + d.size(), 0, aligned - d.size()); + + // Fix the size field. -4 since size does not include the size field itself. + write32(buf, aligned - 4); +} + +void EhFrameSection::finalizeContents() { + assert(!this->size); // Not finalized. + size_t off = 0; + for (CieRecord *rec : cieRecords) { + rec->cie->outputOff = off; + off += alignTo(rec->cie->size, config->wordsize); + + for (EhSectionPiece *fde : rec->fdes) { + fde->outputOff = off; + off += alignTo(fde->size, config->wordsize); + } + } + + // The LSB standard does not allow a .eh_frame section with zero + // Call Frame Information records. glibc unwind-dw2-fde.c + // classify_object_over_fdes expects there is a CIE record length 0 as a + // terminator. Thus we add one unconditionally. + off += 4; + + this->size = off; +} + +// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table +// to get an FDE from an address to which FDE is applied. This function +// returns a list of such pairs. +std::vector EhFrameSection::getFdeData() const { + uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff; + std::vector ret; + + uint64_t va = getPartition().ehFrameHdr->getVA(); + for (CieRecord *rec : cieRecords) { + uint8_t enc = getFdeEncoding(rec->cie); + for (EhSectionPiece *fde : rec->fdes) { + uint64_t pc = getFdePc(buf, fde->outputOff, enc); + uint64_t fdeVA = getParent()->addr + fde->outputOff; + if (!isInt<32>(pc - va)) + fatal(toString(fde->sec) + ": PC offset is too large: 0x" + + Twine::utohexstr(pc - va)); + ret.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)}); + } + } + + // Sort the FDE list by their PC and uniqueify. Usually there is only + // one FDE for a PC (i.e. function), but if ICF merges two functions + // into one, there can be more than one FDEs pointing to the address. + auto less = [](const FdeData &a, const FdeData &b) { + return a.pcRel < b.pcRel; + }; + llvm::stable_sort(ret, less); + auto eq = [](const FdeData &a, const FdeData &b) { + return a.pcRel == b.pcRel; + }; + ret.erase(std::unique(ret.begin(), ret.end(), eq), ret.end()); + + return ret; +} + +static uint64_t readFdeAddr(uint8_t *buf, int size) { + switch (size) { + case DW_EH_PE_udata2: + return read16(buf); + case DW_EH_PE_sdata2: + return (int16_t)read16(buf); + case DW_EH_PE_udata4: + return read32(buf); + case DW_EH_PE_sdata4: + return (int32_t)read32(buf); + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + return read64(buf); + case DW_EH_PE_absptr: + return readUint(buf); + } + fatal("unknown FDE size encoding"); +} + +// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to. +// We need it to create .eh_frame_hdr section. +uint64_t EhFrameSection::getFdePc(uint8_t *buf, size_t fdeOff, + uint8_t enc) const { + // The starting address to which this FDE applies is + // stored at FDE + 8 byte. + size_t off = fdeOff + 8; + uint64_t addr = readFdeAddr(buf + off, enc & 0xf); + if ((enc & 0x70) == DW_EH_PE_absptr) + return addr; + if ((enc & 0x70) == DW_EH_PE_pcrel) + return addr + getParent()->addr + off; + fatal("unknown FDE size relative encoding"); +} + +void EhFrameSection::writeTo(uint8_t *buf) { + // Write CIE and FDE records. + for (CieRecord *rec : cieRecords) { + size_t cieOffset = rec->cie->outputOff; + writeCieFde(buf + cieOffset, rec->cie->data()); + + for (EhSectionPiece *fde : rec->fdes) { + size_t off = fde->outputOff; + writeCieFde(buf + off, fde->data()); + + // FDE's second word should have the offset to an associated CIE. + // Write it. + write32(buf + off + 4, off + 4 - cieOffset); + } + } + + // Apply relocations. .eh_frame section contents are not contiguous + // in the output buffer, but relocateAlloc() still works because + // getOffset() takes care of discontiguous section pieces. + for (EhInputSection *s : sections) + s->relocateAlloc(buf, nullptr); + + if (getPartition().ehFrameHdr && getPartition().ehFrameHdr->getParent()) + getPartition().ehFrameHdr->write(); +} + +GotSection::GotSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize, + ".got") { + // If ElfSym::globalOffsetTable is relative to .got and is referenced, + // increase numEntries by the number of entries used to emit + // ElfSym::globalOffsetTable. + if (ElfSym::globalOffsetTable && !target->gotBaseSymInGotPlt) + numEntries += target->gotHeaderEntriesNum; +} + +void GotSection::addEntry(Symbol &sym) { + sym.gotIndex = numEntries; + ++numEntries; +} + +bool GotSection::addDynTlsEntry(Symbol &sym) { + if (sym.globalDynIndex != -1U) + return false; + sym.globalDynIndex = numEntries; + // Global Dynamic TLS entries take two GOT slots. + numEntries += 2; + return true; +} + +// Reserves TLS entries for a TLS module ID and a TLS block offset. +// In total it takes two GOT slots. +bool GotSection::addTlsIndex() { + if (tlsIndexOff != uint32_t(-1)) + return false; + tlsIndexOff = numEntries * config->wordsize; + numEntries += 2; + return true; +} + +uint64_t GotSection::getGlobalDynAddr(const Symbol &b) const { + return this->getVA() + b.globalDynIndex * config->wordsize; +} + +uint64_t GotSection::getGlobalDynOffset(const Symbol &b) const { + return b.globalDynIndex * config->wordsize; +} + +void GotSection::finalizeContents() { + size = numEntries * config->wordsize; +} + +bool GotSection::isNeeded() const { + // We need to emit a GOT even if it's empty if there's a relocation that is + // relative to GOT(such as GOTOFFREL). + return numEntries || hasGotOffRel; +} + +void GotSection::writeTo(uint8_t *buf) { + // Buf points to the start of this section's buffer, + // whereas InputSectionBase::relocateAlloc() expects its argument + // to point to the start of the output section. + target->writeGotHeader(buf); + relocateAlloc(buf - outSecOff, buf - outSecOff + size); +} + +static uint64_t getMipsPageAddr(uint64_t addr) { + return (addr + 0x8000) & ~0xffff; +} + +static uint64_t getMipsPageCount(uint64_t size) { + return (size + 0xfffe) / 0xffff + 1; +} + +MipsGotSection::MipsGotSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16, + ".got") {} + +void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend, + RelExpr expr) { + FileGot &g = getGot(file); + if (expr == R_MIPS_GOT_LOCAL_PAGE) { + if (const OutputSection *os = sym.getOutputSection()) + g.pagesMap.insert({os, {}}); + else + g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(addend))}, 0}); + } else if (sym.isTls()) + g.tls.insert({&sym, 0}); + else if (sym.isPreemptible && expr == R_ABS) + g.relocs.insert({&sym, 0}); + else if (sym.isPreemptible) + g.global.insert({&sym, 0}); + else if (expr == R_MIPS_GOT_OFF32) + g.local32.insert({{&sym, addend}, 0}); + else + g.local16.insert({{&sym, addend}, 0}); +} + +void MipsGotSection::addDynTlsEntry(InputFile &file, Symbol &sym) { + getGot(file).dynTlsSymbols.insert({&sym, 0}); +} + +void MipsGotSection::addTlsIndex(InputFile &file) { + getGot(file).dynTlsSymbols.insert({nullptr, 0}); +} + +size_t MipsGotSection::FileGot::getEntriesNum() const { + return getPageEntriesNum() + local16.size() + global.size() + relocs.size() + + tls.size() + dynTlsSymbols.size() * 2; +} + +size_t MipsGotSection::FileGot::getPageEntriesNum() const { + size_t num = 0; + for (const std::pair &p : pagesMap) + num += p.second.count; + return num; +} + +size_t MipsGotSection::FileGot::getIndexedEntriesNum() const { + size_t count = getPageEntriesNum() + local16.size() + global.size(); + // If there are relocation-only entries in the GOT, TLS entries + // are allocated after them. TLS entries should be addressable + // by 16-bit index so count both reloc-only and TLS entries. + if (!tls.empty() || !dynTlsSymbols.empty()) + count += relocs.size() + tls.size() + dynTlsSymbols.size() * 2; + return count; +} + +MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &f) { + if (!f.mipsGotIndex.hasValue()) { + gots.emplace_back(); + gots.back().file = &f; + f.mipsGotIndex = gots.size() - 1; + } + return gots[*f.mipsGotIndex]; +} + +uint64_t MipsGotSection::getPageEntryOffset(const InputFile *f, + const Symbol &sym, + int64_t addend) const { + const FileGot &g = gots[*f->mipsGotIndex]; + uint64_t index = 0; + if (const OutputSection *outSec = sym.getOutputSection()) { + uint64_t secAddr = getMipsPageAddr(outSec->addr); + uint64_t symAddr = getMipsPageAddr(sym.getVA(addend)); + index = g.pagesMap.lookup(outSec).firstIndex + (symAddr - secAddr) / 0xffff; + } else { + index = g.local16.lookup({nullptr, getMipsPageAddr(sym.getVA(addend))}); + } + return index * config->wordsize; +} + +uint64_t MipsGotSection::getSymEntryOffset(const InputFile *f, const Symbol &s, + int64_t addend) const { + const FileGot &g = gots[*f->mipsGotIndex]; + Symbol *sym = const_cast(&s); + if (sym->isTls()) + return g.tls.lookup(sym) * config->wordsize; + if (sym->isPreemptible) + return g.global.lookup(sym) * config->wordsize; + return g.local16.lookup({sym, addend}) * config->wordsize; +} + +uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *f) const { + const FileGot &g = gots[*f->mipsGotIndex]; + return g.dynTlsSymbols.lookup(nullptr) * config->wordsize; +} + +uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *f, + const Symbol &s) const { + const FileGot &g = gots[*f->mipsGotIndex]; + Symbol *sym = const_cast(&s); + return g.dynTlsSymbols.lookup(sym) * config->wordsize; +} + +const Symbol *MipsGotSection::getFirstGlobalEntry() const { + if (gots.empty()) + return nullptr; + const FileGot &primGot = gots.front(); + if (!primGot.global.empty()) + return primGot.global.front().first; + if (!primGot.relocs.empty()) + return primGot.relocs.front().first; + return nullptr; +} + +unsigned MipsGotSection::getLocalEntriesNum() const { + if (gots.empty()) + return headerEntriesNum; + return headerEntriesNum + gots.front().getPageEntriesNum() + + gots.front().local16.size(); +} + +bool MipsGotSection::tryMergeGots(FileGot &dst, FileGot &src, bool isPrimary) { + FileGot tmp = dst; + set_union(tmp.pagesMap, src.pagesMap); + set_union(tmp.local16, src.local16); + set_union(tmp.global, src.global); + set_union(tmp.relocs, src.relocs); + set_union(tmp.tls, src.tls); + set_union(tmp.dynTlsSymbols, src.dynTlsSymbols); + + size_t count = isPrimary ? headerEntriesNum : 0; + count += tmp.getIndexedEntriesNum(); + + if (count * config->wordsize > config->mipsGotSize) + return false; + + std::swap(tmp, dst); + return true; +} + +void MipsGotSection::finalizeContents() { updateAllocSize(); } + +bool MipsGotSection::updateAllocSize() { + size = headerEntriesNum * config->wordsize; + for (const FileGot &g : gots) + size += g.getEntriesNum() * config->wordsize; + return false; +} + +void MipsGotSection::build() { + if (gots.empty()) + return; + + std::vector mergedGots(1); + + // For each GOT move non-preemptible symbols from the `Global` + // to `Local16` list. Preemptible symbol might become non-preemptible + // one if, for example, it gets a related copy relocation. + for (FileGot &got : gots) { + for (auto &p: got.global) + if (!p.first->isPreemptible) + got.local16.insert({{p.first, 0}, 0}); + got.global.remove_if([&](const std::pair &p) { + return !p.first->isPreemptible; + }); + } + + // For each GOT remove "reloc-only" entry if there is "global" + // entry for the same symbol. And add local entries which indexed + // using 32-bit value at the end of 16-bit entries. + for (FileGot &got : gots) { + got.relocs.remove_if([&](const std::pair &p) { + return got.global.count(p.first); + }); + set_union(got.local16, got.local32); + got.local32.clear(); + } + + // Evaluate number of "reloc-only" entries in the resulting GOT. + // To do that put all unique "reloc-only" and "global" entries + // from all GOTs to the future primary GOT. + FileGot *primGot = &mergedGots.front(); + for (FileGot &got : gots) { + set_union(primGot->relocs, got.global); + set_union(primGot->relocs, got.relocs); + got.relocs.clear(); + } + + // Evaluate number of "page" entries in each GOT. + for (FileGot &got : gots) { + for (std::pair &p : + got.pagesMap) { + const OutputSection *os = p.first; + uint64_t secSize = 0; + for (BaseCommand *cmd : os->sectionCommands) { + if (auto *isd = dyn_cast(cmd)) + for (InputSection *isec : isd->sections) { + uint64_t off = alignTo(secSize, isec->alignment); + secSize = off + isec->getSize(); + } + } + p.second.count = getMipsPageCount(secSize); + } + } + + // Merge GOTs. Try to join as much as possible GOTs but do not exceed + // maximum GOT size. At first, try to fill the primary GOT because + // the primary GOT can be accessed in the most effective way. If it + // is not possible, try to fill the last GOT in the list, and finally + // create a new GOT if both attempts failed. + for (FileGot &srcGot : gots) { + InputFile *file = srcGot.file; + if (tryMergeGots(mergedGots.front(), srcGot, true)) { + file->mipsGotIndex = 0; + } else { + // If this is the first time we failed to merge with the primary GOT, + // MergedGots.back() will also be the primary GOT. We must make sure not + // to try to merge again with isPrimary=false, as otherwise, if the + // inputs are just right, we could allow the primary GOT to become 1 or 2 + // words bigger due to ignoring the header size. + if (mergedGots.size() == 1 || + !tryMergeGots(mergedGots.back(), srcGot, false)) { + mergedGots.emplace_back(); + std::swap(mergedGots.back(), srcGot); + } + file->mipsGotIndex = mergedGots.size() - 1; + } + } + std::swap(gots, mergedGots); + + // Reduce number of "reloc-only" entries in the primary GOT + // by substracting "global" entries exist in the primary GOT. + primGot = &gots.front(); + primGot->relocs.remove_if([&](const std::pair &p) { + return primGot->global.count(p.first); + }); + + // Calculate indexes for each GOT entry. + size_t index = headerEntriesNum; + for (FileGot &got : gots) { + got.startIndex = &got == primGot ? 0 : index; + for (std::pair &p : + got.pagesMap) { + // For each output section referenced by GOT page relocations calculate + // and save into pagesMap an upper bound of MIPS GOT entries required + // to store page addresses of local symbols. We assume the worst case - + // each 64kb page of the output section has at least one GOT relocation + // against it. And take in account the case when the section intersects + // page boundaries. + p.second.firstIndex = index; + index += p.second.count; + } + for (auto &p: got.local16) + p.second = index++; + for (auto &p: got.global) + p.second = index++; + for (auto &p: got.relocs) + p.second = index++; + for (auto &p: got.tls) + p.second = index++; + for (auto &p: got.dynTlsSymbols) { + p.second = index; + index += 2; + } + } + + // Update Symbol::gotIndex field to use this + // value later in the `sortMipsSymbols` function. + for (auto &p : primGot->global) + p.first->gotIndex = p.second; + for (auto &p : primGot->relocs) + p.first->gotIndex = p.second; + + // Create dynamic relocations. + for (FileGot &got : gots) { + // Create dynamic relocations for TLS entries. + for (std::pair &p : got.tls) { + Symbol *s = p.first; + uint64_t offset = p.second * config->wordsize; + if (s->isPreemptible) + mainPart->relaDyn->addReloc(target->tlsGotRel, this, offset, s); + } + for (std::pair &p : got.dynTlsSymbols) { + Symbol *s = p.first; + uint64_t offset = p.second * config->wordsize; + if (s == nullptr) { + if (!config->isPic) + continue; + mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s); + } else { + // When building a shared library we still need a dynamic relocation + // for the module index. Therefore only checking for + // S->isPreemptible is not sufficient (this happens e.g. for + // thread-locals that have been marked as local through a linker script) + if (!s->isPreemptible && !config->isPic) + continue; + mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s); + // However, we can skip writing the TLS offset reloc for non-preemptible + // symbols since it is known even in shared libraries + if (!s->isPreemptible) + continue; + offset += config->wordsize; + mainPart->relaDyn->addReloc(target->tlsOffsetRel, this, offset, s); + } + } + + // Do not create dynamic relocations for non-TLS + // entries in the primary GOT. + if (&got == primGot) + continue; + + // Dynamic relocations for "global" entries. + for (const std::pair &p : got.global) { + uint64_t offset = p.second * config->wordsize; + mainPart->relaDyn->addReloc(target->relativeRel, this, offset, p.first); + } + if (!config->isPic) + continue; + // Dynamic relocations for "local" entries in case of PIC. + for (const std::pair &l : + got.pagesMap) { + size_t pageCount = l.second.count; + for (size_t pi = 0; pi < pageCount; ++pi) { + uint64_t offset = (l.second.firstIndex + pi) * config->wordsize; + mainPart->relaDyn->addReloc({target->relativeRel, this, offset, l.first, + int64_t(pi * 0x10000)}); + } + } + for (const std::pair &p : got.local16) { + uint64_t offset = p.second * config->wordsize; + mainPart->relaDyn->addReloc({target->relativeRel, this, offset, true, + p.first.first, p.first.second}); + } + } +} + +bool MipsGotSection::isNeeded() const { + // We add the .got section to the result for dynamic MIPS target because + // its address and properties are mentioned in the .dynamic section. + return !config->relocatable; +} + +uint64_t MipsGotSection::getGp(const InputFile *f) const { + // For files without related GOT or files refer a primary GOT + // returns "common" _gp value. For secondary GOTs calculate + // individual _gp values. + if (!f || !f->mipsGotIndex.hasValue() || *f->mipsGotIndex == 0) + return ElfSym::mipsGp->getVA(0); + return getVA() + gots[*f->mipsGotIndex].startIndex * config->wordsize + + 0x7ff0; +} + +void MipsGotSection::writeTo(uint8_t *buf) { + // Set the MSB of the second GOT slot. This is not required by any + // MIPS ABI documentation, though. + // + // There is a comment in glibc saying that "The MSB of got[1] of a + // gnu object is set to identify gnu objects," and in GNU gold it + // says "the second entry will be used by some runtime loaders". + // But how this field is being used is unclear. + // + // We are not really willing to mimic other linkers behaviors + // without understanding why they do that, but because all files + // generated by GNU tools have this special GOT value, and because + // we've been doing this for years, it is probably a safe bet to + // keep doing this for now. We really need to revisit this to see + // if we had to do this. + writeUint(buf + config->wordsize, (uint64_t)1 << (config->wordsize * 8 - 1)); + for (const FileGot &g : gots) { + auto write = [&](size_t i, const Symbol *s, int64_t a) { + uint64_t va = a; + if (s) + va = s->getVA(a); + writeUint(buf + i * config->wordsize, va); + }; + // Write 'page address' entries to the local part of the GOT. + for (const std::pair &l : + g.pagesMap) { + size_t pageCount = l.second.count; + uint64_t firstPageAddr = getMipsPageAddr(l.first->addr); + for (size_t pi = 0; pi < pageCount; ++pi) + write(l.second.firstIndex + pi, nullptr, firstPageAddr + pi * 0x10000); + } + // Local, global, TLS, reloc-only entries. + // If TLS entry has a corresponding dynamic relocations, leave it + // initialized by zero. Write down adjusted TLS symbol's values otherwise. + // To calculate the adjustments use offsets for thread-local storage. + // https://www.linux-mips.org/wiki/NPTL + for (const std::pair &p : g.local16) + write(p.second, p.first.first, p.first.second); + // Write VA to the primary GOT only. For secondary GOTs that + // will be done by REL32 dynamic relocations. + if (&g == &gots.front()) + for (const std::pair &p : g.global) + write(p.second, p.first, 0); + for (const std::pair &p : g.relocs) + write(p.second, p.first, 0); + for (const std::pair &p : g.tls) + write(p.second, p.first, p.first->isPreemptible ? 0 : -0x7000); + for (const std::pair &p : g.dynTlsSymbols) { + if (p.first == nullptr && !config->isPic) + write(p.second, nullptr, 1); + else if (p.first && !p.first->isPreemptible) { + // If we are emitting PIC code with relocations we mustn't write + // anything to the GOT here. When using Elf_Rel relocations the value + // one will be treated as an addend and will cause crashes at runtime + if (!config->isPic) + write(p.second, nullptr, 1); + write(p.second + 1, p.first, -0x8000); + } + } + } +} + +// On PowerPC the .plt section is used to hold the table of function addresses +// instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss +// section. I don't know why we have a BSS style type for the section but it is +// consitent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI. +GotPltSection::GotPltSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize, + ".got.plt") { + if (config->emachine == EM_PPC) { + name = ".plt"; + } else if (config->emachine == EM_PPC64) { + type = SHT_NOBITS; + name = ".plt"; + } +} + +void GotPltSection::addEntry(Symbol &sym) { + assert(sym.pltIndex == entries.size()); + entries.push_back(&sym); +} + +size_t GotPltSection::getSize() const { + return (target->gotPltHeaderEntriesNum + entries.size()) * config->wordsize; +} + +void GotPltSection::writeTo(uint8_t *buf) { + target->writeGotPltHeader(buf); + buf += target->gotPltHeaderEntriesNum * config->wordsize; + for (const Symbol *b : entries) { + target->writeGotPlt(buf, *b); + buf += config->wordsize; + } +} + +bool GotPltSection::isNeeded() const { + // We need to emit GOTPLT even if it's empty if there's a relocation relative + // to it. + return !entries.empty() || hasGotPltOffRel; +} + +static StringRef getIgotPltName() { + // On ARM the IgotPltSection is part of the GotSection. + if (config->emachine == EM_ARM) + return ".got"; + + // On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection + // needs to be named the same. + if (config->emachine == EM_PPC64) + return ".plt"; + + return ".got.plt"; +} + +// On PowerPC64 the GotPltSection type is SHT_NOBITS so we have to follow suit +// with the IgotPltSection. +IgotPltSection::IgotPltSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, + config->emachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS, + config->wordsize, getIgotPltName()) {} + +void IgotPltSection::addEntry(Symbol &sym) { + assert(sym.pltIndex == entries.size()); + entries.push_back(&sym); +} + +size_t IgotPltSection::getSize() const { + return entries.size() * config->wordsize; +} + +void IgotPltSection::writeTo(uint8_t *buf) { + for (const Symbol *b : entries) { + target->writeIgotPlt(buf, *b); + buf += config->wordsize; + } +} + +StringTableSection::StringTableSection(StringRef name, bool dynamic) + : SyntheticSection(dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, name), + dynamic(dynamic) { + // ELF string tables start with a NUL byte. + addString(""); +} + +// Adds a string to the string table. If `hashIt` is true we hash and check for +// duplicates. It is optional because the name of global symbols are already +// uniqued and hashing them again has a big cost for a small value: uniquing +// them with some other string that happens to be the same. +unsigned StringTableSection::addString(StringRef s, bool hashIt) { + if (hashIt) { + auto r = stringMap.insert(std::make_pair(s, this->size)); + if (!r.second) + return r.first->second; + } + unsigned ret = this->size; + this->size = this->size + s.size() + 1; + strings.push_back(s); + return ret; +} + +void StringTableSection::writeTo(uint8_t *buf) { + for (StringRef s : strings) { + memcpy(buf, s.data(), s.size()); + buf[s.size()] = '\0'; + buf += s.size() + 1; + } +} + +// Returns the number of version definition entries. Because the first entry +// is for the version definition itself, it is the number of versioned symbols +// plus one. Note that we don't support multiple versions yet. +static unsigned getVerDefNum() { return config->versionDefinitions.size() + 1; } + +template +DynamicSection::DynamicSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC, config->wordsize, + ".dynamic") { + this->entsize = ELFT::Is64Bits ? 16 : 8; + + // .dynamic section is not writable on MIPS and on Fuchsia OS + // which passes -z rodynamic. + // See "Special Section" in Chapter 4 in the following document: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + if (config->emachine == EM_MIPS || config->zRodynamic) + this->flags = SHF_ALLOC; +} + +template +void DynamicSection::add(int32_t tag, std::function fn) { + entries.push_back({tag, fn}); +} + +template +void DynamicSection::addInt(int32_t tag, uint64_t val) { + entries.push_back({tag, [=] { return val; }}); +} + +template +void DynamicSection::addInSec(int32_t tag, InputSection *sec) { + entries.push_back({tag, [=] { return sec->getVA(0); }}); +} + +template +void DynamicSection::addInSecRelative(int32_t tag, InputSection *sec) { + size_t tagOffset = entries.size() * entsize; + entries.push_back( + {tag, [=] { return sec->getVA(0) - (getVA() + tagOffset); }}); +} + +template +void DynamicSection::addOutSec(int32_t tag, OutputSection *sec) { + entries.push_back({tag, [=] { return sec->addr; }}); +} + +template +void DynamicSection::addSize(int32_t tag, OutputSection *sec) { + entries.push_back({tag, [=] { return sec->size; }}); +} + +template +void DynamicSection::addSym(int32_t tag, Symbol *sym) { + entries.push_back({tag, [=] { return sym->getVA(); }}); +} + +// A Linker script may assign the RELA relocation sections to the same +// output section. When this occurs we cannot just use the OutputSection +// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to +// overlap with the [DT_RELA, DT_RELA + DT_RELASZ). +static uint64_t addPltRelSz() { + size_t size = in.relaPlt->getSize(); + if (in.relaIplt->getParent() == in.relaPlt->getParent() && + in.relaIplt->name == in.relaPlt->name) + size += in.relaIplt->getSize(); + return size; +} + +// Add remaining entries to complete .dynamic contents. +template void DynamicSection::finalizeContents() { + elf::Partition &part = getPartition(); + bool isMain = part.name.empty(); + + for (StringRef s : config->filterList) + addInt(DT_FILTER, part.dynStrTab->addString(s)); + for (StringRef s : config->auxiliaryList) + addInt(DT_AUXILIARY, part.dynStrTab->addString(s)); + + if (!config->rpath.empty()) + addInt(config->enableNewDtags ? DT_RUNPATH : DT_RPATH, + part.dynStrTab->addString(config->rpath)); + + for (SharedFile *file : sharedFiles) + if (file->isNeeded) + addInt(DT_NEEDED, part.dynStrTab->addString(file->soName)); + + if (isMain) { + if (!config->soName.empty()) + addInt(DT_SONAME, part.dynStrTab->addString(config->soName)); + } else { + if (!config->soName.empty()) + addInt(DT_NEEDED, part.dynStrTab->addString(config->soName)); + addInt(DT_SONAME, part.dynStrTab->addString(part.name)); + } + + // Set DT_FLAGS and DT_FLAGS_1. + uint32_t dtFlags = 0; + uint32_t dtFlags1 = 0; + if (config->bsymbolic) + dtFlags |= DF_SYMBOLIC; + if (config->zGlobal) + dtFlags1 |= DF_1_GLOBAL; + if (config->zInitfirst) + dtFlags1 |= DF_1_INITFIRST; + if (config->zInterpose) + dtFlags1 |= DF_1_INTERPOSE; + if (config->zNodefaultlib) + dtFlags1 |= DF_1_NODEFLIB; + if (config->zNodelete) + dtFlags1 |= DF_1_NODELETE; + if (config->zNodlopen) + dtFlags1 |= DF_1_NOOPEN; + if (config->zNow) { + dtFlags |= DF_BIND_NOW; + dtFlags1 |= DF_1_NOW; + } + if (config->zOrigin) { + dtFlags |= DF_ORIGIN; + dtFlags1 |= DF_1_ORIGIN; + } + if (!config->zText) + dtFlags |= DF_TEXTREL; + if (config->hasStaticTlsModel) + dtFlags |= DF_STATIC_TLS; + + if (dtFlags) + addInt(DT_FLAGS, dtFlags); + if (dtFlags1) + addInt(DT_FLAGS_1, dtFlags1); + + // DT_DEBUG is a pointer to debug informaion used by debuggers at runtime. We + // need it for each process, so we don't write it for DSOs. The loader writes + // the pointer into this entry. + // + // DT_DEBUG is the only .dynamic entry that needs to be written to. Some + // systems (currently only Fuchsia OS) provide other means to give the + // debugger this information. Such systems may choose make .dynamic read-only. + // If the target is such a system (used -z rodynamic) don't write DT_DEBUG. + if (!config->shared && !config->relocatable && !config->zRodynamic) + addInt(DT_DEBUG, 0); + + if (OutputSection *sec = part.dynStrTab->getParent()) + this->link = sec->sectionIndex; + + if (part.relaDyn->isNeeded()) { + addInSec(part.relaDyn->dynamicTag, part.relaDyn); + addSize(part.relaDyn->sizeDynamicTag, part.relaDyn->getParent()); + + bool isRela = config->isRela; + addInt(isRela ? DT_RELAENT : DT_RELENT, + isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel)); + + // MIPS dynamic loader does not support RELCOUNT tag. + // The problem is in the tight relation between dynamic + // relocations and GOT. So do not emit this tag on MIPS. + if (config->emachine != EM_MIPS) { + size_t numRelativeRels = part.relaDyn->getRelativeRelocCount(); + if (config->zCombreloc && numRelativeRels) + addInt(isRela ? DT_RELACOUNT : DT_RELCOUNT, numRelativeRels); + } + } + if (part.relrDyn && !part.relrDyn->relocs.empty()) { + addInSec(config->useAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR, + part.relrDyn); + addSize(config->useAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ, + part.relrDyn->getParent()); + addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT, + sizeof(Elf_Relr)); + } + // .rel[a].plt section usually consists of two parts, containing plt and + // iplt relocations. It is possible to have only iplt relocations in the + // output. In that case relaPlt is empty and have zero offset, the same offset + // as relaIplt has. And we still want to emit proper dynamic tags for that + // case, so here we always use relaPlt as marker for the begining of + // .rel[a].plt section. + if (isMain && (in.relaPlt->isNeeded() || in.relaIplt->isNeeded())) { + addInSec(DT_JMPREL, in.relaPlt); + entries.push_back({DT_PLTRELSZ, addPltRelSz}); + switch (config->emachine) { + case EM_MIPS: + addInSec(DT_MIPS_PLTGOT, in.gotPlt); + break; + case EM_SPARCV9: + addInSec(DT_PLTGOT, in.plt); + break; + default: + addInSec(DT_PLTGOT, in.gotPlt); + break; + } + addInt(DT_PLTREL, config->isRela ? DT_RELA : DT_REL); + } + + if (config->emachine == EM_AARCH64) { + if (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) + addInt(DT_AARCH64_BTI_PLT, 0); + if (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC) + addInt(DT_AARCH64_PAC_PLT, 0); + } + + addInSec(DT_SYMTAB, part.dynSymTab); + addInt(DT_SYMENT, sizeof(Elf_Sym)); + addInSec(DT_STRTAB, part.dynStrTab); + addInt(DT_STRSZ, part.dynStrTab->getSize()); + if (!config->zText) + addInt(DT_TEXTREL, 0); + if (part.gnuHashTab) + addInSec(DT_GNU_HASH, part.gnuHashTab); + if (part.hashTab) + addInSec(DT_HASH, part.hashTab); + + if (isMain) { + if (Out::preinitArray) { + addOutSec(DT_PREINIT_ARRAY, Out::preinitArray); + addSize(DT_PREINIT_ARRAYSZ, Out::preinitArray); + } + if (Out::initArray) { + addOutSec(DT_INIT_ARRAY, Out::initArray); + addSize(DT_INIT_ARRAYSZ, Out::initArray); + } + if (Out::finiArray) { + addOutSec(DT_FINI_ARRAY, Out::finiArray); + addSize(DT_FINI_ARRAYSZ, Out::finiArray); + } + + if (Symbol *b = symtab->find(config->init)) + if (b->isDefined()) + addSym(DT_INIT, b); + if (Symbol *b = symtab->find(config->fini)) + if (b->isDefined()) + addSym(DT_FINI, b); + } + + bool hasVerNeed = SharedFile::vernauxNum != 0; + if (hasVerNeed || part.verDef) + addInSec(DT_VERSYM, part.verSym); + if (part.verDef) { + addInSec(DT_VERDEF, part.verDef); + addInt(DT_VERDEFNUM, getVerDefNum()); + } + if (hasVerNeed) { + addInSec(DT_VERNEED, part.verNeed); + unsigned needNum = 0; + for (SharedFile *f : sharedFiles) + if (!f->vernauxs.empty()) + ++needNum; + addInt(DT_VERNEEDNUM, needNum); + } + + if (config->emachine == EM_MIPS) { + addInt(DT_MIPS_RLD_VERSION, 1); + addInt(DT_MIPS_FLAGS, RHF_NOTPOT); + addInt(DT_MIPS_BASE_ADDRESS, target->getImageBase()); + addInt(DT_MIPS_SYMTABNO, part.dynSymTab->getNumSymbols()); + + add(DT_MIPS_LOCAL_GOTNO, [] { return in.mipsGot->getLocalEntriesNum(); }); + + if (const Symbol *b = in.mipsGot->getFirstGlobalEntry()) + addInt(DT_MIPS_GOTSYM, b->dynsymIndex); + else + addInt(DT_MIPS_GOTSYM, part.dynSymTab->getNumSymbols()); + addInSec(DT_PLTGOT, in.mipsGot); + if (in.mipsRldMap) { + if (!config->pie) + addInSec(DT_MIPS_RLD_MAP, in.mipsRldMap); + // Store the offset to the .rld_map section + // relative to the address of the tag. + addInSecRelative(DT_MIPS_RLD_MAP_REL, in.mipsRldMap); + } + } + + // DT_PPC_GOT indicates to glibc Secure PLT is used. If DT_PPC_GOT is absent, + // glibc assumes the old-style BSS PLT layout which we don't support. + if (config->emachine == EM_PPC) + add(DT_PPC_GOT, [] { return in.got->getVA(); }); + + // Glink dynamic tag is required by the V2 abi if the plt section isn't empty. + if (config->emachine == EM_PPC64 && in.plt->isNeeded()) { + // The Glink tag points to 32 bytes before the first lazy symbol resolution + // stub, which starts directly after the header. + entries.push_back({DT_PPC64_GLINK, [=] { + unsigned offset = target->pltHeaderSize - 32; + return in.plt->getVA(0) + offset; + }}); + } + + addInt(DT_NULL, 0); + + getParent()->link = this->link; + this->size = entries.size() * this->entsize; +} + +template void DynamicSection::writeTo(uint8_t *buf) { + auto *p = reinterpret_cast(buf); + + for (std::pair> &kv : entries) { + p->d_tag = kv.first; + p->d_un.d_val = kv.second(); + ++p; + } +} + +uint64_t DynamicReloc::getOffset() const { + return inputSec->getVA(offsetInSec); +} + +int64_t DynamicReloc::computeAddend() const { + if (useSymVA) + return sym->getVA(addend); + if (!outputSec) + return addend; + // See the comment in the DynamicReloc ctor. + return getMipsPageAddr(outputSec->addr) + addend; +} + +uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const { + if (sym && !useSymVA) + return symTab->getSymbolIndex(sym); + return 0; +} + +RelocationBaseSection::RelocationBaseSection(StringRef name, uint32_t type, + int32_t dynamicTag, + int32_t sizeDynamicTag) + : SyntheticSection(SHF_ALLOC, type, config->wordsize, name), + dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag) {} + +void RelocationBaseSection::addReloc(RelType dynType, InputSectionBase *isec, + uint64_t offsetInSec, Symbol *sym) { + addReloc({dynType, isec, offsetInSec, false, sym, 0}); +} + +void RelocationBaseSection::addReloc(RelType dynType, + InputSectionBase *inputSec, + uint64_t offsetInSec, Symbol *sym, + int64_t addend, RelExpr expr, + RelType type) { + // Write the addends to the relocated address if required. We skip + // it if the written value would be zero. + if (config->writeAddends && (expr != R_ADDEND || addend != 0)) + inputSec->relocations.push_back({expr, type, offsetInSec, addend, sym}); + addReloc({dynType, inputSec, offsetInSec, expr != R_ADDEND, sym, addend}); +} + +void RelocationBaseSection::addReloc(const DynamicReloc &reloc) { + if (reloc.type == target->relativeRel) + ++numRelativeRelocs; + relocs.push_back(reloc); +} + +void RelocationBaseSection::finalizeContents() { + SymbolTableBaseSection *symTab = getPartition().dynSymTab; + + // When linking glibc statically, .rel{,a}.plt contains R_*_IRELATIVE + // relocations due to IFUNC (e.g. strcpy). sh_link will be set to 0 in that + // case. + if (symTab && symTab->getParent()) + getParent()->link = symTab->getParent()->sectionIndex; + else + getParent()->link = 0; + + if (in.relaPlt == this) + getParent()->info = in.gotPlt->getParent()->sectionIndex; + if (in.relaIplt == this) + getParent()->info = in.igotPlt->getParent()->sectionIndex; +} + +RelrBaseSection::RelrBaseSection() + : SyntheticSection(SHF_ALLOC, + config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR, + config->wordsize, ".relr.dyn") {} + +template +static void encodeDynamicReloc(SymbolTableBaseSection *symTab, + typename ELFT::Rela *p, + const DynamicReloc &rel) { + if (config->isRela) + p->r_addend = rel.computeAddend(); + p->r_offset = rel.getOffset(); + p->setSymbolAndType(rel.getSymIndex(symTab), rel.type, config->isMips64EL); +} + +template +RelocationSection::RelocationSection(StringRef name, bool sort) + : RelocationBaseSection(name, config->isRela ? SHT_RELA : SHT_REL, + config->isRela ? DT_RELA : DT_REL, + config->isRela ? DT_RELASZ : DT_RELSZ), + sort(sort) { + this->entsize = config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); +} + +template void RelocationSection::writeTo(uint8_t *buf) { + SymbolTableBaseSection *symTab = getPartition().dynSymTab; + + // Sort by (!IsRelative,SymIndex,r_offset). DT_REL[A]COUNT requires us to + // place R_*_RELATIVE first. SymIndex is to improve locality, while r_offset + // is to make results easier to read. + if (sort) + llvm::stable_sort( + relocs, [&](const DynamicReloc &a, const DynamicReloc &b) { + return std::make_tuple(a.type != target->relativeRel, + a.getSymIndex(symTab), a.getOffset()) < + std::make_tuple(b.type != target->relativeRel, + b.getSymIndex(symTab), b.getOffset()); + }); + + for (const DynamicReloc &rel : relocs) { + encodeDynamicReloc(symTab, reinterpret_cast(buf), rel); + buf += config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); + } +} + +template +AndroidPackedRelocationSection::AndroidPackedRelocationSection( + StringRef name) + : RelocationBaseSection( + name, config->isRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL, + config->isRela ? DT_ANDROID_RELA : DT_ANDROID_REL, + config->isRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) { + this->entsize = 1; +} + +template +bool AndroidPackedRelocationSection::updateAllocSize() { + // This function computes the contents of an Android-format packed relocation + // section. + // + // This format compresses relocations by using relocation groups to factor out + // fields that are common between relocations and storing deltas from previous + // relocations in SLEB128 format (which has a short representation for small + // numbers). A good example of a relocation type with common fields is + // R_*_RELATIVE, which is normally used to represent function pointers in + // vtables. In the REL format, each relative relocation has the same r_info + // field, and is only different from other relative relocations in terms of + // the r_offset field. By sorting relocations by offset, grouping them by + // r_info and representing each relocation with only the delta from the + // previous offset, each 8-byte relocation can be compressed to as little as 1 + // byte (or less with run-length encoding). This relocation packer was able to + // reduce the size of the relocation section in an Android Chromium DSO from + // 2,911,184 bytes to 174,693 bytes, or 6% of the original size. + // + // A relocation section consists of a header containing the literal bytes + // 'APS2' followed by a sequence of SLEB128-encoded integers. The first two + // elements are the total number of relocations in the section and an initial + // r_offset value. The remaining elements define a sequence of relocation + // groups. Each relocation group starts with a header consisting of the + // following elements: + // + // - the number of relocations in the relocation group + // - flags for the relocation group + // - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is set) the r_offset delta + // for each relocation in the group. + // - (if RELOCATION_GROUPED_BY_INFO_FLAG is set) the value of the r_info + // field for each relocation in the group. + // - (if RELOCATION_GROUP_HAS_ADDEND_FLAG and + // RELOCATION_GROUPED_BY_ADDEND_FLAG are set) the r_addend delta for + // each relocation in the group. + // + // Following the relocation group header are descriptions of each of the + // relocations in the group. They consist of the following elements: + // + // - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is not set) the r_offset + // delta for this relocation. + // - (if RELOCATION_GROUPED_BY_INFO_FLAG is not set) the value of the r_info + // field for this relocation. + // - (if RELOCATION_GROUP_HAS_ADDEND_FLAG is set and + // RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for + // this relocation. + + size_t oldSize = relocData.size(); + + relocData = {'A', 'P', 'S', '2'}; + raw_svector_ostream os(relocData); + auto add = [&](int64_t v) { encodeSLEB128(v, os); }; + + // The format header includes the number of relocations and the initial + // offset (we set this to zero because the first relocation group will + // perform the initial adjustment). + add(relocs.size()); + add(0); + + std::vector relatives, nonRelatives; + + for (const DynamicReloc &rel : relocs) { + Elf_Rela r; + encodeDynamicReloc(getPartition().dynSymTab, &r, rel); + + if (r.getType(config->isMips64EL) == target->relativeRel) + relatives.push_back(r); + else + nonRelatives.push_back(r); + } + + llvm::sort(relatives, [](const Elf_Rel &a, const Elf_Rel &b) { + return a.r_offset < b.r_offset; + }); + + // Try to find groups of relative relocations which are spaced one word + // apart from one another. These generally correspond to vtable entries. The + // format allows these groups to be encoded using a sort of run-length + // encoding, but each group will cost 7 bytes in addition to the offset from + // the previous group, so it is only profitable to do this for groups of + // size 8 or larger. + std::vector ungroupedRelatives; + std::vector> relativeGroups; + for (auto i = relatives.begin(), e = relatives.end(); i != e;) { + std::vector group; + do { + group.push_back(*i++); + } while (i != e && (i - 1)->r_offset + config->wordsize == i->r_offset); + + if (group.size() < 8) + ungroupedRelatives.insert(ungroupedRelatives.end(), group.begin(), + group.end()); + else + relativeGroups.emplace_back(std::move(group)); + } + + unsigned hasAddendIfRela = + config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0; + + uint64_t offset = 0; + uint64_t addend = 0; + + // Emit the run-length encoding for the groups of adjacent relative + // relocations. Each group is represented using two groups in the packed + // format. The first is used to set the current offset to the start of the + // group (and also encodes the first relocation), and the second encodes the + // remaining relocations. + for (std::vector &g : relativeGroups) { + // The first relocation in the group. + add(1); + add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG | + RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela); + add(g[0].r_offset - offset); + add(target->relativeRel); + if (config->isRela) { + add(g[0].r_addend - addend); + addend = g[0].r_addend; + } + + // The remaining relocations. + add(g.size() - 1); + add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG | + RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela); + add(config->wordsize); + add(target->relativeRel); + if (config->isRela) { + for (auto i = g.begin() + 1, e = g.end(); i != e; ++i) { + add(i->r_addend - addend); + addend = i->r_addend; + } + } + + offset = g.back().r_offset; + } + + // Now the ungrouped relatives. + if (!ungroupedRelatives.empty()) { + add(ungroupedRelatives.size()); + add(RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela); + add(target->relativeRel); + for (Elf_Rela &r : ungroupedRelatives) { + add(r.r_offset - offset); + offset = r.r_offset; + if (config->isRela) { + add(r.r_addend - addend); + addend = r.r_addend; + } + } + } + + // Finally the non-relative relocations. + llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) { + return a.r_offset < b.r_offset; + }); + if (!nonRelatives.empty()) { + add(nonRelatives.size()); + add(hasAddendIfRela); + for (Elf_Rela &r : nonRelatives) { + add(r.r_offset - offset); + offset = r.r_offset; + add(r.r_info); + if (config->isRela) { + add(r.r_addend - addend); + addend = r.r_addend; + } + } + } + + // Don't allow the section to shrink; otherwise the size of the section can + // oscillate infinitely. + if (relocData.size() < oldSize) + relocData.append(oldSize - relocData.size(), 0); + + // Returns whether the section size changed. We need to keep recomputing both + // section layout and the contents of this section until the size converges + // because changing this section's size can affect section layout, which in + // turn can affect the sizes of the LEB-encoded integers stored in this + // section. + return relocData.size() != oldSize; +} + +template RelrSection::RelrSection() { + this->entsize = config->wordsize; +} + +template bool RelrSection::updateAllocSize() { + // This function computes the contents of an SHT_RELR packed relocation + // section. + // + // Proposal for adding SHT_RELR sections to generic-abi is here: + // https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg + // + // The encoded sequence of Elf64_Relr entries in a SHT_RELR section looks + // like [ AAAAAAAA BBBBBBB1 BBBBBBB1 ... AAAAAAAA BBBBBB1 ... ] + // + // i.e. start with an address, followed by any number of bitmaps. The address + // entry encodes 1 relocation. The subsequent bitmap entries encode up to 63 + // relocations each, at subsequent offsets following the last address entry. + // + // The bitmap entries must have 1 in the least significant bit. The assumption + // here is that an address cannot have 1 in lsb. Odd addresses are not + // supported. + // + // Excluding the least significant bit in the bitmap, each non-zero bit in + // the bitmap represents a relocation to be applied to a corresponding machine + // word that follows the base address word. The second least significant bit + // represents the machine word immediately following the initial address, and + // each bit that follows represents the next word, in linear order. As such, + // a single bitmap can encode up to 31 relocations in a 32-bit object, and + // 63 relocations in a 64-bit object. + // + // This encoding has a couple of interesting properties: + // 1. Looking at any entry, it is clear whether it's an address or a bitmap: + // even means address, odd means bitmap. + // 2. Just a simple list of addresses is a valid encoding. + + size_t oldSize = relrRelocs.size(); + relrRelocs.clear(); + + // Same as Config->Wordsize but faster because this is a compile-time + // constant. + const size_t wordsize = sizeof(typename ELFT::uint); + + // Number of bits to use for the relocation offsets bitmap. + // Must be either 63 or 31. + const size_t nBits = wordsize * 8 - 1; + + // Get offsets for all relative relocations and sort them. + std::vector offsets; + for (const RelativeReloc &rel : relocs) + offsets.push_back(rel.getOffset()); + llvm::sort(offsets); + + // For each leading relocation, find following ones that can be folded + // as a bitmap and fold them. + for (size_t i = 0, e = offsets.size(); i < e;) { + // Add a leading relocation. + relrRelocs.push_back(Elf_Relr(offsets[i])); + uint64_t base = offsets[i] + wordsize; + ++i; + + // Find foldable relocations to construct bitmaps. + while (i < e) { + uint64_t bitmap = 0; + + while (i < e) { + uint64_t delta = offsets[i] - base; + + // If it is too far, it cannot be folded. + if (delta >= nBits * wordsize) + break; + + // If it is not a multiple of wordsize away, it cannot be folded. + if (delta % wordsize) + break; + + // Fold it. + bitmap |= 1ULL << (delta / wordsize); + ++i; + } + + if (!bitmap) + break; + + relrRelocs.push_back(Elf_Relr((bitmap << 1) | 1)); + base += nBits * wordsize; + } + } + + return relrRelocs.size() != oldSize; +} + +SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &strTabSec) + : SyntheticSection(strTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0, + strTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB, + config->wordsize, + strTabSec.isDynamic() ? ".dynsym" : ".symtab"), + strTabSec(strTabSec) {} + +// Orders symbols according to their positions in the GOT, +// in compliance with MIPS ABI rules. +// See "Global Offset Table" in Chapter 5 in the following document +// for detailed description: +// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf +static bool sortMipsSymbols(const SymbolTableEntry &l, + const SymbolTableEntry &r) { + // Sort entries related to non-local preemptible symbols by GOT indexes. + // All other entries go to the beginning of a dynsym in arbitrary order. + if (l.sym->isInGot() && r.sym->isInGot()) + return l.sym->gotIndex < r.sym->gotIndex; + if (!l.sym->isInGot() && !r.sym->isInGot()) + return false; + return !l.sym->isInGot(); +} + +void SymbolTableBaseSection::finalizeContents() { + if (OutputSection *sec = strTabSec.getParent()) + getParent()->link = sec->sectionIndex; + + if (this->type != SHT_DYNSYM) { + sortSymTabSymbols(); + return; + } + + // If it is a .dynsym, there should be no local symbols, but we need + // to do a few things for the dynamic linker. + + // Section's Info field has the index of the first non-local symbol. + // Because the first symbol entry is a null entry, 1 is the first. + getParent()->info = 1; + + if (getPartition().gnuHashTab) { + // NB: It also sorts Symbols to meet the GNU hash table requirements. + getPartition().gnuHashTab->addSymbols(symbols); + } else if (config->emachine == EM_MIPS) { + llvm::stable_sort(symbols, sortMipsSymbols); + } + + // Only the main partition's dynsym indexes are stored in the symbols + // themselves. All other partitions use a lookup table. + if (this == mainPart->dynSymTab) { + size_t i = 0; + for (const SymbolTableEntry &s : symbols) + s.sym->dynsymIndex = ++i; + } +} + +// The ELF spec requires that all local symbols precede global symbols, so we +// sort symbol entries in this function. (For .dynsym, we don't do that because +// symbols for dynamic linking are inherently all globals.) +// +// Aside from above, we put local symbols in groups starting with the STT_FILE +// symbol. That is convenient for purpose of identifying where are local symbols +// coming from. +void SymbolTableBaseSection::sortSymTabSymbols() { + // Move all local symbols before global symbols. + auto e = std::stable_partition( + symbols.begin(), symbols.end(), [](const SymbolTableEntry &s) { + return s.sym->isLocal() || s.sym->computeBinding() == STB_LOCAL; + }); + size_t numLocals = e - symbols.begin(); + getParent()->info = numLocals + 1; + + // We want to group the local symbols by file. For that we rebuild the local + // part of the symbols vector. We do not need to care about the STT_FILE + // symbols, they are already naturally placed first in each group. That + // happens because STT_FILE is always the first symbol in the object and hence + // precede all other local symbols we add for a file. + MapVector> arr; + for (const SymbolTableEntry &s : llvm::make_range(symbols.begin(), e)) + arr[s.sym->file].push_back(s); + + auto i = symbols.begin(); + for (std::pair> &p : arr) + for (SymbolTableEntry &entry : p.second) + *i++ = entry; +} + +void SymbolTableBaseSection::addSymbol(Symbol *b) { + // Adding a local symbol to a .dynsym is a bug. + assert(this->type != SHT_DYNSYM || !b->isLocal()); + + bool hashIt = b->isLocal(); + symbols.push_back({b, strTabSec.addString(b->getName(), hashIt)}); +} + +size_t SymbolTableBaseSection::getSymbolIndex(Symbol *sym) { + if (this == mainPart->dynSymTab) + return sym->dynsymIndex; + + // Initializes symbol lookup tables lazily. This is used only for -r, + // -emit-relocs and dynsyms in partitions other than the main one. + llvm::call_once(onceFlag, [&] { + symbolIndexMap.reserve(symbols.size()); + size_t i = 0; + for (const SymbolTableEntry &e : symbols) { + if (e.sym->type == STT_SECTION) + sectionIndexMap[e.sym->getOutputSection()] = ++i; + else + symbolIndexMap[e.sym] = ++i; + } + }); + + // Section symbols are mapped based on their output sections + // to maintain their semantics. + if (sym->type == STT_SECTION) + return sectionIndexMap.lookup(sym->getOutputSection()); + return symbolIndexMap.lookup(sym); +} + +template +SymbolTableSection::SymbolTableSection(StringTableSection &strTabSec) + : SymbolTableBaseSection(strTabSec) { + this->entsize = sizeof(Elf_Sym); +} + +static BssSection *getCommonSec(Symbol *sym) { + if (!config->defineCommon) + if (auto *d = dyn_cast(sym)) + return dyn_cast_or_null(d->section); + return nullptr; +} + +static uint32_t getSymSectionIndex(Symbol *sym) { + if (getCommonSec(sym)) + return SHN_COMMON; + if (!isa(sym) || sym->needsPltAddr) + return SHN_UNDEF; + if (const OutputSection *os = sym->getOutputSection()) + return os->sectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX + : os->sectionIndex; + return SHN_ABS; +} + +// Write the internal symbol table contents to the output symbol table. +template void SymbolTableSection::writeTo(uint8_t *buf) { + // The first entry is a null entry as per the ELF spec. + memset(buf, 0, sizeof(Elf_Sym)); + buf += sizeof(Elf_Sym); + + auto *eSym = reinterpret_cast(buf); + + for (SymbolTableEntry &ent : symbols) { + Symbol *sym = ent.sym; + bool isDefinedHere = type == SHT_SYMTAB || sym->partition == partition; + + // Set st_info and st_other. + eSym->st_other = 0; + if (sym->isLocal()) { + eSym->setBindingAndType(STB_LOCAL, sym->type); + } else { + eSym->setBindingAndType(sym->computeBinding(), sym->type); + eSym->setVisibility(sym->visibility); + } + + // The 3 most significant bits of st_other are used by OpenPOWER ABI. + // See getPPC64GlobalEntryToLocalEntryOffset() for more details. + if (config->emachine == EM_PPC64) + eSym->st_other |= sym->stOther & 0xe0; + + eSym->st_name = ent.strTabOffset; + if (isDefinedHere) + eSym->st_shndx = getSymSectionIndex(ent.sym); + else + eSym->st_shndx = 0; + + // Copy symbol size if it is a defined symbol. st_size is not significant + // for undefined symbols, so whether copying it or not is up to us if that's + // the case. We'll leave it as zero because by not setting a value, we can + // get the exact same outputs for two sets of input files that differ only + // in undefined symbol size in DSOs. + if (eSym->st_shndx == SHN_UNDEF || !isDefinedHere) + eSym->st_size = 0; + else + eSym->st_size = sym->getSize(); + + // st_value is usually an address of a symbol, but that has a + // special meaining for uninstantiated common symbols (this can + // occur if -r is given). + if (BssSection *commonSec = getCommonSec(ent.sym)) + eSym->st_value = commonSec->alignment; + else if (isDefinedHere) + eSym->st_value = sym->getVA(); + else + eSym->st_value = 0; + + ++eSym; + } + + // On MIPS we need to mark symbol which has a PLT entry and requires + // pointer equality by STO_MIPS_PLT flag. That is necessary to help + // dynamic linker distinguish such symbols and MIPS lazy-binding stubs. + // https://sourceware.org/ml/binutils/2008-07/txt00000.txt + if (config->emachine == EM_MIPS) { + auto *eSym = reinterpret_cast(buf); + + for (SymbolTableEntry &ent : symbols) { + Symbol *sym = ent.sym; + if (sym->isInPlt() && sym->needsPltAddr) + eSym->st_other |= STO_MIPS_PLT; + if (isMicroMips()) { + // We already set the less-significant bit for symbols + // marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT + // records. That allows us to distinguish such symbols in + // the `MIPS::relocateOne()` routine. Now we should + // clear that bit for non-dynamic symbol table, so tools + // like `objdump` will be able to deal with a correct + // symbol position. + if (sym->isDefined() && + ((sym->stOther & STO_MIPS_MICROMIPS) || sym->needsPltAddr)) { + if (!strTabSec.isDynamic()) + eSym->st_value &= ~1; + eSym->st_other |= STO_MIPS_MICROMIPS; + } + } + if (config->relocatable) + if (auto *d = dyn_cast(sym)) + if (isMipsPIC(d)) + eSym->st_other |= STO_MIPS_PIC; + ++eSym; + } + } +} + +SymtabShndxSection::SymtabShndxSection() + : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndx") { + this->entsize = 4; +} + +void SymtabShndxSection::writeTo(uint8_t *buf) { + // We write an array of 32 bit values, where each value has 1:1 association + // with an entry in .symtab. If the corresponding entry contains SHN_XINDEX, + // we need to write actual index, otherwise, we must write SHN_UNDEF(0). + buf += 4; // Ignore .symtab[0] entry. + for (const SymbolTableEntry &entry : in.symTab->getSymbols()) { + if (getSymSectionIndex(entry.sym) == SHN_XINDEX) + write32(buf, entry.sym->getOutputSection()->sectionIndex); + buf += 4; + } +} + +bool SymtabShndxSection::isNeeded() const { + // SHT_SYMTAB can hold symbols with section indices values up to + // SHN_LORESERVE. If we need more, we want to use extension SHT_SYMTAB_SHNDX + // section. Problem is that we reveal the final section indices a bit too + // late, and we do not know them here. For simplicity, we just always create + // a .symtab_shndx section when the amount of output sections is huge. + size_t size = 0; + for (BaseCommand *base : script->sectionCommands) + if (isa(base)) + ++size; + return size >= SHN_LORESERVE; +} + +void SymtabShndxSection::finalizeContents() { + getParent()->link = in.symTab->getParent()->sectionIndex; +} + +size_t SymtabShndxSection::getSize() const { + return in.symTab->getNumSymbols() * 4; +} + +// .hash and .gnu.hash sections contain on-disk hash tables that map +// symbol names to their dynamic symbol table indices. Their purpose +// is to help the dynamic linker resolve symbols quickly. If ELF files +// don't have them, the dynamic linker has to do linear search on all +// dynamic symbols, which makes programs slower. Therefore, a .hash +// section is added to a DSO by default. A .gnu.hash is added if you +// give the -hash-style=gnu or -hash-style=both option. +// +// The Unix semantics of resolving dynamic symbols is somewhat expensive. +// Each ELF file has a list of DSOs that the ELF file depends on and a +// list of dynamic symbols that need to be resolved from any of the +// DSOs. That means resolving all dynamic symbols takes O(m)*O(n) +// where m is the number of DSOs and n is the number of dynamic +// symbols. For modern large programs, both m and n are large. So +// making each step faster by using hash tables substiantially +// improves time to load programs. +// +// (Note that this is not the only way to design the shared library. +// For instance, the Windows DLL takes a different approach. On +// Windows, each dynamic symbol has a name of DLL from which the symbol +// has to be resolved. That makes the cost of symbol resolution O(n). +// This disables some hacky techniques you can use on Unix such as +// LD_PRELOAD, but this is arguably better semantics than the Unix ones.) +// +// Due to historical reasons, we have two different hash tables, .hash +// and .gnu.hash. They are for the same purpose, and .gnu.hash is a new +// and better version of .hash. .hash is just an on-disk hash table, but +// .gnu.hash has a bloom filter in addition to a hash table to skip +// DSOs very quickly. If you are sure that your dynamic linker knows +// about .gnu.hash, you want to specify -hash-style=gnu. Otherwise, a +// safe bet is to specify -hash-style=both for backward compatibilty. +GnuHashTableSection::GnuHashTableSection() + : SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, config->wordsize, ".gnu.hash") { +} + +void GnuHashTableSection::finalizeContents() { + if (OutputSection *sec = getPartition().dynSymTab->getParent()) + getParent()->link = sec->sectionIndex; + + // Computes bloom filter size in word size. We want to allocate 12 + // bits for each symbol. It must be a power of two. + if (symbols.empty()) { + maskWords = 1; + } else { + uint64_t numBits = symbols.size() * 12; + maskWords = NextPowerOf2(numBits / (config->wordsize * 8)); + } + + size = 16; // Header + size += config->wordsize * maskWords; // Bloom filter + size += nBuckets * 4; // Hash buckets + size += symbols.size() * 4; // Hash values +} + +void GnuHashTableSection::writeTo(uint8_t *buf) { + // The output buffer is not guaranteed to be zero-cleared because we pre- + // fill executable sections with trap instructions. This is a precaution + // for that case, which happens only when -no-rosegment is given. + memset(buf, 0, size); + + // Write a header. + write32(buf, nBuckets); + write32(buf + 4, getPartition().dynSymTab->getNumSymbols() - symbols.size()); + write32(buf + 8, maskWords); + write32(buf + 12, Shift2); + buf += 16; + + // Write a bloom filter and a hash table. + writeBloomFilter(buf); + buf += config->wordsize * maskWords; + writeHashTable(buf); +} + +// This function writes a 2-bit bloom filter. This bloom filter alone +// usually filters out 80% or more of all symbol lookups [1]. +// The dynamic linker uses the hash table only when a symbol is not +// filtered out by a bloom filter. +// +// [1] Ulrich Drepper (2011), "How To Write Shared Libraries" (Ver. 4.1.2), +// p.9, https://www.akkadia.org/drepper/dsohowto.pdf +void GnuHashTableSection::writeBloomFilter(uint8_t *buf) { + unsigned c = config->is64 ? 64 : 32; + for (const Entry &sym : symbols) { + // When C = 64, we choose a word with bits [6:...] and set 1 to two bits in + // the word using bits [0:5] and [26:31]. + size_t i = (sym.hash / c) & (maskWords - 1); + uint64_t val = readUint(buf + i * config->wordsize); + val |= uint64_t(1) << (sym.hash % c); + val |= uint64_t(1) << ((sym.hash >> Shift2) % c); + writeUint(buf + i * config->wordsize, val); + } +} + +void GnuHashTableSection::writeHashTable(uint8_t *buf) { + uint32_t *buckets = reinterpret_cast(buf); + uint32_t oldBucket = -1; + uint32_t *values = buckets + nBuckets; + for (auto i = symbols.begin(), e = symbols.end(); i != e; ++i) { + // Write a hash value. It represents a sequence of chains that share the + // same hash modulo value. The last element of each chain is terminated by + // LSB 1. + uint32_t hash = i->hash; + bool isLastInChain = (i + 1) == e || i->bucketIdx != (i + 1)->bucketIdx; + hash = isLastInChain ? hash | 1 : hash & ~1; + write32(values++, hash); + + if (i->bucketIdx == oldBucket) + continue; + // Write a hash bucket. Hash buckets contain indices in the following hash + // value table. + write32(buckets + i->bucketIdx, + getPartition().dynSymTab->getSymbolIndex(i->sym)); + oldBucket = i->bucketIdx; + } +} + +static uint32_t hashGnu(StringRef name) { + uint32_t h = 5381; + for (uint8_t c : name) + h = (h << 5) + h + c; + return h; +} + +// Add symbols to this symbol hash table. Note that this function +// destructively sort a given vector -- which is needed because +// GNU-style hash table places some sorting requirements. +void GnuHashTableSection::addSymbols(std::vector &v) { + // We cannot use 'auto' for Mid because GCC 6.1 cannot deduce + // its type correctly. + std::vector::iterator mid = + std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) { + return !s.sym->isDefined() || s.sym->partition != partition; + }); + + // We chose load factor 4 for the on-disk hash table. For each hash + // collision, the dynamic linker will compare a uint32_t hash value. + // Since the integer comparison is quite fast, we believe we can + // make the load factor even larger. 4 is just a conservative choice. + // + // Note that we don't want to create a zero-sized hash table because + // Android loader as of 2018 doesn't like a .gnu.hash containing such + // table. If that's the case, we create a hash table with one unused + // dummy slot. + nBuckets = std::max((v.end() - mid) / 4, 1); + + if (mid == v.end()) + return; + + for (SymbolTableEntry &ent : llvm::make_range(mid, v.end())) { + Symbol *b = ent.sym; + uint32_t hash = hashGnu(b->getName()); + uint32_t bucketIdx = hash % nBuckets; + symbols.push_back({b, ent.strTabOffset, hash, bucketIdx}); + } + + llvm::stable_sort(symbols, [](const Entry &l, const Entry &r) { + return l.bucketIdx < r.bucketIdx; + }); + + v.erase(mid, v.end()); + for (const Entry &ent : symbols) + v.push_back({ent.sym, ent.strTabOffset}); +} + +HashTableSection::HashTableSection() + : SyntheticSection(SHF_ALLOC, SHT_HASH, 4, ".hash") { + this->entsize = 4; +} + +void HashTableSection::finalizeContents() { + SymbolTableBaseSection *symTab = getPartition().dynSymTab; + + if (OutputSection *sec = symTab->getParent()) + getParent()->link = sec->sectionIndex; + + unsigned numEntries = 2; // nbucket and nchain. + numEntries += symTab->getNumSymbols(); // The chain entries. + + // Create as many buckets as there are symbols. + numEntries += symTab->getNumSymbols(); + this->size = numEntries * 4; +} + +void HashTableSection::writeTo(uint8_t *buf) { + SymbolTableBaseSection *symTab = getPartition().dynSymTab; + + // See comment in GnuHashTableSection::writeTo. + memset(buf, 0, size); + + unsigned numSymbols = symTab->getNumSymbols(); + + uint32_t *p = reinterpret_cast(buf); + write32(p++, numSymbols); // nbucket + write32(p++, numSymbols); // nchain + + uint32_t *buckets = p; + uint32_t *chains = p + numSymbols; + + for (const SymbolTableEntry &s : symTab->getSymbols()) { + Symbol *sym = s.sym; + StringRef name = sym->getName(); + unsigned i = sym->dynsymIndex; + uint32_t hash = hashSysV(name) % numSymbols; + chains[i] = buckets[hash]; + write32(buckets + hash, i); + } +} + +// On PowerPC64 the lazy symbol resolvers go into the `global linkage table` +// in the .glink section, rather then the typical .plt section. +PltSection::PltSection(bool isIplt) + : SyntheticSection( + SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, + (config->emachine == EM_PPC || config->emachine == EM_PPC64) + ? ".glink" + : ".plt"), + headerSize(!isIplt || config->zRetpolineplt ? target->pltHeaderSize : 0), + isIplt(isIplt) { + // The PLT needs to be writable on SPARC as the dynamic linker will + // modify the instructions in the PLT entries. + if (config->emachine == EM_SPARCV9) + this->flags |= SHF_WRITE; +} + +void PltSection::writeTo(uint8_t *buf) { + if (config->emachine == EM_PPC) { + writePPC32GlinkSection(buf, entries.size()); + return; + } + + // At beginning of PLT or retpoline IPLT, we have code to call the dynamic + // linker to resolve dynsyms at runtime. Write such code. + if (headerSize) + target->writePltHeader(buf); + size_t off = headerSize; + + RelocationBaseSection *relSec = isIplt ? in.relaIplt : in.relaPlt; + + // The IPlt is immediately after the Plt, account for this in relOff + size_t pltOff = isIplt ? in.plt->getSize() : 0; + + for (size_t i = 0, e = entries.size(); i != e; ++i) { + const Symbol *b = entries[i]; + unsigned relOff = relSec->entsize * i + pltOff; + uint64_t got = b->getGotPltVA(); + uint64_t plt = this->getVA() + off; + target->writePlt(buf + off, got, plt, b->pltIndex, relOff); + off += target->pltEntrySize; + } +} + +template void PltSection::addEntry(Symbol &sym) { + sym.pltIndex = entries.size(); + entries.push_back(&sym); +} + +size_t PltSection::getSize() const { + return headerSize + entries.size() * target->pltEntrySize; +} + +// Some architectures such as additional symbols in the PLT section. For +// example ARM uses mapping symbols to aid disassembly +void PltSection::addSymbols() { + // The PLT may have symbols defined for the Header, the IPLT has no header + if (!isIplt) + target->addPltHeaderSymbols(*this); + + size_t off = headerSize; + for (size_t i = 0; i < entries.size(); ++i) { + target->addPltSymbols(*this, off); + off += target->pltEntrySize; + } +} + +// The string hash function for .gdb_index. +static uint32_t computeGdbHash(StringRef s) { + uint32_t h = 0; + for (uint8_t c : s) + h = h * 67 + toLower(c) - 113; + return h; +} + +GdbIndexSection::GdbIndexSection() + : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index") {} + +// Returns the desired size of an on-disk hash table for a .gdb_index section. +// There's a tradeoff between size and collision rate. We aim 75% utilization. +size_t GdbIndexSection::computeSymtabSize() const { + return std::max(NextPowerOf2(symbols.size() * 4 / 3), 1024); +} + +// Compute the output section size. +void GdbIndexSection::initOutputSize() { + size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8; + + for (GdbChunk &chunk : chunks) + size += chunk.compilationUnits.size() * 16 + chunk.addressAreas.size() * 20; + + // Add the constant pool size if exists. + if (!symbols.empty()) { + GdbSymbol &sym = symbols.back(); + size += sym.nameOff + sym.name.size() + 1; + } +} + +static std::vector getDebugInfoSections() { + std::vector ret; + for (InputSectionBase *s : inputSections) + if (InputSection *isec = dyn_cast(s)) + if (isec->name == ".debug_info") + ret.push_back(isec); + return ret; +} + +static std::vector readCuList(DWARFContext &dwarf) { + std::vector ret; + for (std::unique_ptr &cu : dwarf.compile_units()) + ret.push_back({cu->getOffset(), cu->getLength() + 4}); + return ret; +} + +static std::vector +readAddressAreas(DWARFContext &dwarf, InputSection *sec) { + std::vector ret; + + uint32_t cuIdx = 0; + for (std::unique_ptr &cu : dwarf.compile_units()) { + Expected ranges = cu->collectAddressRanges(); + if (!ranges) { + error(toString(sec) + ": " + toString(ranges.takeError())); + return {}; + } + + ArrayRef sections = sec->file->getSections(); + for (DWARFAddressRange &r : *ranges) { + if (r.SectionIndex == -1ULL) + continue; + InputSectionBase *s = sections[r.SectionIndex]; + if (!s || s == &InputSection::discarded || !s->isLive()) + continue; + // Range list with zero size has no effect. + if (r.LowPC == r.HighPC) + continue; + auto *isec = cast(s); + uint64_t offset = isec->getOffsetInFile(); + ret.push_back({isec, r.LowPC - offset, r.HighPC - offset, cuIdx}); + } + ++cuIdx; + } + + return ret; +} + +template +static std::vector +readPubNamesAndTypes(const LLDDwarfObj &obj, + const std::vector &cUs) { + const DWARFSection &pubNames = obj.getGnuPubNamesSection(); + const DWARFSection &pubTypes = obj.getGnuPubTypesSection(); + + std::vector ret; + for (const DWARFSection *pub : {&pubNames, &pubTypes}) { + DWARFDebugPubTable table(obj, *pub, config->isLE, true); + for (const DWARFDebugPubTable::Set &set : table.getData()) { + // The value written into the constant pool is kind << 24 | cuIndex. As we + // don't know how many compilation units precede this object to compute + // cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add + // the number of preceding compilation units later. + uint32_t i = + lower_bound(cUs, set.Offset, + [](GdbIndexSection::CuEntry cu, uint32_t offset) { + return cu.cuOffset < offset; + }) - + cUs.begin(); + for (const DWARFDebugPubTable::Entry &ent : set.Entries) + ret.push_back({{ent.Name, computeGdbHash(ent.Name)}, + (ent.Descriptor.toBits() << 24) | i}); + } + } + return ret; +} + +// Create a list of symbols from a given list of symbol names and types +// by uniquifying them by name. +static std::vector +createSymbols(ArrayRef> nameAttrs, + const std::vector &chunks) { + using GdbSymbol = GdbIndexSection::GdbSymbol; + using NameAttrEntry = GdbIndexSection::NameAttrEntry; + + // For each chunk, compute the number of compilation units preceding it. + uint32_t cuIdx = 0; + std::vector cuIdxs(chunks.size()); + for (uint32_t i = 0, e = chunks.size(); i != e; ++i) { + cuIdxs[i] = cuIdx; + cuIdx += chunks[i].compilationUnits.size(); + } + + // The number of symbols we will handle in this function is of the order + // of millions for very large executables, so we use multi-threading to + // speed it up. + size_t numShards = 32; + size_t concurrency = 1; + if (threadsEnabled) + concurrency = + std::min(PowerOf2Floor(hardware_concurrency()), numShards); + + // A sharded map to uniquify symbols by name. + std::vector> map(numShards); + size_t shift = 32 - countTrailingZeros(numShards); + + // Instantiate GdbSymbols while uniqufying them by name. + std::vector> symbols(numShards); + parallelForEachN(0, concurrency, [&](size_t threadId) { + uint32_t i = 0; + for (ArrayRef entries : nameAttrs) { + for (const NameAttrEntry &ent : entries) { + size_t shardId = ent.name.hash() >> shift; + if ((shardId & (concurrency - 1)) != threadId) + continue; + + uint32_t v = ent.cuIndexAndAttrs + cuIdxs[i]; + size_t &idx = map[shardId][ent.name]; + if (idx) { + symbols[shardId][idx - 1].cuVector.push_back(v); + continue; + } + + idx = symbols[shardId].size() + 1; + symbols[shardId].push_back({ent.name, {v}, 0, 0}); + } + ++i; + } + }); + + size_t numSymbols = 0; + for (ArrayRef v : symbols) + numSymbols += v.size(); + + // The return type is a flattened vector, so we'll copy each vector + // contents to Ret. + std::vector ret; + ret.reserve(numSymbols); + for (std::vector &vec : symbols) + for (GdbSymbol &sym : vec) + ret.push_back(std::move(sym)); + + // CU vectors and symbol names are adjacent in the output file. + // We can compute their offsets in the output file now. + size_t off = 0; + for (GdbSymbol &sym : ret) { + sym.cuVectorOff = off; + off += (sym.cuVector.size() + 1) * 4; + } + for (GdbSymbol &sym : ret) { + sym.nameOff = off; + off += sym.name.size() + 1; + } + + return ret; +} + +// Returns a newly-created .gdb_index section. +template GdbIndexSection *GdbIndexSection::create() { + std::vector sections = getDebugInfoSections(); + + // .debug_gnu_pub{names,types} are useless in executables. + // They are present in input object files solely for creating + // a .gdb_index. So we can remove them from the output. + for (InputSectionBase *s : inputSections) + if (s->name == ".debug_gnu_pubnames" || s->name == ".debug_gnu_pubtypes") + s->markDead(); + + std::vector chunks(sections.size()); + std::vector> nameAttrs(sections.size()); + + parallelForEachN(0, sections.size(), [&](size_t i) { + ObjFile *file = sections[i]->getFile(); + DWARFContext dwarf(make_unique>(file)); + + chunks[i].sec = sections[i]; + chunks[i].compilationUnits = readCuList(dwarf); + chunks[i].addressAreas = readAddressAreas(dwarf, sections[i]); + nameAttrs[i] = readPubNamesAndTypes( + static_cast &>(dwarf.getDWARFObj()), + chunks[i].compilationUnits); + }); + + auto *ret = make(); + ret->chunks = std::move(chunks); + ret->symbols = createSymbols(nameAttrs, ret->chunks); + ret->initOutputSize(); + return ret; +} + +void GdbIndexSection::writeTo(uint8_t *buf) { + // Write the header. + auto *hdr = reinterpret_cast(buf); + uint8_t *start = buf; + hdr->version = 7; + buf += sizeof(*hdr); + + // Write the CU list. + hdr->cuListOff = buf - start; + for (GdbChunk &chunk : chunks) { + for (CuEntry &cu : chunk.compilationUnits) { + write64le(buf, chunk.sec->outSecOff + cu.cuOffset); + write64le(buf + 8, cu.cuLength); + buf += 16; + } + } + + // Write the address area. + hdr->cuTypesOff = buf - start; + hdr->addressAreaOff = buf - start; + uint32_t cuOff = 0; + for (GdbChunk &chunk : chunks) { + for (AddressEntry &e : chunk.addressAreas) { + uint64_t baseAddr = e.section->getVA(0); + write64le(buf, baseAddr + e.lowAddress); + write64le(buf + 8, baseAddr + e.highAddress); + write32le(buf + 16, e.cuIndex + cuOff); + buf += 20; + } + cuOff += chunk.compilationUnits.size(); + } + + // Write the on-disk open-addressing hash table containing symbols. + hdr->symtabOff = buf - start; + size_t symtabSize = computeSymtabSize(); + uint32_t mask = symtabSize - 1; + + for (GdbSymbol &sym : symbols) { + uint32_t h = sym.name.hash(); + uint32_t i = h & mask; + uint32_t step = ((h * 17) & mask) | 1; + + while (read32le(buf + i * 8)) + i = (i + step) & mask; + + write32le(buf + i * 8, sym.nameOff); + write32le(buf + i * 8 + 4, sym.cuVectorOff); + } + + buf += symtabSize * 8; + + // Write the string pool. + hdr->constantPoolOff = buf - start; + parallelForEach(symbols, [&](GdbSymbol &sym) { + memcpy(buf + sym.nameOff, sym.name.data(), sym.name.size()); + }); + + // Write the CU vectors. + for (GdbSymbol &sym : symbols) { + write32le(buf, sym.cuVector.size()); + buf += 4; + for (uint32_t val : sym.cuVector) { + write32le(buf, val); + buf += 4; + } + } +} + +bool GdbIndexSection::isNeeded() const { return !chunks.empty(); } + +EhFrameHeader::EhFrameHeader() + : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {} + +void EhFrameHeader::writeTo(uint8_t *buf) { + // Unlike most sections, the EhFrameHeader section is written while writing + // another section, namely EhFrameSection, which calls the write() function + // below from its writeTo() function. This is necessary because the contents + // of EhFrameHeader depend on the relocated contents of EhFrameSection and we + // don't know which order the sections will be written in. +} + +// .eh_frame_hdr contains a binary search table of pointers to FDEs. +// Each entry of the search table consists of two values, +// the starting PC from where FDEs covers, and the FDE's address. +// It is sorted by PC. +void EhFrameHeader::write() { + uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff; + using FdeData = EhFrameSection::FdeData; + + std::vector fdes = getPartition().ehFrame->getFdeData(); + + buf[0] = 1; + buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; + buf[2] = DW_EH_PE_udata4; + buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; + write32(buf + 4, + getPartition().ehFrame->getParent()->addr - this->getVA() - 4); + write32(buf + 8, fdes.size()); + buf += 12; + + for (FdeData &fde : fdes) { + write32(buf, fde.pcRel); + write32(buf + 4, fde.fdeVARel); + buf += 8; + } +} + +size_t EhFrameHeader::getSize() const { + // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs. + return 12 + getPartition().ehFrame->numFdes * 8; +} + +bool EhFrameHeader::isNeeded() const { + return isLive() && getPartition().ehFrame->isNeeded(); +} + +VersionDefinitionSection::VersionDefinitionSection() + : SyntheticSection(SHF_ALLOC, SHT_GNU_verdef, sizeof(uint32_t), + ".gnu.version_d") {} + +StringRef VersionDefinitionSection::getFileDefName() { + if (!getPartition().name.empty()) + return getPartition().name; + if (!config->soName.empty()) + return config->soName; + return config->outputFile; +} + +void VersionDefinitionSection::finalizeContents() { + fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName()); + for (VersionDefinition &v : config->versionDefinitions) + verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name)); + + if (OutputSection *sec = getPartition().dynStrTab->getParent()) + getParent()->link = sec->sectionIndex; + + // sh_info should be set to the number of definitions. This fact is missed in + // documentation, but confirmed by binutils community: + // https://sourceware.org/ml/binutils/2014-11/msg00355.html + getParent()->info = getVerDefNum(); +} + +void VersionDefinitionSection::writeOne(uint8_t *buf, uint32_t index, + StringRef name, size_t nameOff) { + uint16_t flags = index == 1 ? VER_FLG_BASE : 0; + + // Write a verdef. + write16(buf, 1); // vd_version + write16(buf + 2, flags); // vd_flags + write16(buf + 4, index); // vd_ndx + write16(buf + 6, 1); // vd_cnt + write32(buf + 8, hashSysV(name)); // vd_hash + write32(buf + 12, 20); // vd_aux + write32(buf + 16, 28); // vd_next + + // Write a veraux. + write32(buf + 20, nameOff); // vda_name + write32(buf + 24, 0); // vda_next +} + +void VersionDefinitionSection::writeTo(uint8_t *buf) { + writeOne(buf, 1, getFileDefName(), fileDefNameOff); + + auto nameOffIt = verDefNameOffs.begin(); + for (VersionDefinition &v : config->versionDefinitions) { + buf += EntrySize; + writeOne(buf, v.id, v.name, *nameOffIt++); + } + + // Need to terminate the last version definition. + write32(buf + 16, 0); // vd_next +} + +size_t VersionDefinitionSection::getSize() const { + return EntrySize * getVerDefNum(); +} + +// .gnu.version is a table where each entry is 2 byte long. +VersionTableSection::VersionTableSection() + : SyntheticSection(SHF_ALLOC, SHT_GNU_versym, sizeof(uint16_t), + ".gnu.version") { + this->entsize = 2; +} + +void VersionTableSection::finalizeContents() { + // At the moment of june 2016 GNU docs does not mention that sh_link field + // should be set, but Sun docs do. Also readelf relies on this field. + getParent()->link = getPartition().dynSymTab->getParent()->sectionIndex; +} + +size_t VersionTableSection::getSize() const { + return (getPartition().dynSymTab->getSymbols().size() + 1) * 2; +} + +void VersionTableSection::writeTo(uint8_t *buf) { + buf += 2; + for (const SymbolTableEntry &s : getPartition().dynSymTab->getSymbols()) { + write16(buf, s.sym->versionId); + buf += 2; + } +} + +bool VersionTableSection::isNeeded() const { + return getPartition().verDef || getPartition().verNeed->isNeeded(); +} + +void elf::addVerneed(Symbol *ss) { + auto &file = cast(*ss->file); + if (ss->verdefIndex == VER_NDX_GLOBAL) { + ss->versionId = VER_NDX_GLOBAL; + return; + } + + if (file.vernauxs.empty()) + file.vernauxs.resize(file.verdefs.size()); + + // Select a version identifier for the vernaux data structure, if we haven't + // already allocated one. The verdef identifiers cover the range + // [1..getVerDefNum()]; this causes the vernaux identifiers to start from + // getVerDefNum()+1. + if (file.vernauxs[ss->verdefIndex] == 0) + file.vernauxs[ss->verdefIndex] = ++SharedFile::vernauxNum + getVerDefNum(); + + ss->versionId = file.vernauxs[ss->verdefIndex]; +} + +template +VersionNeedSection::VersionNeedSection() + : SyntheticSection(SHF_ALLOC, SHT_GNU_verneed, sizeof(uint32_t), + ".gnu.version_r") {} + +template void VersionNeedSection::finalizeContents() { + for (SharedFile *f : sharedFiles) { + if (f->vernauxs.empty()) + continue; + verneeds.emplace_back(); + Verneed &vn = verneeds.back(); + vn.nameStrTab = getPartition().dynStrTab->addString(f->soName); + for (unsigned i = 0; i != f->vernauxs.size(); ++i) { + if (f->vernauxs[i] == 0) + continue; + auto *verdef = + reinterpret_cast(f->verdefs[i]); + vn.vernauxs.push_back( + {verdef->vd_hash, f->vernauxs[i], + getPartition().dynStrTab->addString(f->getStringTable().data() + + verdef->getAux()->vda_name)}); + } + } + + if (OutputSection *sec = getPartition().dynStrTab->getParent()) + getParent()->link = sec->sectionIndex; + getParent()->info = verneeds.size(); +} + +template void VersionNeedSection::writeTo(uint8_t *buf) { + // The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs. + auto *verneed = reinterpret_cast(buf); + auto *vernaux = reinterpret_cast(verneed + verneeds.size()); + + for (auto &vn : verneeds) { + // Create an Elf_Verneed for this DSO. + verneed->vn_version = 1; + verneed->vn_cnt = vn.vernauxs.size(); + verneed->vn_file = vn.nameStrTab; + verneed->vn_aux = + reinterpret_cast(vernaux) - reinterpret_cast(verneed); + verneed->vn_next = sizeof(Elf_Verneed); + ++verneed; + + // Create the Elf_Vernauxs for this Elf_Verneed. + for (auto &vna : vn.vernauxs) { + vernaux->vna_hash = vna.hash; + vernaux->vna_flags = 0; + vernaux->vna_other = vna.verneedIndex; + vernaux->vna_name = vna.nameStrTab; + vernaux->vna_next = sizeof(Elf_Vernaux); + ++vernaux; + } + + vernaux[-1].vna_next = 0; + } + verneed[-1].vn_next = 0; +} + +template size_t VersionNeedSection::getSize() const { + return verneeds.size() * sizeof(Elf_Verneed) + + SharedFile::vernauxNum * sizeof(Elf_Vernaux); +} + +template bool VersionNeedSection::isNeeded() const { + return SharedFile::vernauxNum != 0; +} + +void MergeSyntheticSection::addSection(MergeInputSection *ms) { + ms->parent = this; + sections.push_back(ms); + assert(alignment == ms->alignment || !(ms->flags & SHF_STRINGS)); + alignment = std::max(alignment, ms->alignment); +} + +MergeTailSection::MergeTailSection(StringRef name, uint32_t type, + uint64_t flags, uint32_t alignment) + : MergeSyntheticSection(name, type, flags, alignment), + builder(StringTableBuilder::RAW, alignment) {} + +size_t MergeTailSection::getSize() const { return builder.getSize(); } + +void MergeTailSection::writeTo(uint8_t *buf) { builder.write(buf); } + +void MergeTailSection::finalizeContents() { + // Add all string pieces to the string table builder to create section + // contents. + for (MergeInputSection *sec : sections) + for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) + if (sec->pieces[i].live) + builder.add(sec->getData(i)); + + // Fix the string table content. After this, the contents will never change. + builder.finalize(); + + // finalize() fixed tail-optimized strings, so we can now get + // offsets of strings. Get an offset for each string and save it + // to a corresponding SectionPiece for easy access. + for (MergeInputSection *sec : sections) + for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) + if (sec->pieces[i].live) + sec->pieces[i].outputOff = builder.getOffset(sec->getData(i)); +} + +void MergeNoTailSection::writeTo(uint8_t *buf) { + for (size_t i = 0; i < numShards; ++i) + shards[i].write(buf + shardOffsets[i]); +} + +// This function is very hot (i.e. it can take several seconds to finish) +// because sometimes the number of inputs is in an order of magnitude of +// millions. So, we use multi-threading. +// +// For any strings S and T, we know S is not mergeable with T if S's hash +// value is different from T's. If that's the case, we can safely put S and +// T into different string builders without worrying about merge misses. +// We do it in parallel. +void MergeNoTailSection::finalizeContents() { + // Initializes string table builders. + for (size_t i = 0; i < numShards; ++i) + shards.emplace_back(StringTableBuilder::RAW, alignment); + + // Concurrency level. Must be a power of 2 to avoid expensive modulo + // operations in the following tight loop. + size_t concurrency = 1; + if (threadsEnabled) + concurrency = + std::min(PowerOf2Floor(hardware_concurrency()), numShards); + + // Add section pieces to the builders. + parallelForEachN(0, concurrency, [&](size_t threadId) { + for (MergeInputSection *sec : sections) { + for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) { + if (!sec->pieces[i].live) + continue; + size_t shardId = getShardId(sec->pieces[i].hash); + if ((shardId & (concurrency - 1)) == threadId) + sec->pieces[i].outputOff = shards[shardId].add(sec->getData(i)); + } + } + }); + + // Compute an in-section offset for each shard. + size_t off = 0; + for (size_t i = 0; i < numShards; ++i) { + shards[i].finalizeInOrder(); + if (shards[i].getSize() > 0) + off = alignTo(off, alignment); + shardOffsets[i] = off; + off += shards[i].getSize(); + } + size = off; + + // So far, section pieces have offsets from beginning of shards, but + // we want offsets from beginning of the whole section. Fix them. + parallelForEach(sections, [&](MergeInputSection *sec) { + for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) + if (sec->pieces[i].live) + sec->pieces[i].outputOff += + shardOffsets[getShardId(sec->pieces[i].hash)]; + }); +} + +static MergeSyntheticSection *createMergeSynthetic(StringRef name, + uint32_t type, + uint64_t flags, + uint32_t alignment) { + bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2; + if (shouldTailMerge) + return make(name, type, flags, alignment); + return make(name, type, flags, alignment); +} + +template void elf::splitSections() { + // splitIntoPieces needs to be called on each MergeInputSection + // before calling finalizeContents(). + parallelForEach(inputSections, [](InputSectionBase *sec) { + if (auto *s = dyn_cast(sec)) + s->splitIntoPieces(); + else if (auto *eh = dyn_cast(sec)) + eh->split(); + }); +} + +// This function scans over the inputsections to create mergeable +// synthetic sections. +// +// It removes MergeInputSections from the input section array and adds +// new synthetic sections at the location of the first input section +// that it replaces. It then finalizes each synthetic section in order +// to compute an output offset for each piece of each input section. +void elf::mergeSections() { + std::vector mergeSections; + for (InputSectionBase *&s : inputSections) { + MergeInputSection *ms = dyn_cast(s); + if (!ms) + continue; + + // We do not want to handle sections that are not alive, so just remove + // them instead of trying to merge. + if (!ms->isLive()) { + s = nullptr; + continue; + } + + StringRef outsecName = getOutputSectionName(ms); + + auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) { + // While we could create a single synthetic section for two different + // values of Entsize, it is better to take Entsize into consideration. + // + // With a single synthetic section no two pieces with different Entsize + // could be equal, so we may as well have two sections. + // + // Using Entsize in here also allows us to propagate it to the synthetic + // section. + // + // SHF_STRINGS section with different alignments should not be merged. + return sec->name == outsecName && sec->flags == ms->flags && + sec->entsize == ms->entsize && + (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS)); + }); + if (i == mergeSections.end()) { + MergeSyntheticSection *syn = + createMergeSynthetic(outsecName, ms->type, ms->flags, ms->alignment); + mergeSections.push_back(syn); + i = std::prev(mergeSections.end()); + s = syn; + syn->entsize = ms->entsize; + } else { + s = nullptr; + } + (*i)->addSection(ms); + } + for (auto *ms : mergeSections) + ms->finalizeContents(); + + std::vector &v = inputSections; + v.erase(std::remove(v.begin(), v.end(), nullptr), v.end()); +} + +MipsRldMapSection::MipsRldMapSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize, + ".rld_map") {} + +ARMExidxSyntheticSection::ARMExidxSyntheticSection() + : SyntheticSection(SHF_ALLOC | SHF_LINK_ORDER, SHT_ARM_EXIDX, + config->wordsize, ".ARM.exidx") {} + +static InputSection *findExidxSection(InputSection *isec) { + for (InputSection *d : isec->dependentSections) + if (d->type == SHT_ARM_EXIDX) + return d; + return nullptr; +} + +bool ARMExidxSyntheticSection::addSection(InputSection *isec) { + if (isec->type == SHT_ARM_EXIDX) { + exidxSections.push_back(isec); + return true; + } + + if ((isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) && + isec->getSize() > 0) { + executableSections.push_back(isec); + if (empty && findExidxSection(isec)) + empty = false; + return false; + } + + // FIXME: we do not output a relocation section when --emit-relocs is used + // as we do not have relocation sections for linker generated table entries + // and we would have to erase at a late stage relocations from merged entries. + // Given that exception tables are already position independent and a binary + // analyzer could derive the relocations we choose to erase the relocations. + if (config->emitRelocs && isec->type == SHT_REL) + if (InputSectionBase *ex = isec->getRelocatedSection()) + if (isa(ex) && ex->type == SHT_ARM_EXIDX) + return true; + + return false; +} + +// References to .ARM.Extab Sections have bit 31 clear and are not the +// special EXIDX_CANTUNWIND bit-pattern. +static bool isExtabRef(uint32_t unwind) { + return (unwind & 0x80000000) == 0 && unwind != 0x1; +} + +// Return true if the .ARM.exidx section Cur can be merged into the .ARM.exidx +// section Prev, where Cur follows Prev in the table. This can be done if the +// unwinding instructions in Cur are identical to Prev. Linker generated +// EXIDX_CANTUNWIND entries are represented by nullptr as they do not have an +// InputSection. +static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) { + + struct ExidxEntry { + ulittle32_t fn; + ulittle32_t unwind; + }; + // Get the last table Entry from the previous .ARM.exidx section. If Prev is + // nullptr then it will be a synthesized EXIDX_CANTUNWIND entry. + ExidxEntry prevEntry = {ulittle32_t(0), ulittle32_t(1)}; + if (prev) + prevEntry = prev->getDataAs().back(); + if (isExtabRef(prevEntry.unwind)) + return false; + + // We consider the unwind instructions of an .ARM.exidx table entry + // a duplicate if the previous unwind instructions if: + // - Both are the special EXIDX_CANTUNWIND. + // - Both are the same inline unwind instructions. + // We do not attempt to follow and check links into .ARM.extab tables as + // consecutive identical entries are rare and the effort to check that they + // are identical is high. + + // If Cur is nullptr then this is synthesized EXIDX_CANTUNWIND entry. + if (cur == nullptr) + return prevEntry.unwind == 1; + + for (const ExidxEntry entry : cur->getDataAs()) + if (isExtabRef(entry.unwind) || entry.unwind != prevEntry.unwind) + return false; + + // All table entries in this .ARM.exidx Section can be merged into the + // previous Section. + return true; +} + +// The .ARM.exidx table must be sorted in ascending order of the address of the +// functions the table describes. Optionally duplicate adjacent table entries +// can be removed. At the end of the function the executableSections must be +// sorted in ascending order of address, Sentinel is set to the InputSection +// with the highest address and any InputSections that have mergeable +// .ARM.exidx table entries are removed from it. +void ARMExidxSyntheticSection::finalizeContents() { + if (script->hasSectionsCommand) { + // The executableSections and exidxSections that we use to derive the + // final contents of this SyntheticSection are populated before the + // linker script assigns InputSections to OutputSections. The linker script + // SECTIONS command may have a /DISCARD/ entry that removes executable + // InputSections and their dependent .ARM.exidx section that we recorded + // earlier. + auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); }; + llvm::erase_if(executableSections, isDiscarded); + llvm::erase_if(exidxSections, isDiscarded); + } + + // Sort the executable sections that may or may not have associated + // .ARM.exidx sections by order of ascending address. This requires the + // relative positions of InputSections to be known. + auto compareByFilePosition = [](const InputSection *a, + const InputSection *b) { + OutputSection *aOut = a->getParent(); + OutputSection *bOut = b->getParent(); + + if (aOut != bOut) + return aOut->sectionIndex < bOut->sectionIndex; + return a->outSecOff < b->outSecOff; + }; + llvm::stable_sort(executableSections, compareByFilePosition); + sentinel = executableSections.back(); + // Optionally merge adjacent duplicate entries. + if (config->mergeArmExidx) { + std::vector selectedSections; + selectedSections.reserve(executableSections.size()); + selectedSections.push_back(executableSections[0]); + size_t prev = 0; + for (size_t i = 1; i < executableSections.size(); ++i) { + InputSection *ex1 = findExidxSection(executableSections[prev]); + InputSection *ex2 = findExidxSection(executableSections[i]); + if (!isDuplicateArmExidxSec(ex1, ex2)) { + selectedSections.push_back(executableSections[i]); + prev = i; + } + } + executableSections = std::move(selectedSections); + } + + size_t offset = 0; + size = 0; + for (InputSection *isec : executableSections) { + if (InputSection *d = findExidxSection(isec)) { + d->outSecOff = offset; + d->parent = getParent(); + offset += d->getSize(); + } else { + offset += 8; + } + } + // Size includes Sentinel. + size = offset + 8; +} + +InputSection *ARMExidxSyntheticSection::getLinkOrderDep() const { + return executableSections.front(); +} + +// To write the .ARM.exidx table from the ExecutableSections we have three cases +// 1.) The InputSection has a .ARM.exidx InputSection in its dependent sections. +// We write the .ARM.exidx section contents and apply its relocations. +// 2.) The InputSection does not have a dependent .ARM.exidx InputSection. We +// must write the contents of an EXIDX_CANTUNWIND directly. We use the +// start of the InputSection as the purpose of the linker generated +// section is to terminate the address range of the previous entry. +// 3.) A trailing EXIDX_CANTUNWIND sentinel section is required at the end of +// the table to terminate the address range of the final entry. +void ARMExidxSyntheticSection::writeTo(uint8_t *buf) { + + const uint8_t cantUnwindData[8] = {0, 0, 0, 0, // PREL31 to target + 1, 0, 0, 0}; // EXIDX_CANTUNWIND + + uint64_t offset = 0; + for (InputSection *isec : executableSections) { + assert(isec->getParent() != nullptr); + if (InputSection *d = findExidxSection(isec)) { + memcpy(buf + offset, d->data().data(), d->data().size()); + d->relocateAlloc(buf, buf + d->getSize()); + offset += d->getSize(); + } else { + // A Linker generated CANTUNWIND section. + memcpy(buf + offset, cantUnwindData, sizeof(cantUnwindData)); + uint64_t s = isec->getVA(); + uint64_t p = getVA() + offset; + target->relocateOne(buf + offset, R_ARM_PREL31, s - p); + offset += 8; + } + } + // Write Sentinel. + memcpy(buf + offset, cantUnwindData, sizeof(cantUnwindData)); + uint64_t s = sentinel->getVA(sentinel->getSize()); + uint64_t p = getVA() + offset; + target->relocateOne(buf + offset, R_ARM_PREL31, s - p); + assert(size == offset + 8); +} + +bool ARMExidxSyntheticSection::classof(const SectionBase *d) { + return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX; +} + +ThunkSection::ThunkSection(OutputSection *os, uint64_t off) + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, + config->wordsize, ".text.thunk") { + this->parent = os; + this->outSecOff = off; +} + +void ThunkSection::addThunk(Thunk *t) { + thunks.push_back(t); + t->addSymbols(*this); +} + +void ThunkSection::writeTo(uint8_t *buf) { + for (Thunk *t : thunks) + t->writeTo(buf + t->offset); +} + +InputSection *ThunkSection::getTargetInputSection() const { + if (thunks.empty()) + return nullptr; + const Thunk *t = thunks.front(); + return t->getTargetInputSection(); +} + +bool ThunkSection::assignOffsets() { + uint64_t off = 0; + for (Thunk *t : thunks) { + off = alignTo(off, t->alignment); + t->setOffset(off); + uint32_t size = t->size(); + t->getThunkTargetSym()->size = size; + off += size; + } + bool changed = off != size; + size = off; + return changed; +} + +PPC32Got2Section::PPC32Got2Section() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 4, ".got2") {} + +bool PPC32Got2Section::isNeeded() const { + // See the comment below. This is not needed if there is no other + // InputSection. + for (BaseCommand *base : getParent()->sectionCommands) + if (auto *isd = dyn_cast(base)) + for (InputSection *isec : isd->sections) + if (isec != this) + return true; + return false; +} + +void PPC32Got2Section::finalizeContents() { + // PPC32 may create multiple GOT sections for -fPIC/-fPIE, one per file in + // .got2 . This function computes outSecOff of each .got2 to be used in + // PPC32PltCallStub::writeTo(). The purpose of this empty synthetic section is + // to collect input sections named ".got2". + uint32_t offset = 0; + for (BaseCommand *base : getParent()->sectionCommands) + if (auto *isd = dyn_cast(base)) { + for (InputSection *isec : isd->sections) { + if (isec == this) + continue; + isec->file->ppc32Got2OutSecOff = offset; + offset += (uint32_t)isec->getSize(); + } + } +} + +// If linking position-dependent code then the table will store the addresses +// directly in the binary so the section has type SHT_PROGBITS. If linking +// position-independent code the section has type SHT_NOBITS since it will be +// allocated and filled in by the dynamic linker. +PPC64LongBranchTargetSection::PPC64LongBranchTargetSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, + config->isPic ? SHT_NOBITS : SHT_PROGBITS, 8, + ".branch_lt") {} + +void PPC64LongBranchTargetSection::addEntry(Symbol &sym) { + assert(sym.ppc64BranchltIndex == 0xffff); + sym.ppc64BranchltIndex = entries.size(); + entries.push_back(&sym); +} + +size_t PPC64LongBranchTargetSection::getSize() const { + return entries.size() * 8; +} + +void PPC64LongBranchTargetSection::writeTo(uint8_t *buf) { + // If linking non-pic we have the final addresses of the targets and they get + // written to the table directly. For pic the dynamic linker will allocate + // the section and fill it it. + if (config->isPic) + return; + + for (const Symbol *sym : entries) { + assert(sym->getVA()); + // Need calls to branch to the local entry-point since a long-branch + // must be a local-call. + write64(buf, + sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(sym->stOther)); + buf += 8; + } +} + +bool PPC64LongBranchTargetSection::isNeeded() const { + // `removeUnusedSyntheticSections()` is called before thunk allocation which + // is too early to determine if this section will be empty or not. We need + // Finalized to keep the section alive until after thunk creation. Finalized + // only gets set to true once `finalizeSections()` is called after thunk + // creation. Becuase of this, if we don't create any long-branch thunks we end + // up with an empty .branch_lt section in the binary. + return !finalized || !entries.empty(); +} + +RISCVSdataSection::RISCVSdataSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 1, ".sdata") {} + +bool RISCVSdataSection::isNeeded() const { + if (!ElfSym::riscvGlobalPointer) + return false; + + // __global_pointer$ is defined relative to .sdata . If the section does not + // exist, create a dummy one. + for (BaseCommand *base : getParent()->sectionCommands) + if (auto *isd = dyn_cast(base)) + for (InputSection *isec : isd->sections) + if (isec != this) + return false; + return true; +} + +static uint8_t getAbiVersion() { + // MIPS non-PIC executable gets ABI version 1. + if (config->emachine == EM_MIPS) { + if (!config->isPic && !config->relocatable && + (config->eflags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC) + return 1; + return 0; + } + + if (config->emachine == EM_AMDGPU) { + uint8_t ver = objectFiles[0]->abiVersion; + for (InputFile *file : makeArrayRef(objectFiles).slice(1)) + if (file->abiVersion != ver) + error("incompatible ABI version: " + toString(file)); + return ver; + } + + return 0; +} + +template void elf::writeEhdr(uint8_t *buf, Partition &part) { + // For executable segments, the trap instructions are written before writing + // the header. Setting Elf header bytes to zero ensures that any unused bytes + // in header are zero-cleared, instead of having trap instructions. + memset(buf, 0, sizeof(typename ELFT::Ehdr)); + memcpy(buf, "\177ELF", 4); + + auto *eHdr = reinterpret_cast(buf); + eHdr->e_ident[EI_CLASS] = config->is64 ? ELFCLASS64 : ELFCLASS32; + eHdr->e_ident[EI_DATA] = config->isLE ? ELFDATA2LSB : ELFDATA2MSB; + eHdr->e_ident[EI_VERSION] = EV_CURRENT; + eHdr->e_ident[EI_OSABI] = config->osabi; + eHdr->e_ident[EI_ABIVERSION] = getAbiVersion(); + eHdr->e_machine = config->emachine; + eHdr->e_version = EV_CURRENT; + eHdr->e_flags = config->eflags; + eHdr->e_ehsize = sizeof(typename ELFT::Ehdr); + eHdr->e_phnum = part.phdrs.size(); + eHdr->e_shentsize = sizeof(typename ELFT::Shdr); + + if (!config->relocatable) { + eHdr->e_phoff = sizeof(typename ELFT::Ehdr); + eHdr->e_phentsize = sizeof(typename ELFT::Phdr); + } +} + +template void elf::writePhdrs(uint8_t *buf, Partition &part) { + // Write the program header table. + auto *hBuf = reinterpret_cast(buf); + for (PhdrEntry *p : part.phdrs) { + hBuf->p_type = p->p_type; + hBuf->p_flags = p->p_flags; + hBuf->p_offset = p->p_offset; + hBuf->p_vaddr = p->p_vaddr; + hBuf->p_paddr = p->p_paddr; + hBuf->p_filesz = p->p_filesz; + hBuf->p_memsz = p->p_memsz; + hBuf->p_align = p->p_align; + ++hBuf; + } +} + +template +PartitionElfHeaderSection::PartitionElfHeaderSection() + : SyntheticSection(SHF_ALLOC, SHT_LLVM_PART_EHDR, 1, "") {} + +template +size_t PartitionElfHeaderSection::getSize() const { + return sizeof(typename ELFT::Ehdr); +} + +template +void PartitionElfHeaderSection::writeTo(uint8_t *buf) { + writeEhdr(buf, getPartition()); + + // Loadable partitions are always ET_DYN. + auto *eHdr = reinterpret_cast(buf); + eHdr->e_type = ET_DYN; +} + +template +PartitionProgramHeadersSection::PartitionProgramHeadersSection() + : SyntheticSection(SHF_ALLOC, SHT_LLVM_PART_PHDR, 1, ".phdrs") {} + +template +size_t PartitionProgramHeadersSection::getSize() const { + return sizeof(typename ELFT::Phdr) * getPartition().phdrs.size(); +} + +template +void PartitionProgramHeadersSection::writeTo(uint8_t *buf) { + writePhdrs(buf, getPartition()); +} + +PartitionIndexSection::PartitionIndexSection() + : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".rodata") {} + +size_t PartitionIndexSection::getSize() const { + return 12 * (partitions.size() - 1); +} + +void PartitionIndexSection::finalizeContents() { + for (size_t i = 1; i != partitions.size(); ++i) + partitions[i].nameStrTab = mainPart->dynStrTab->addString(partitions[i].name); +} + +void PartitionIndexSection::writeTo(uint8_t *buf) { + uint64_t va = getVA(); + for (size_t i = 1; i != partitions.size(); ++i) { + write32(buf, mainPart->dynStrTab->getVA() + partitions[i].nameStrTab - va); + write32(buf + 4, partitions[i].elfHeader->getVA() - (va + 4)); + + SyntheticSection *next = + i == partitions.size() - 1 ? in.partEnd : partitions[i + 1].elfHeader; + write32(buf + 8, next->getVA() - partitions[i].elfHeader->getVA()); + + va += 12; + buf += 12; + } +} + +InStruct elf::in; + +std::vector elf::partitions; +Partition *elf::mainPart; + +template GdbIndexSection *GdbIndexSection::create(); +template GdbIndexSection *GdbIndexSection::create(); +template GdbIndexSection *GdbIndexSection::create(); +template GdbIndexSection *GdbIndexSection::create(); + +template void elf::splitSections(); +template void elf::splitSections(); +template void elf::splitSections(); +template void elf::splitSections(); + +template void EhFrameSection::addSection(InputSectionBase *); +template void EhFrameSection::addSection(InputSectionBase *); +template void EhFrameSection::addSection(InputSectionBase *); +template void EhFrameSection::addSection(InputSectionBase *); + +template void PltSection::addEntry(Symbol &Sym); +template void PltSection::addEntry(Symbol &Sym); +template void PltSection::addEntry(Symbol &Sym); +template void PltSection::addEntry(Symbol &Sym); + +template class elf::MipsAbiFlagsSection; +template class elf::MipsAbiFlagsSection; +template class elf::MipsAbiFlagsSection; +template class elf::MipsAbiFlagsSection; + +template class elf::MipsOptionsSection; +template class elf::MipsOptionsSection; +template class elf::MipsOptionsSection; +template class elf::MipsOptionsSection; + +template class elf::MipsReginfoSection; +template class elf::MipsReginfoSection; +template class elf::MipsReginfoSection; +template class elf::MipsReginfoSection; + +template class elf::DynamicSection; +template class elf::DynamicSection; +template class elf::DynamicSection; +template class elf::DynamicSection; + +template class elf::RelocationSection; +template class elf::RelocationSection; +template class elf::RelocationSection; +template class elf::RelocationSection; + +template class elf::AndroidPackedRelocationSection; +template class elf::AndroidPackedRelocationSection; +template class elf::AndroidPackedRelocationSection; +template class elf::AndroidPackedRelocationSection; + +template class elf::RelrSection; +template class elf::RelrSection; +template class elf::RelrSection; +template class elf::RelrSection; + +template class elf::SymbolTableSection; +template class elf::SymbolTableSection; +template class elf::SymbolTableSection; +template class elf::SymbolTableSection; + +template class elf::VersionNeedSection; +template class elf::VersionNeedSection; +template class elf::VersionNeedSection; +template class elf::VersionNeedSection; + +template void elf::writeEhdr(uint8_t *Buf, Partition &Part); +template void elf::writeEhdr(uint8_t *Buf, Partition &Part); +template void elf::writeEhdr(uint8_t *Buf, Partition &Part); +template void elf::writeEhdr(uint8_t *Buf, Partition &Part); + +template void elf::writePhdrs(uint8_t *Buf, Partition &Part); +template void elf::writePhdrs(uint8_t *Buf, Partition &Part); +template void elf::writePhdrs(uint8_t *Buf, Partition &Part); +template void elf::writePhdrs(uint8_t *Buf, Partition &Part); + +template class elf::PartitionElfHeaderSection; +template class elf::PartitionElfHeaderSection; +template class elf::PartitionElfHeaderSection; +template class elf::PartitionElfHeaderSection; + +template class elf::PartitionProgramHeadersSection; +template class elf::PartitionProgramHeadersSection; +template class elf::PartitionProgramHeadersSection; +template class elf::PartitionProgramHeadersSection; Property changes on: vendor/lld/lld-release_900-r372316/ELF/SyntheticSections.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.cpp (revision 352529) @@ -0,0 +1,651 @@ +//===- AArch64ErrataFix.cpp -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// This file implements Section Patching for the purpose of working around +// errata in CPUs. The general principle is that an erratum sequence of one or +// more instructions is detected in the instruction stream, one of the +// instructions in the sequence is replaced with a branch to a patch sequence +// of replacement instructions. At the end of the replacement sequence the +// patch branches back to the instruction stream. + +// This technique is only suitable for fixing an erratum when: +// - There is a set of necessary conditions required to trigger the erratum that +// can be detected at static link time. +// - There is a set of replacement instructions that can be used to remove at +// least one of the necessary conditions that trigger the erratum. +// - We can overwrite an instruction in the erratum sequence with a branch to +// the replacement sequence. +// - We can place the replacement sequence within range of the branch. + +// FIXME: +// - The implementation here only supports one patch, the AArch64 Cortex-53 +// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core. +// To keep the initial version simple there is no support for multiple +// architectures or selection of different patches. +//===----------------------------------------------------------------------===// + +#include "AArch64ErrataFix.h" +#include "Config.h" +#include "LinkerScript.h" +#include "OutputSections.h" +#include "Relocations.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::support; +using namespace llvm::support::endian; + +using namespace lld; +using namespace lld::elf; + +// Helper functions to identify instructions and conditions needed to trigger +// the Cortex-A53-843419 erratum. + +// ADRP +// | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) | +static bool isADRP(uint32_t instr) { + return (instr & 0x9f000000) == 0x90000000; +} + +// Load and store bit patterns from ARMv8-A ARM ARM. +// Instructions appear in order of appearance starting from table in +// C4.1.3 Loads and Stores. + +// All loads and stores have 1 (at bit postion 27), (0 at bit position 25). +// | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) | +static bool isLoadStoreClass(uint32_t instr) { + return (instr & 0x0a000000) == 0x08000000; +} + +// LDN/STN multiple no offset +// | 0 Q 00 | 1100 | 0 L 00 | 0000 | opcode (4) | size (2) | Rn (5) | Rt (5) | +// LDN/STN multiple post-indexed +// | 0 Q 00 | 1100 | 1 L 0 | Rm (5)| opcode (4) | size (2) | Rn (5) | Rt (5) | +// L == 0 for stores. + +// Utility routine to decode opcode field of LDN/STN multiple structure +// instructions to find the ST1 instructions. +// opcode == 0010 ST1 4 registers. +// opcode == 0110 ST1 3 registers. +// opcode == 0111 ST1 1 register. +// opcode == 1010 ST1 2 registers. +static bool isST1MultipleOpcode(uint32_t instr) { + return (instr & 0x0000f000) == 0x00002000 || + (instr & 0x0000f000) == 0x00006000 || + (instr & 0x0000f000) == 0x00007000 || + (instr & 0x0000f000) == 0x0000a000; +} + +static bool isST1Multiple(uint32_t instr) { + return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr); +} + +// Writes to Rn (writeback). +static bool isST1MultiplePost(uint32_t instr) { + return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr); +} + +// LDN/STN single no offset +// | 0 Q 00 | 1101 | 0 L R 0 | 0000 | opc (3) S | size (2) | Rn (5) | Rt (5)| +// LDN/STN single post-indexed +// | 0 Q 00 | 1101 | 1 L R | Rm (5) | opc (3) S | size (2) | Rn (5) | Rt (5)| +// L == 0 for stores + +// Utility routine to decode opcode field of LDN/STN single structure +// instructions to find the ST1 instructions. +// R == 0 for ST1 and ST3, R == 1 for ST2 and ST4. +// opcode == 000 ST1 8-bit. +// opcode == 010 ST1 16-bit. +// opcode == 100 ST1 32 or 64-bit (Size determines which). +static bool isST1SingleOpcode(uint32_t instr) { + return (instr & 0x0040e000) == 0x00000000 || + (instr & 0x0040e000) == 0x00004000 || + (instr & 0x0040e000) == 0x00008000; +} + +static bool isST1Single(uint32_t instr) { + return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr); +} + +// Writes to Rn (writeback). +static bool isST1SinglePost(uint32_t instr) { + return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr); +} + +static bool isST1(uint32_t instr) { + return isST1Multiple(instr) || isST1MultiplePost(instr) || + isST1Single(instr) || isST1SinglePost(instr); +} + +// Load/store exclusive +// | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) | +// L == 0 for Stores. +static bool isLoadStoreExclusive(uint32_t instr) { + return (instr & 0x3f000000) == 0x08000000; +} + +static bool isLoadExclusive(uint32_t instr) { + return (instr & 0x3f400000) == 0x08400000; +} + +// Load register literal +// | opc (2) 01 | 1 V 00 | imm19 | Rt (5) | +static bool isLoadLiteral(uint32_t instr) { + return (instr & 0x3b000000) == 0x18000000; +} + +// Load/store no-allocate pair +// (offset) +// | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | +// L == 0 for stores. +// Never writes to register +static bool isSTNP(uint32_t instr) { + return (instr & 0x3bc00000) == 0x28000000; +} + +// Load/store register pair +// (post-indexed) +// | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | +// L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP +// Writes to Rn. +static bool isSTPPost(uint32_t instr) { + return (instr & 0x3bc00000) == 0x28800000; +} + +// (offset) +// | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | +static bool isSTPOffset(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29000000; +} + +// (pre-index) +// | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | +// Writes to Rn. +static bool isSTPPre(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29800000; +} + +static bool isSTP(uint32_t instr) { + return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr); +} + +// Load/store register (unscaled immediate) +// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) | +// V == 0 for Scalar, V == 1 for Simd/FP. +static bool isLoadStoreUnscaled(uint32_t instr) { + return (instr & 0x3b000c00) == 0x38000000; +} + +// Load/store register (immediate post-indexed) +// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) | +static bool isLoadStoreImmediatePost(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000400; +} + +// Load/store register (unprivileged) +// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) | +static bool isLoadStoreUnpriv(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000800; +} + +// Load/store register (immediate pre-indexed) +// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) | +static bool isLoadStoreImmediatePre(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000c00; +} + +// Load/store register (register offset) +// | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt | +static bool isLoadStoreRegisterOff(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38200800; +} + +// Load/store register (unsigned immediate) +// | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) | +static bool isLoadStoreRegisterUnsigned(uint32_t instr) { + return (instr & 0x3b000000) == 0x39000000; +} + +// Rt is always in bit position 0 - 4. +static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); } + +// Rn is always in bit position 5 - 9. +static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; } + +// C4.1.2 Branches, Exception Generating and System instructions +// | op0 (3) 1 | 01 op1 (4) | x (22) | +// op0 == 010 101 op1 == 0xxx Conditional Branch. +// op0 == 110 101 op1 == 1xxx Unconditional Branch Register. +// op0 == x00 101 op1 == xxxx Unconditional Branch immediate. +// op0 == x01 101 op1 == 0xxx Compare and branch immediate. +// op0 == x01 101 op1 == 1xxx Test and branch immediate. +static bool isBranch(uint32_t instr) { + return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch. + ((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg. + ((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm. + ((instr & 0x7c000000) == 0x34000000); // Compare and test branch. +} + +static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) { + return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) || + isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) || + isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr); +} + +// Note that this function refers to v8.0 only and does not include the +// additional load and store instructions added for in later revisions of +// the architecture such as the Atomic memory operations introduced +// in v8.1. +static bool isV8NonStructureLoad(uint32_t instr) { + if (isLoadExclusive(instr)) + return true; + if (isLoadLiteral(instr)) + return true; + else if (isV8SingleRegisterNonStructureLoadStore(instr)) { + // For Load and Store single register, Loads are derived from a + // combination of the Size, V and Opc fields. + uint32_t size = (instr >> 30) & 0xff; + uint32_t v = (instr >> 26) & 0x1; + uint32_t opc = (instr >> 22) & 0x3; + // For the load and store instructions that we are decoding. + // Opc == 0 are all stores. + // Opc == 1 with a couple of exceptions are loads. The exceptions are: + // Size == 00 (0), V == 1, Opc == 10 (2) which is a store and + // Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch. + return opc != 0 && !(size == 0 && v == 1 && opc == 2) && + !(size == 3 && v == 0 && opc == 2); + } + return false; +} + +// The following decode instructions are only complete up to the instructions +// needed for errata 843419. + +// Instruction with writeback updates the index register after the load/store. +static bool hasWriteback(uint32_t instr) { + return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) || + isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) || + isST1MultiplePost(instr); +} + +// For the load and store class of instructions, a load can write to the +// destination register, a load and a store can write to the base register when +// the instruction has writeback. +static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) { + return (isV8NonStructureLoad(instr) && getRt(instr) == reg) || + (hasWriteback(instr) && getRn(instr) == reg); +} + +// Scanner for Cortex-A53 errata 843419 +// Full details are available in the Cortex A53 MPCore revision 0 Software +// Developers Errata Notice (ARM-EPM-048406). +// +// The instruction sequence that triggers the erratum is common in compiled +// AArch64 code, however it is sensitive to the offset of the sequence within +// a 4k page. This means that by scanning and fixing the patch after we have +// assigned addresses we only need to disassemble and fix instances of the +// sequence in the range of affected offsets. +// +// In summary the erratum conditions are a series of 4 instructions: +// 1.) An ADRP instruction that writes to register Rn with low 12 bits of +// address of instruction either 0xff8 or 0xffc. +// 2.) A load or store instruction that can be: +// - A single register load or store, of either integer or vector registers. +// - An STP or STNP, of either integer or vector registers. +// - An Advanced SIMD ST1 store instruction. +// - Must not write to Rn, but may optionally read from it. +// 3.) An optional instruction that is not a branch and does not write to Rn. +// 4.) A load or store from the Load/store register (unsigned immediate) class +// that uses Rn as the base address register. +// +// Note that we do not attempt to scan for Sequence 2 as described in the +// Software Developers Errata Notice as this has been assessed to be extremely +// unlikely to occur in compiled code. This matches gold and ld.bfd behavior. + +// Return true if the Instruction sequence Adrp, Instr2, and Instr4 match +// the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.), +// and 4.) in the Scanner for Cortex-A53 errata comment above. +static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2, + uint32_t instr4) { + if (!isADRP(instr1)) + return false; + + uint32_t rn = getRt(instr1); + return isLoadStoreClass(instr2) && + (isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) || + isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) || + isSTNP(instr2) || isST1(instr2)) && + !doesLoadStoreWriteToReg(instr2, rn) && + isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn; +} + +// Scan the instruction sequence starting at Offset Off from the base of +// InputSection IS. We update Off in this function rather than in the caller as +// we can skip ahead much further into the section when we know how many +// instructions we've scanned. +// Return the offset of the load or store instruction in IS that we want to +// patch or 0 if no patch required. +static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, + uint64_t limit) { + uint64_t isecAddr = isec->getVA(0); + + // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. + uint64_t initialPageOff = (isecAddr + off) & 0xfff; + if (initialPageOff < 0xff8) + off += 0xff8 - initialPageOff; + + bool optionalAllowed = limit - off > 12; + if (off >= limit || limit - off < 12) { + // Need at least 3 4-byte sized instructions to trigger erratum. + off = limit; + return 0; + } + + uint64_t patchOff = 0; + const uint8_t *buf = isec->data().begin(); + const ulittle32_t *instBuf = reinterpret_cast(buf + off); + uint32_t instr1 = *instBuf++; + uint32_t instr2 = *instBuf++; + uint32_t instr3 = *instBuf++; + if (is843419ErratumSequence(instr1, instr2, instr3)) { + patchOff = off + 8; + } else if (optionalAllowed && !isBranch(instr3)) { + uint32_t instr4 = *instBuf++; + if (is843419ErratumSequence(instr1, instr2, instr4)) + patchOff = off + 12; + } + if (((isecAddr + off) & 0xfff) == 0xff8) + off += 4; + else + off += 0xffc; + return patchOff; +} + +class lld::elf::Patch843419Section : public SyntheticSection { +public: + Patch843419Section(InputSection *p, uint64_t off); + + void writeTo(uint8_t *buf) override; + + size_t getSize() const override { return 8; } + + uint64_t getLDSTAddr() const; + + // The Section we are patching. + const InputSection *patchee; + // The offset of the instruction in the Patchee section we are patching. + uint64_t patcheeOffset; + // A label for the start of the Patch that we can use as a relocation target. + Symbol *patchSym; +}; + +lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4, + ".text.patch"), + patchee(p), patcheeOffset(off) { + this->parent = p->getParent(); + patchSym = addSyntheticLocal( + saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, + getSize(), *this); + addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); +} + +uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { + return patchee->getVA(patcheeOffset); +} + +void lld::elf::Patch843419Section::writeTo(uint8_t *buf) { + // Copy the instruction that we will be replacing with a branch in the + // Patchee Section. + write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); + + // Apply any relocation transferred from the original PatcheeSection. + // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc + // also adds outSecOff so we need to subtract to avoid double counting. + this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); + + // Return address is the next instruction after the one we have just copied. + uint64_t s = getLDSTAddr() + 4; + uint64_t p = patchSym->getVA() + 4; + target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p); +} + +void AArch64Err843419Patcher::init() { + // The AArch64 ABI permits data in executable sections. We must avoid scanning + // this data as if it were instructions to avoid false matches. We use the + // mapping symbols in the InputObjects to identify this data, caching the + // results in sectionMap so we don't have to recalculate it each pass. + + // The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe + // half open intervals [Symbol Value, Next Symbol Value) of code and data + // within sections. If there is no next symbol then the half open interval is + // [Symbol Value, End of section). The type, code or data, is determined by + // the mapping symbol name, $x for code, $d for data. + auto isCodeMapSymbol = [](const Symbol *b) { + return b->getName() == "$x" || b->getName().startswith("$x."); + }; + auto isDataMapSymbol = [](const Symbol *b) { + return b->getName() == "$d" || b->getName().startswith("$d."); + }; + + // Collect mapping symbols for every executable InputSection. + for (InputFile *file : objectFiles) { + auto *f = cast>(file); + for (Symbol *b : f->getLocalSymbols()) { + auto *def = dyn_cast(b); + if (!def) + continue; + if (!isCodeMapSymbol(def) && !isDataMapSymbol(def)) + continue; + if (auto *sec = dyn_cast_or_null(def->section)) + if (sec->flags & SHF_EXECINSTR) + sectionMap[sec].push_back(def); + } + } + // For each InputSection make sure the mapping symbols are in sorted in + // ascending order and free from consecutive runs of mapping symbols with + // the same type. For example we must remove the redundant $d.1 from $x.0 + // $d.0 $d.1 $x.1. + for (auto &kv : sectionMap) { + std::vector &mapSyms = kv.second; + if (mapSyms.size() <= 1) + continue; + llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) { + return a->value < b->value; + }); + mapSyms.erase( + std::unique(mapSyms.begin(), mapSyms.end(), + [=](const Defined *a, const Defined *b) { + return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) || + (isDataMapSymbol(a) && isDataMapSymbol(b)); + }), + mapSyms.end()); + } + initialized = true; +} + +// Insert the PatchSections we have created back into the +// InputSectionDescription. As inserting patches alters the addresses of +// InputSections that follow them, we try and place the patches after all the +// executable sections, although we may need to insert them earlier if the +// InputSectionDescription is larger than the maximum branch range. +void AArch64Err843419Patcher::insertPatches( + InputSectionDescription &isd, std::vector &patches) { + uint64_t isecLimit; + uint64_t prevIsecLimit = isd.sections.front()->outSecOff; + uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); + uint64_t outSecAddr = isd.sections.front()->getParent()->addr; + + // Set the outSecOff of patches to the place where we want to insert them. + // We use a similar strategy to Thunk placement. Place patches roughly + // every multiple of maximum branch range. + auto patchIt = patches.begin(); + auto patchEnd = patches.end(); + for (const InputSection *isec : isd.sections) { + isecLimit = isec->outSecOff + isec->getSize(); + if (isecLimit > patchUpperBound) { + while (patchIt != patchEnd) { + if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit) + break; + (*patchIt)->outSecOff = prevIsecLimit; + ++patchIt; + } + patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); + } + prevIsecLimit = isecLimit; + } + for (; patchIt != patchEnd; ++patchIt) { + (*patchIt)->outSecOff = isecLimit; + } + + // merge all patch sections. We use the outSecOff assigned above to + // determine the insertion point. This is ok as we only merge into an + // InputSectionDescription once per pass, and at the end of the pass + // assignAddresses() will recalculate all the outSecOff values. + std::vector tmp; + tmp.reserve(isd.sections.size() + patches.size()); + auto mergeCmp = [](const InputSection *a, const InputSection *b) { + if (a->outSecOff < b->outSecOff) + return true; + if (a->outSecOff == b->outSecOff && isa(a) && + !isa(b)) + return true; + return false; + }; + std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(), + patches.end(), std::back_inserter(tmp), mergeCmp); + isd.sections = std::move(tmp); +} + +// Given an erratum sequence that starts at address adrpAddr, with an +// instruction that we need to patch at patcheeOffset from the start of +// InputSection IS, create a Patch843419 Section and add it to the +// Patches that we need to insert. +static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, + InputSection *isec, + std::vector &patches) { + // There may be a relocation at the same offset that we are patching. There + // are four cases that we need to consider. + // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this + // instance of the erratum on a previous patch and altered the relocation. We + // have nothing more to do. + // Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that + // we read will be transformed into a MOVZ later so we actually don't match + // the sequence and have nothing more to do. + // Case 3: A load/store register (unsigned immediate) class relocation. There + // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and + // they are both absolute. We need to add the same relocation to the patch, + // and replace the relocation with a R_AARCH_JUMP26 branch relocation. + // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch + // relocation at the offset. + auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) { + return r.offset == patcheeOffset; + }); + if (relIt != isec->relocations.end() && + (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE)) + return; + + log("detected cortex-a53-843419 erratum sequence starting at " + + utohexstr(adrpAddr) + " in unpatched output."); + + auto *ps = make(isec, patcheeOffset); + patches.push_back(ps); + + auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) { + return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym}; + }; + + if (relIt != isec->relocations.end()) { + ps->relocations.push_back( + {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym}); + *relIt = makeRelToPatch(patcheeOffset, ps->patchSym); + } else + isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym)); +} + +// Scan all the instructions in InputSectionDescription, for each instance of +// the erratum sequence create a Patch843419Section. We return the list of +// Patch843419Sections that need to be applied to ISD. +std::vector +AArch64Err843419Patcher::patchInputSectionDescription( + InputSectionDescription &isd) { + std::vector patches; + for (InputSection *isec : isd.sections) { + // LLD doesn't use the erratum sequence in SyntheticSections. + if (isa(isec)) + continue; + // Use sectionMap to make sure we only scan code and not inline data. + // We have already sorted MapSyms in ascending order and removed consecutive + // mapping symbols of the same type. Our range of executable instructions to + // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value, + // section size). + std::vector &mapSyms = sectionMap[isec]; + + auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) { + return ms->getName().startswith("$x"); + }); + + while (codeSym != mapSyms.end()) { + auto dataSym = std::next(codeSym); + uint64_t off = (*codeSym)->value; + uint64_t limit = + (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value; + + while (off < limit) { + uint64_t startAddr = isec->getVA(off); + if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit)) + implementPatch(startAddr, patcheeOffset, isec, patches); + } + if (dataSym == mapSyms.end()) + break; + codeSym = std::next(dataSym); + } + } + return patches; +} + +// For each InputSectionDescription make one pass over the executable sections +// looking for the erratum sequence; creating a synthetic Patch843419Section +// for each instance found. We insert these synthetic patch sections after the +// executable code in each InputSectionDescription. +// +// PreConditions: +// The Output and Input Sections have had their final addresses assigned. +// +// PostConditions: +// Returns true if at least one patch was added. The addresses of the +// Ouptut and Input Sections may have been changed. +// Returns false if no patches were required and no changes were made. +bool AArch64Err843419Patcher::createFixes() { + if (initialized == false) + init(); + + bool addressesChanged = false; + for (OutputSection *os : outputSections) { + if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR)) + continue; + for (BaseCommand *bc : os->sectionCommands) + if (auto *isd = dyn_cast(bc)) { + std::vector patches = + patchInputSectionDescription(*isd); + if (!patches.empty()) { + insertPatches(*isd, patches); + addressesChanged = true; + } + } + } + return addressesChanged; +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.h =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.h (revision 352529) @@ -0,0 +1,50 @@ +//===- AArch64ErrataFix.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_AARCH64ERRATAFIX_H +#define LLD_ELF_AARCH64ERRATAFIX_H + +#include "lld/Common/LLVM.h" +#include +#include + +namespace lld { +namespace elf { + +class Defined; +class InputSection; +struct InputSectionDescription; +class OutputSection; +class Patch843419Section; + +class AArch64Err843419Patcher { +public: + // return true if Patches have been added to the OutputSections. + bool createFixes(); + +private: + std::vector + patchInputSectionDescription(InputSectionDescription &isd); + + void insertPatches(InputSectionDescription &isd, + std::vector &patches); + + void init(); + + // A cache of the mapping symbols defined by the InputSection sorted in order + // of ascending value with redundant symbols removed. These describe + // the ranges of code and data in an executable InputSection. + std::map> sectionMap; + + bool initialized = false; +}; + +} // namespace elf +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/ELF/AArch64ErrataFix.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/CMakeLists.txt (revision 352529) @@ -0,0 +1,67 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(ELFOptionsTableGen) + +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lld_library(lldELF + AArch64ErrataFix.cpp + Arch/AArch64.cpp + Arch/AMDGPU.cpp + Arch/ARM.cpp + Arch/AVR.cpp + Arch/Hexagon.cpp + Arch/Mips.cpp + Arch/MipsArchTree.cpp + Arch/MSP430.cpp + Arch/PPC.cpp + Arch/PPC64.cpp + Arch/RISCV.cpp + Arch/SPARCV9.cpp + Arch/X86.cpp + Arch/X86_64.cpp + CallGraphSort.cpp + DWARF.cpp + Driver.cpp + DriverUtils.cpp + EhFrame.cpp + ICF.cpp + InputFiles.cpp + InputSection.cpp + LTO.cpp + LinkerScript.cpp + MapFile.cpp + MarkLive.cpp + OutputSections.cpp + Relocations.cpp + ScriptLexer.cpp + ScriptParser.cpp + SymbolTable.cpp + Symbols.cpp + SyntheticSections.cpp + Target.cpp + Thunks.cpp + Writer.cpp + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + BinaryFormat + BitWriter + Core + DebugInfoDWARF + LTO + MC + Object + Option + Support + + LINK_LIBS + lldCommon + ${LLVM_PTHREAD_LIB} + + DEPENDS + ELFOptionsTableGen + ${tablegen_deps} + ) Property changes on: vendor/lld/lld-release_900-r372316/ELF/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.cpp (revision 352529) @@ -0,0 +1,259 @@ +//===- CallGraphSort.cpp --------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// Implementation of Call-Chain Clustering from: Optimizing Function Placement +/// for Large-Scale Data-Center Applications +/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf +/// +/// The goal of this algorithm is to improve runtime performance of the final +/// executable by arranging code sections such that page table and i-cache +/// misses are minimized. +/// +/// Definitions: +/// * Cluster +/// * An ordered list of input sections which are layed out as a unit. At the +/// beginning of the algorithm each input section has its own cluster and +/// the weight of the cluster is the sum of the weight of all incomming +/// edges. +/// * Call-Chain Clustering (C³) Heuristic +/// * Defines when and how clusters are combined. Pick the highest weighted +/// input section then add it to its most likely predecessor if it wouldn't +/// penalize it too much. +/// * Density +/// * The weight of the cluster divided by the size of the cluster. This is a +/// proxy for the ammount of execution time spent per byte of the cluster. +/// +/// It does so given a call graph profile by the following: +/// * Build a weighted call graph from the call graph profile +/// * Sort input sections by weight +/// * For each input section starting with the highest weight +/// * Find its most likely predecessor cluster +/// * Check if the combined cluster would be too large, or would have too low +/// a density. +/// * If not, then combine the clusters. +/// * Sort non-empty clusters by density +/// +//===----------------------------------------------------------------------===// + +#include "CallGraphSort.h" +#include "OutputSections.h" +#include "SymbolTable.h" +#include "Symbols.h" + +using namespace llvm; +using namespace lld; +using namespace lld::elf; + +namespace { +struct Edge { + int from; + uint64_t weight; +}; + +struct Cluster { + Cluster(int sec, size_t s) : sections{sec}, size(s) {} + + double getDensity() const { + if (size == 0) + return 0; + return double(weight) / double(size); + } + + std::vector sections; + size_t size = 0; + uint64_t weight = 0; + uint64_t initialWeight = 0; + Edge bestPred = {-1, 0}; +}; + +class CallGraphSort { +public: + CallGraphSort(); + + DenseMap run(); + +private: + std::vector clusters; + std::vector sections; + + void groupClusters(); +}; + +// Maximum ammount the combined cluster density can be worse than the original +// cluster to consider merging. +constexpr int MAX_DENSITY_DEGRADATION = 8; + +// Maximum cluster size in bytes. +constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; +} // end anonymous namespace + +using SectionPair = + std::pair; + +// Take the edge list in Config->CallGraphProfile, resolve symbol names to +// Symbols, and generate a graph between InputSections with the provided +// weights. +CallGraphSort::CallGraphSort() { + MapVector &profile = config->callGraphProfile; + DenseMap secToCluster; + + auto getOrCreateNode = [&](const InputSectionBase *isec) -> int { + auto res = secToCluster.insert(std::make_pair(isec, clusters.size())); + if (res.second) { + sections.push_back(isec); + clusters.emplace_back(clusters.size(), isec->getSize()); + } + return res.first->second; + }; + + // Create the graph. + for (std::pair &c : profile) { + const auto *fromSB = cast(c.first.first->repl); + const auto *toSB = cast(c.first.second->repl); + uint64_t weight = c.second; + + // Ignore edges between input sections belonging to different output + // sections. This is done because otherwise we would end up with clusters + // containing input sections that can't actually be placed adjacently in the + // output. This messes with the cluster size and density calculations. We + // would also end up moving input sections in other output sections without + // moving them closer to what calls them. + if (fromSB->getOutputSection() != toSB->getOutputSection()) + continue; + + int from = getOrCreateNode(fromSB); + int to = getOrCreateNode(toSB); + + clusters[to].weight += weight; + + if (from == to) + continue; + + // Remember the best edge. + Cluster &toC = clusters[to]; + if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) { + toC.bestPred.from = from; + toC.bestPred.weight = weight; + } + } + for (Cluster &c : clusters) + c.initialWeight = c.weight; +} + +// It's bad to merge clusters which would degrade the density too much. +static bool isNewDensityBad(Cluster &a, Cluster &b) { + double newDensity = double(a.weight + b.weight) / double(a.size + b.size); + return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION; +} + +static void mergeClusters(Cluster &into, Cluster &from) { + into.sections.insert(into.sections.end(), from.sections.begin(), + from.sections.end()); + into.size += from.size; + into.weight += from.weight; + from.sections.clear(); + from.size = 0; + from.weight = 0; +} + +// Group InputSections into clusters using the Call-Chain Clustering heuristic +// then sort the clusters by density. +void CallGraphSort::groupClusters() { + std::vector sortedSecs(clusters.size()); + std::vector secToCluster(clusters.size()); + + for (size_t i = 0; i < clusters.size(); ++i) { + sortedSecs[i] = i; + secToCluster[i] = &clusters[i]; + } + + llvm::stable_sort(sortedSecs, [&](int a, int b) { + return clusters[a].getDensity() > clusters[b].getDensity(); + }); + + for (int si : sortedSecs) { + // clusters[si] is the same as secToClusters[si] here because it has not + // been merged into another cluster yet. + Cluster &c = clusters[si]; + + // Don't consider merging if the edge is unlikely. + if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) + continue; + + Cluster *predC = secToCluster[c.bestPred.from]; + if (predC == &c) + continue; + + if (c.size + predC->size > MAX_CLUSTER_SIZE) + continue; + + if (isNewDensityBad(*predC, c)) + continue; + + // NOTE: Consider using a disjoint-set to track section -> cluster mapping + // if this is ever slow. + for (int si : c.sections) + secToCluster[si] = predC; + + mergeClusters(*predC, c); + } + + // Remove empty or dead nodes. Invalidates all cluster indices. + llvm::erase_if(clusters, [](const Cluster &c) { + return c.size == 0 || c.sections.empty(); + }); + + // Sort by density. + llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) { + return a.getDensity() > b.getDensity(); + }); +} + +DenseMap CallGraphSort::run() { + groupClusters(); + + // Generate order. + DenseMap orderMap; + ssize_t curOrder = 1; + + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + orderMap[sections[secIndex]] = curOrder++; + + if (!config->printSymbolOrder.empty()) { + std::error_code ec; + raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None); + if (ec) { + error("cannot open " + config->printSymbolOrder + ": " + ec.message()); + return orderMap; + } + + // Print the symbols ordered by C3, in the order of increasing curOrder + // Instead of sorting all the orderMap, just repeat the loops above. + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + // Search all the symbols in the file of the section + // and find out a Defined symbol with name that is within the section. + for (Symbol *sym: sections[secIndex]->file->getSymbols()) + if (!sym->isSection()) // Filter out section-type symbols here. + if (auto *d = dyn_cast(sym)) + if (sections[secIndex] == d->section) + os << sym->getName() << "\n"; + } + + return orderMap; +} + +// Sort sections by the profile data provided by -callgraph-profile-file +// +// This first builds a call graph based on the profile data then merges sections +// according to the C³ huristic. All clusters are then sorted by a density +// metric to further improve locality. +DenseMap elf::computeCallGraphProfileOrder() { + return CallGraphSort().run(); +} Property changes on: vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.h =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.h (revision 352529) @@ -0,0 +1,22 @@ +//===- CallGraphSort.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_CALL_GRAPH_SORT_H +#define LLD_ELF_CALL_GRAPH_SORT_H + +#include "llvm/ADT/DenseMap.h" + +namespace lld { +namespace elf { +class InputSectionBase; + +llvm::DenseMap computeCallGraphProfileOrder(); +} // namespace elf +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/ELF/CallGraphSort.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/Config.h =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Config.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Config.h (revision 352529) @@ -0,0 +1,321 @@ +//===- Config.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_CONFIG_H +#define LLD_ELF_CONFIG_H + +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Endian.h" +#include +#include + +namespace lld { +namespace elf { + +class InputFile; +class InputSectionBase; + +enum ELFKind { + ELFNoneKind, + ELF32LEKind, + ELF32BEKind, + ELF64LEKind, + ELF64BEKind +}; + +// For --build-id. +enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; + +// For --discard-{all,locals,none}. +enum class DiscardPolicy { Default, All, Locals, None }; + +// For --icf={none,safe,all}. +enum class ICFLevel { None, Safe, All }; + +// For --strip-{all,debug}. +enum class StripPolicy { None, All, Debug }; + +// For --unresolved-symbols. +enum class UnresolvedPolicy { ReportError, Warn, Ignore }; + +// For --orphan-handling. +enum class OrphanHandlingPolicy { Place, Warn, Error }; + +// For --sort-section and linkerscript sorting rules. +enum class SortSectionPolicy { Default, None, Alignment, Name, Priority }; + +// For --target2 +enum class Target2Policy { Abs, Rel, GotRel }; + +// For tracking ARM Float Argument PCS +enum class ARMVFPArgKind { Default, Base, VFP, ToolChain }; + +struct SymbolVersion { + llvm::StringRef name; + bool isExternCpp; + bool hasWildcard; +}; + +// This struct contains symbols version definition that +// can be found in version script if it is used for link. +struct VersionDefinition { + llvm::StringRef name; + uint16_t id = 0; + std::vector globals; +}; + +// This struct contains the global configuration for the linker. +// Most fields are direct mapping from the command line options +// and such fields have the same name as the corresponding options. +// Most fields are initialized by the driver. +struct Configuration { + uint8_t osabi = 0; + uint32_t andFeatures = 0; + llvm::CachePruningPolicy thinLTOCachePolicy; + llvm::StringMap sectionStartMap; + llvm::StringRef chroot; + llvm::StringRef dynamicLinker; + llvm::StringRef dwoDir; + llvm::StringRef entry; + llvm::StringRef emulation; + llvm::StringRef fini; + llvm::StringRef init; + llvm::StringRef ltoAAPipeline; + llvm::StringRef ltoCSProfileFile; + llvm::StringRef ltoNewPmPasses; + llvm::StringRef ltoObjPath; + llvm::StringRef ltoSampleProfile; + llvm::StringRef mapFile; + llvm::StringRef outputFile; + llvm::StringRef optRemarksFilename; + llvm::StringRef optRemarksPasses; + llvm::StringRef optRemarksFormat; + llvm::StringRef progName; + llvm::StringRef printSymbolOrder; + llvm::StringRef soName; + llvm::StringRef sysroot; + llvm::StringRef thinLTOCacheDir; + llvm::StringRef thinLTOIndexOnlyArg; + std::pair thinLTOObjectSuffixReplace; + std::pair thinLTOPrefixReplace; + std::string rpath; + std::vector versionDefinitions; + std::vector auxiliaryList; + std::vector filterList; + std::vector searchPaths; + std::vector symbolOrderingFile; + std::vector undefined; + std::vector dynamicList; + std::vector versionScriptGlobals; + std::vector versionScriptLocals; + std::vector buildIdVector; + llvm::MapVector, + uint64_t> + callGraphProfile; + bool allowMultipleDefinition; + bool allowShlibUndefined; + bool androidPackDynRelocs; + bool armHasBlx = false; + bool armHasMovtMovw = false; + bool armJ1J2BranchEncoding = false; + bool asNeeded = false; + bool bsymbolic; + bool bsymbolicFunctions; + bool callGraphProfileSort; + bool checkSections; + bool compressDebugSections; + bool cref; + bool defineCommon; + bool demangle = true; + bool dependentLibraries; + bool disableVerify; + bool ehFrameHdr; + bool emitLLVM; + bool emitRelocs; + bool enableNewDtags; + bool executeOnly; + bool exportDynamic; + bool fixCortexA53Errata843419; + bool forceBTI; + bool formatBinary = false; + bool requireCET; + bool gcSections; + bool gdbIndex; + bool gnuHash = false; + bool gnuUnique; + bool hasDynamicList = false; + bool hasDynSymTab; + bool ignoreDataAddressEquality; + bool ignoreFunctionAddressEquality; + bool ltoCSProfileGenerate; + bool ltoDebugPassManager; + bool ltoNewPassManager; + bool mergeArmExidx; + bool mipsN32Abi = false; + bool nmagic; + bool noinhibitExec; + bool nostdlib; + bool oFormatBinary; + bool omagic; + bool optRemarksWithHotness; + bool pacPlt; + bool picThunk; + bool pie; + bool printGcSections; + bool printIcfSections; + bool relocatable; + bool relrPackDynRelocs; + bool saveTemps; + bool singleRoRx; + bool shared; + bool isStatic = false; + bool sysvHash = false; + bool target1Rel; + bool trace; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; + bool tocOptimize; + bool undefinedVersion; + bool useAndroidRelrTags = false; + bool warnBackrefs; + bool warnCommon; + bool warnIfuncTextrel; + bool warnMissingEntry; + bool warnSymbolOrdering; + bool writeAddends; + bool zCombreloc; + bool zCopyreloc; + bool zExecstack; + bool zGlobal; + bool zHazardplt; + bool zIfuncNoplt; + bool zInitfirst; + bool zInterpose; + bool zKeepTextSectionPrefix; + bool zNodefaultlib; + bool zNodelete; + bool zNodlopen; + bool zNow; + bool zOrigin; + bool zRelro; + bool zRodynamic; + bool zText; + bool zRetpolineplt; + bool zWxneeded; + DiscardPolicy discard; + ICFLevel icf; + OrphanHandlingPolicy orphanHandling; + SortSectionPolicy sortSection; + StripPolicy strip; + UnresolvedPolicy unresolvedSymbols; + Target2Policy target2; + ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; + BuildIdKind buildId = BuildIdKind::None; + ELFKind ekind = ELFNoneKind; + uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; + uint16_t emachine = llvm::ELF::EM_NONE; + llvm::Optional imageBase; + uint64_t commonPageSize; + uint64_t maxPageSize; + uint64_t mipsGotSize; + uint64_t zStackSize; + unsigned ltoPartitions; + unsigned ltoo; + unsigned optimize; + unsigned thinLTOJobs; + int32_t splitStackAdjustSize; + + // The following config options do not directly correspond to any + // particualr command line options. + + // True if we need to pass through relocations in input files to the + // output file. Usually false because we consume relocations. + bool copyRelocs; + + // True if the target is ELF64. False if ELF32. + bool is64; + + // True if the target is little-endian. False if big-endian. + bool isLE; + + // endianness::little if isLE is true. endianness::big otherwise. + llvm::support::endianness endianness; + + // True if the target is the little-endian MIPS64. + // + // The reason why we have this variable only for the MIPS is because + // we use this often. Some ELF headers for MIPS64EL are in a + // mixed-endian (which is horrible and I'd say that's a serious spec + // bug), and we need to know whether we are reading MIPS ELF files or + // not in various places. + // + // (Note that MIPS64EL is not a typo for MIPS64LE. This is the official + // name whatever that means. A fun hypothesis is that "EL" is short for + // little-endian written in the little-endian order, but I don't know + // if that's true.) + bool isMips64EL; + + // True if we need to set the DF_STATIC_TLS flag to an output file, + // which works as a hint to the dynamic loader that the file contains + // code compiled with the static TLS model. The thread-local variable + // compiled with the static TLS model is faster but less flexible, and + // it may not be loaded using dlopen(). + // + // We set this flag to true when we see a relocation for the static TLS + // model. Once this becomes true, it will never become false. + // + // Since the flag is updated by multi-threaded code, we use std::atomic. + // (Writing to a variable is not considered thread-safe even if the + // variable is boolean and we always set the same value from all threads.) + std::atomic hasStaticTlsModel{false}; + + // Holds set of ELF header flags for the target. + uint32_t eflags = 0; + + // The ELF spec defines two types of relocation table entries, RELA and + // REL. RELA is a triplet of (offset, info, addend) while REL is a + // tuple of (offset, info). Addends for REL are implicit and read from + // the location where the relocations are applied. So, REL is more + // compact than RELA but requires a bit of more work to process. + // + // (From the linker writer's view, this distinction is not necessary. + // If the ELF had chosen whichever and sticked with it, it would have + // been easier to write code to process relocations, but it's too late + // to change the spec.) + // + // Each ABI defines its relocation type. IsRela is true if target + // uses RELA. As far as we know, all 64-bit ABIs are using RELA. A + // few 32-bit ABIs are using RELA too. + bool isRela; + + // True if we are creating position-independent code. + bool isPic; + + // 4 for ELF32, 8 for ELF64. + int wordsize; +}; + +// The only instance of Configuration struct. +extern Configuration *config; + +static inline void errorOrWarn(const Twine &msg) { + if (!config->noinhibitExec) + error(msg); + else + warn(msg); +} +} // namespace elf +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/ELF/Config.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/ELF/DWARF.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/DWARF.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/DWARF.cpp (revision 352529) @@ -0,0 +1,129 @@ +//===- DWARF.cpp ----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// The -gdb-index option instructs the linker to emit a .gdb_index section. +// The section contains information to make gdb startup faster. +// The format of the section is described at +// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html. +// +//===----------------------------------------------------------------------===// + +#include "DWARF.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/Memory.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/Object/ELFObjectFile.h" + +using namespace llvm; +using namespace llvm::object; +using namespace lld; +using namespace lld::elf; + +template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { + for (InputSectionBase *sec : obj->getSections()) { + if (!sec) + continue; + + if (LLDDWARFSection *m = + StringSwitch(sec->name) + .Case(".debug_addr", &addrSection) + .Case(".debug_gnu_pubnames", &gnuPubNamesSection) + .Case(".debug_gnu_pubtypes", &gnuPubTypesSection) + .Case(".debug_info", &infoSection) + .Case(".debug_ranges", &rangeSection) + .Case(".debug_rnglists", &rngListsSection) + .Case(".debug_line", &lineSection) + .Default(nullptr)) { + m->Data = toStringRef(sec->data()); + m->sec = sec; + continue; + } + + if (sec->name == ".debug_abbrev") + abbrevSection = toStringRef(sec->data()); + else if (sec->name == ".debug_str") + strSection = toStringRef(sec->data()); + else if (sec->name == ".debug_line_str") + lineStringSection = toStringRef(sec->data()); + } +} + +namespace { +template struct LLDRelocationResolver { + // In the ELF ABIs, S sepresents the value of the symbol in the relocation + // entry. For Rela, the addend is stored as part of the relocation entry. + static uint64_t resolve(object::RelocationRef ref, uint64_t s, + uint64_t /* A */) { + return s + ref.getRawDataRefImpl().p; + } +}; + +template struct LLDRelocationResolver> { + // For Rel, the addend A is supplied by the caller. + static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s, + uint64_t a) { + return s + a; + } +}; +} // namespace + +// Find if there is a relocation at Pos in Sec. The code is a bit +// more complicated than usual because we need to pass a section index +// to llvm since it has no idea about InputSection. +template +template +Optional +LLDDwarfObj::findAux(const InputSectionBase &sec, uint64_t pos, + ArrayRef rels) const { + auto it = + partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; }); + if (it == rels.end() || it->r_offset != pos) + return None; + const RelTy &rel = *it; + + const ObjFile *file = sec.getFile(); + uint32_t symIndex = rel.getSymbol(config->isMips64EL); + const typename ELFT::Sym &sym = file->template getELFSyms()[symIndex]; + uint32_t secIndex = file->getSectionIndex(sym); + + // An undefined symbol may be a symbol defined in a discarded section. We + // shall still resolve it. This is important for --gdb-index: the end address + // offset of an entry in .debug_ranges is relocated. If it is not resolved, + // its zero value will terminate the decoding of .debug_ranges prematurely. + Symbol &s = file->getRelocTargetSym(rel); + uint64_t val = 0; + if (auto *dr = dyn_cast(&s)) { + val = dr->value; + + // FIXME: We should be consistent about always adding the file + // offset or not. + if (dr->section->flags & ELF::SHF_ALLOC) + val += cast(dr->section)->getOffsetInFile(); + } + + DataRefImpl d; + d.p = getAddend(rel); + return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), + val, Optional(), + 0, LLDRelocationResolver::resolve}; +} + +template +Optional LLDDwarfObj::find(const llvm::DWARFSection &s, + uint64_t pos) const { + auto &sec = static_cast(s); + if (sec.sec->areRelocsRela) + return findAux(*sec.sec, pos, sec.sec->template relas()); + return findAux(*sec.sec, pos, sec.sec->template rels()); +} + +template class elf::LLDDwarfObj; +template class elf::LLDDwarfObj; +template class elf::LLDDwarfObj; +template class elf::LLDDwarfObj; Index: vendor/lld/lld-release_900-r372316/ELF/DWARF.h =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/DWARF.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/DWARF.h (revision 352529) @@ -0,0 +1,92 @@ +//===- DWARF.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_DWARF_H +#define LLD_ELF_DWARF_H + +#include "InputFiles.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ELF.h" + +namespace lld { +namespace elf { + +class InputSection; + +struct LLDDWARFSection final : public llvm::DWARFSection { + InputSectionBase *sec = nullptr; +}; + +template class LLDDwarfObj final : public llvm::DWARFObject { +public: + explicit LLDDwarfObj(ObjFile *obj); + + void forEachInfoSections( + llvm::function_ref f) const override { + f(infoSection); + } + + const llvm::DWARFSection &getRangeSection() const override { + return rangeSection; + } + + const llvm::DWARFSection &getRnglistsSection() const override { + return rngListsSection; + } + + const llvm::DWARFSection &getLineSection() const override { + return lineSection; + } + + const llvm::DWARFSection &getAddrSection() const override { + return addrSection; + } + + const llvm::DWARFSection &getGnuPubNamesSection() const override { + return gnuPubNamesSection; + } + + const llvm::DWARFSection &getGnuPubTypesSection() const override { + return gnuPubTypesSection; + } + + StringRef getFileName() const override { return ""; } + StringRef getAbbrevSection() const override { return abbrevSection; } + StringRef getStringSection() const override { return strSection; } + StringRef getLineStringSection() const override { return lineStringSection; } + + bool isLittleEndian() const override { + return ELFT::TargetEndianness == llvm::support::little; + } + + llvm::Optional find(const llvm::DWARFSection &sec, + uint64_t pos) const override; + +private: + template + llvm::Optional findAux(const InputSectionBase &sec, + uint64_t pos, + ArrayRef rels) const; + + LLDDWARFSection gnuPubNamesSection; + LLDDWARFSection gnuPubTypesSection; + LLDDWARFSection infoSection; + LLDDWARFSection rangeSection; + LLDDWARFSection rngListsSection; + LLDDWARFSection lineSection; + LLDDWARFSection addrSection; + StringRef abbrevSection; + StringRef strSection; + StringRef lineStringSection; +}; + +} // namespace elf +} // namespace lld + +#endif Index: vendor/lld/lld-release_900-r372316/ELF/Driver.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/ELF/Driver.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/ELF/Driver.cpp (revision 352529) @@ -0,0 +1,1911 @@ +//===- Driver.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// The driver drives the entire linking process. It is responsible for +// parsing command line options and doing whatever it is instructed to do. +// +// One notable thing in the LLD's driver when compared to other linkers is +// that the LLD's driver is agnostic on the host operating system. +// Other linkers usually have implicit default values (such as a dynamic +// linker path or library paths) for each host OS. +// +// I don't think implicit default values are useful because they are +// usually explicitly specified by the compiler driver. They can even +// be harmful when you are doing cross-linking. Therefore, in LLD, we +// simply trust the compiler driver to pass all required options and +// don't try to make effort on our side. +// +//===----------------------------------------------------------------------===// + +#include "Driver.h" +#include "Config.h" +#include "ICF.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "LinkerScript.h" +#include "MarkLive.h" +#include "OutputSections.h" +#include "ScriptParser.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "Writer.h" +#include "lld/Common/Args.h" +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "lld/Common/TargetOptionsCommandFlags.h" +#include "lld/Common/Threads.h" +#include "lld/Common/Version.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/GlobPattern.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TarWriter.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::sys; +using namespace llvm::support; + +using namespace lld; +using namespace lld::elf; + +Configuration *elf::config; +LinkerDriver *elf::driver; + +static void setConfigs(opt::InputArgList &args); +static void readConfigs(opt::InputArgList &args); + +bool elf::link(ArrayRef args, bool canExitEarly, + raw_ostream &error) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorLimitExceededMsg = + "too many errors emitted, stopping now (use " + "-error-limit=0 to see all errors)"; + errorHandler().errorOS = &error; + errorHandler().exitEarly = canExitEarly; + errorHandler().colorDiagnostics = error.has_colors(); + + inputSections.clear(); + outputSections.clear(); + binaryFiles.clear(); + bitcodeFiles.clear(); + objectFiles.clear(); + sharedFiles.clear(); + + config = make(); + driver = make(); + script = make(); + symtab = make(); + + tar = nullptr; + memset(&in, 0, sizeof(in)); + + partitions = {Partition()}; + + SharedFile::vernauxNum = 0; + + config->progName = args[0]; + + driver->main(args); + + // Exit immediately if we don't need to return to the caller. + // This saves time because the overhead of calling destructors + // for all globally-allocated objects is not negligible. + if (canExitEarly) + exitLld(errorCount() ? 1 : 0); + + freeArena(); + return !errorCount(); +} + +// Parses a linker -m option. +static std::tuple parseEmulation(StringRef emul) { + uint8_t osabi = 0; + StringRef s = emul; + if (s.endswith("_fbsd")) { + s = s.drop_back(5); + osabi = ELFOSABI_FREEBSD; + } + + std::pair ret = + StringSwitch>(s) + .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec", + {ELF64LEKind, EM_AARCH64}) + .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) + .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) + .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) + .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) + .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) + .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) + .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) + .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) + .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) + .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) + .Case("elf64lppc", {ELF64LEKind, EM_PPC64}) + .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) + .Case("elf_i386", {ELF32LEKind, EM_386}) + .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) + .Default({ELFNoneKind, EM_NONE}); + + if (ret.first == ELFNoneKind) + error("unknown emulation: " + emul); + return std::make_tuple(ret.first, ret.second, osabi); +} + +// Returns slices of MB by parsing MB as an archive file. +// Each slice consists of a member file in the archive. +std::vector> static getArchiveMembers( + MemoryBufferRef mb) { + std::unique_ptr file = + CHECK(Archive::create(mb), + mb.getBufferIdentifier() + ": failed to parse archive"); + + std::vector> v; + Error err = Error::success(); + bool addToTar = file->isThin() && tar; + for (const ErrorOr &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, mb.getBufferIdentifier() + + ": could not get the child of the archive"); + MemoryBufferRef mbref = + CHECK(c.getMemoryBufferRef(), + mb.getBufferIdentifier() + + ": could not get the buffer for a child of the archive"); + if (addToTar) + tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer()); + v.push_back(std::make_pair(mbref, c.getChildOffset())); + } + if (err) + fatal(mb.getBufferIdentifier() + ": Archive::children failed: " + + toString(std::move(err))); + + // Take ownership of memory buffers created for members of thin archives. + for (std::unique_ptr &mb : file->takeThinBuffers()) + make>(std::move(mb)); + + return v; +} + +// Opens a file and create a file object. Path has to be resolved already. +void LinkerDriver::addFile(StringRef path, bool withLOption) { + using namespace sys::fs; + + Optional buffer = readFile(path); + if (!buffer.hasValue()) + return; + MemoryBufferRef mbref = *buffer; + + if (config->formatBinary) { + files.push_back(make(mbref)); + return; + } + + switch (identify_magic(mbref.getBuffer())) { + case file_magic::unknown: + readLinkerScript(mbref); + return; + case file_magic::archive: { + // Handle -whole-archive. + if (inWholeArchive) { + for (const auto &p : getArchiveMembers(mbref)) + files.push_back(createObjectFile(p.first, path, p.second)); + return; + } + + std::unique_ptr file = + CHECK(Archive::create(mbref), path + ": failed to parse archive"); + + // If an archive file has no symbol table, it is likely that a user + // is attempting LTO and using a default ar command that doesn't + // understand the LLVM bitcode file. It is a pretty common error, so + // we'll handle it as if it had a symbol table. + if (!file->isEmpty() && !file->hasSymbolTable()) { + // Check if all members are bitcode files. If not, ignore, which is the + // default action without the LTO hack described above. + for (const std::pair &p : + getArchiveMembers(mbref)) + if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) { + error(path + ": archive has no index; run ranlib to add one"); + return; + } + + for (const std::pair &p : + getArchiveMembers(mbref)) + files.push_back(make(p.first, path, p.second)); + return; + } + + // Handle the regular case. + files.push_back(make(std::move(file))); + return; + } + case file_magic::elf_shared_object: + if (config->isStatic || config->relocatable) { + error("attempted static link of dynamic object " + path); + return; + } + + // DSOs usually have DT_SONAME tags in their ELF headers, and the + // sonames are used to identify DSOs. But if they are missing, + // they are identified by filenames. We don't know whether the new + // file has a DT_SONAME or not because we haven't parsed it yet. + // Here, we set the default soname for the file because we might + // need it later. + // + // If a file was specified by -lfoo, the directory part is not + // significant, as a user did not specify it. This behavior is + // compatible with GNU. + files.push_back( + make(mbref, withLOption ? path::filename(path) : path)); + return; + case file_magic::bitcode: + case file_magic::elf_relocatable: + if (inLib) + files.push_back(make(mbref, "", 0)); + else + files.push_back(createObjectFile(mbref)); + break; + default: + error(path + ": unknown file type"); + } +} + +// Add a given library by searching it from input search paths. +void LinkerDriver::addLibrary(StringRef name) { + if (Optional path = searchLibrary(name)) + addFile(*path, /*withLOption=*/true); + else + error("unable to find library -l" + name); +} + +// This function is called on startup. We need this for LTO since +// LTO calls LLVM functions to compile bitcode files to native code. +// Technically this can be delayed until we read bitcode files, but +// we don't bother to do lazily because the initialization is fast. +static void initLLVM() { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); +} + +// Some command line options or some combinations of them are not allowed. +// This function checks for such errors. +static void checkOptions() { + // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup + // table which is a relatively new feature. + if (config->emachine == EM_MIPS && config->gnuHash) + error("the .gnu.hash section is not compatible with the MIPS target"); + + if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) + error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); + + if (config->tocOptimize && config->emachine != EM_PPC64) + error("--toc-optimize is only supported on the PowerPC64 target"); + + if (config->pie && config->shared) + error("-shared and -pie may not be used together"); + + if (!config->shared && !config->filterList.empty()) + error("-F may not be used without -shared"); + + if (!config->shared && !config->auxiliaryList.empty()) + error("-f may not be used without -shared"); + + if (!config->relocatable && !config->defineCommon) + error("-no-define-common not supported in non relocatable output"); + + if (config->zText && config->zIfuncNoplt) + error("-z text and -z ifunc-noplt may not be used together"); + + if (config->relocatable) { + if (config->shared) + error("-r and -shared may not be used together"); + if (config->gcSections) + error("-r and --gc-sections may not be used together"); + if (config->gdbIndex) + error("-r and --gdb-index may not be used together"); + if (config->icf != ICFLevel::None) + error("-r and --icf may not be used together"); + if (config->pie) + error("-r and -pie may not be used together"); + } + + if (config->executeOnly) { + if (config->emachine != EM_AARCH64) + error("-execute-only is only supported on AArch64 targets"); + + if (config->singleRoRx && !script->hasSectionsCommand) + error("-execute-only and -no-rosegment cannot be used together"); + } + + if (config->zRetpolineplt && config->requireCET) + error("--require-cet may not be used with -z retpolineplt"); + + if (config->emachine != EM_AARCH64) { + if (config->pacPlt) + error("--pac-plt only supported on AArch64"); + if (config->forceBTI) + error("--force-bti only supported on AArch64"); + } +} + +static const char *getReproduceOption(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return arg->getValue(); + return getenv("LLD_REPRODUCE"); +} + +static bool hasZOption(opt::InputArgList &args, StringRef key) { + for (auto *arg : args.filtered(OPT_z)) + if (key == arg->getValue()) + return true; + return false; +} + +static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2, + bool Default) { + for (auto *arg : args.filtered_reverse(OPT_z)) { + if (k1 == arg->getValue()) + return true; + if (k2 == arg->getValue()) + return false; + } + return Default; +} + +static bool isKnownZFlag(StringRef s) { + return s == "combreloc" || s == "copyreloc" || s == "defs" || + s == "execstack" || s == "global" || s == "hazardplt" || + s == "ifunc-noplt" || s == "initfirst" || s == "interpose" || + s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || + s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || + s == "nodelete" || s == "nodlopen" || s == "noexecstack" || + s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" || + s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || + s == "rodynamic" || s == "text" || s == "wxneeded" || + s.startswith("common-page-size") || s.startswith("max-page-size=") || + s.startswith("stack-size="); +} + +// Report an error for an unknown -z option. +static void checkZOptions(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_z)) + if (!isKnownZFlag(arg->getValue())) + error("unknown -z value: " + StringRef(arg->getValue())); +} + +void LinkerDriver::main(ArrayRef argsArr) { + ELFOptTable parser; + opt::InputArgList args = parser.parse(argsArr.slice(1)); + + // Interpret this flag early because error() depends on them. + errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); + checkZOptions(args); + + // Handle -help + if (args.hasArg(OPT_help)) { + printHelp(); + return; + } + + // Handle -v or -version. + // + // A note about "compatible with GNU linkers" message: this is a hack for + // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and + // still the newest version in March 2017) or earlier to recognize LLD as + // a GNU compatible linker. As long as an output for the -v option + // contains "GNU" or "with BFD", they recognize us as GNU-compatible. + // + // This is somewhat ugly hack, but in reality, we had no choice other + // than doing this. Considering the very long release cycle of Libtool, + // it is not easy to improve it to recognize LLD as a GNU compatible + // linker in a timely manner. Even if we can make it, there are still a + // lot of "configure" scripts out there that are generated by old version + // of Libtool. We cannot convince every software developer to migrate to + // the latest version and re-generate scripts. So we have this hack. + if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) + message(getLLDVersion() + " (compatible with GNU linkers)"); + + if (const char *path = getReproduceOption(args)) { + // Note that --reproduce is a debug option so you can ignore it + // if you are trying to understand the whole picture of the code. + Expected> errOrWriter = + TarWriter::create(path, path::stem(path)); + if (errOrWriter) { + tar = std::move(*errOrWriter); + tar->append("response.txt", createResponseFile(args)); + tar->append("version.txt", getLLDVersion() + "\n"); + } else { + error("--reproduce: " + toString(errOrWriter.takeError())); + } + } + + readConfigs(args); + + // The behavior of -v or --version is a bit strange, but this is + // needed for compatibility with GNU linkers. + if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT)) + return; + if (args.hasArg(OPT_version)) + return; + + initLLVM(); + createFiles(args); + if (errorCount()) + return; + + inferMachineType(); + setConfigs(args); + checkOptions(); + if (errorCount()) + return; + + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + switch (config->ekind) { + case ELF32LEKind: + link(args); + return; + case ELF32BEKind: + link(args); + return; + case ELF64LEKind: + link(args); + return; + case ELF64BEKind: + link(args); + return; + default: + llvm_unreachable("unknown Config->EKind"); + } +} + +static std::string getRpath(opt::InputArgList &args) { + std::vector v = args::getStrings(args, OPT_rpath); + return llvm::join(v.begin(), v.end(), ":"); +} + +// Determines what we should do if there are remaining unresolved +// symbols after the name resolution. +static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { + UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, + OPT_warn_unresolved_symbols, true) + ? UnresolvedPolicy::ReportError + : UnresolvedPolicy::Warn; + + // Process the last of -unresolved-symbols, -no-undefined or -z defs. + for (auto *arg : llvm::reverse(args)) { + switch (arg->getOption().getID()) { + case OPT_unresolved_symbols: { + StringRef s = arg->getValue(); + if (s == "ignore-all" || s == "ignore-in-object-files") + return UnresolvedPolicy::Ignore; + if (s == "ignore-in-shared-libs" || s == "report-all") + return errorOrWarn; + error("unknown --unresolved-symbols value: " + s); + continue; + } + case OPT_no_undefined: + return errorOrWarn; + case OPT_z: + if (StringRef(arg->getValue()) == "defs") + return errorOrWarn; + continue; + } + } + + // -shared implies -unresolved-symbols=ignore-all because missing + // symbols are likely to be resolved at runtime using other DSOs. + if (config->shared) + return UnresolvedPolicy::Ignore; + return errorOrWarn; +} + +static Target2Policy getTarget2(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_target2, "got-rel"); + if (s == "rel") + return Target2Policy::Rel; + if (s == "abs") + return Target2Policy::Abs; + if (s == "got-rel") + return Target2Policy::GotRel; + error("unknown --target2 option: " + s); + return Target2Policy::GotRel; +} + +static bool isOutputFormatBinary(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_oformat, "elf"); + if (s == "binary") + return true; + if (!s.startswith("elf")) + error("unknown --oformat value: " + s); + return false; +} + +static DiscardPolicy getDiscard(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) + return DiscardPolicy::None; + + auto *arg = + args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); + if (!arg) + return DiscardPolicy::Default; + if (arg->getOption().getID() == OPT_discard_all) + return DiscardPolicy::All; + if (arg->getOption().getID() == OPT_discard_locals) + return DiscardPolicy::Locals; + return DiscardPolicy::None; +} + +static StringRef getDynamicLinker(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); + if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker) + return ""; + return arg->getValue(); +} + +static ICFLevel getICF(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!arg || arg->getOption().getID() == OPT_icf_none) + return ICFLevel::None; + if (arg->getOption().getID() == OPT_icf_safe) + return ICFLevel::Safe; + return ICFLevel::All; +} + +static StripPolicy getStrip(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) + return StripPolicy::None; + + auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug); + if (!arg) + return StripPolicy::None; + if (arg->getOption().getID() == OPT_strip_all) + return StripPolicy::All; + return StripPolicy::Debug; +} + +static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args, + const opt::Arg &arg) { + uint64_t va = 0; + if (s.startswith("0x")) + s = s.drop_front(2); + if (!to_integer(s, va, 16)) + error("invalid argument: " + arg.getAsString(args)); + return va; +} + +static StringMap getSectionStartMap(opt::InputArgList &args) { + StringMap ret; + for (auto *arg : args.filtered(OPT_section_start)) { + StringRef name; + StringRef addr; + std::tie(name, addr) = StringRef(arg->getValue()).split('='); + ret[name] = parseSectionAddress(addr, args, *arg); + } + + if (auto *arg = args.getLastArg(OPT_Ttext)) + ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tdata)) + ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tbss)) + ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg); + return ret; +} + +static SortSectionPolicy getSortSection(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_sort_section); + if (s == "alignment") + return SortSectionPolicy::Alignment; + if (s == "name") + return SortSectionPolicy::Name; + if (!s.empty()) + error("unknown --sort-section rule: " + s); + return SortSectionPolicy::Default; +} + +static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_orphan_handling, "place"); + if (s == "warn") + return OrphanHandlingPolicy::Warn; + if (s == "error") + return OrphanHandlingPolicy::Error; + if (s != "place") + error("unknown --orphan-handling mode: " + s); + return OrphanHandlingPolicy::Place; +} + +// Parse --build-id or --build-id= + +.. role:: none +.. role:: partial +.. role:: good + +=============== +Windows support +=============== + +LLD supports Windows operating system. When invoked as ``lld-link.exe`` or with +``-flavor link``, the driver for Windows operating system is used to parse +command line options, and it drives further linking processes. LLD accepts +almost all command line options that the linker shipped with Microsoft Visual +C++ (link.exe) supports. + +The current status is that LLD is used to link production builds of large +real-world binaries such as Firefox and Chromium. + +Development status +================== + +Driver + :good:`Mostly done`. Some exotic command line options that are not usually + used for application develompent, such as ``/DRIVER``, are not supported. + +Linking against DLL + :good:`Done`. LLD can read import libraries needed to link against DLL. Both + export-by-name and export-by-ordinal are supported. + +Linking against static library + :good:`Done`. The format of static library (.lib) on Windows is actually the + same as on Unix (.a). LLD can read it. + +Creating DLL + :good:`Done`. LLD creates a DLL if ``/DLL`` option is given. Exported + functions can be specified either via command line (``/EXPORT``) or via + module-definition file (.def). Both export-by-name and export-by-ordinal are + supported. + +Windows resource files support + :good:`Done`. If an ``.res`` file is given, LLD converts the file to a COFF + file using LLVM's Object library. + +Safe Structured Exception Handler (SEH) + :good:`Done` for both x86 and x64. + +Module-definition file + :partial:`Partially done`. LLD currently recognizes these directives: + ``EXPORTS``, ``HEAPSIZE``, ``STACKSIZE``, ``NAME``, and ``VERSION``. + +Debug info + :good:`Done`. LLD can emit PDBs that are at parity with those generated by + link.exe. However, LLD does not support /DEBUG:FASTLINK. + + +Downloading LLD +=============== + +The Windows version of LLD is included in the `pre-built binaries of LLVM's +releases `_ and in the `LLVM Snapshot +Builds `_. + +Building LLD +============ + +Using Visual Studio IDE/MSBuild +------------------------------- + +1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror), +#. run ``cmake -G "Visual Studio 12" `` from VS command prompt, +#. open LLVM.sln with Visual Studio, and +#. build ``lld`` target in ``lld executables`` folder + +Alternatively, you can use msbuild if you don't like to work in an IDE:: + + msbuild LLVM.sln /m /target:"lld executables\lld" + +MSBuild.exe had been shipped as a component of the .NET framework, but since +2013 it's part of Visual Studio. You can find it at "C:\\Program Files +(x86)\\msbuild". + +You can build LLD as a 64 bit application. To do that, open VS2013 x64 command +prompt and run cmake for "Visual Studio 12 Win64" target. + +Using Ninja +----------- + +1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror), +#. run ``cmake -G ninja `` from VS command prompt, +#. run ``ninja lld`` Index: vendor/lld/lld-release_900-r372316/docs/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/docs/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/CMakeLists.txt (revision 352529) @@ -0,0 +1,8 @@ +if (LLVM_ENABLE_SPHINX) + include(AddSphinxTarget) + if (SPHINX_FOUND) + if (${SPHINX_OUTPUT_HTML}) + add_sphinx_target(html lld) + endif() + endif() +endif() Property changes on: vendor/lld/lld-release_900-r372316/docs/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/_templates/indexsidebar.html =================================================================== --- vendor/lld/lld-release_900-r372316/docs/_templates/indexsidebar.html (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/_templates/indexsidebar.html (revision 352529) @@ -0,0 +1,4 @@ +

Bugs

+ +

lld bugs should be reported at the + LLVM Bugzilla.

Property changes on: vendor/lld/lld-release_900-r372316/docs/_templates/indexsidebar.html ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/html \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/_templates/layout.html =================================================================== --- vendor/lld/lld-release_900-r372316/docs/_templates/layout.html (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/_templates/layout.html (revision 352529) @@ -0,0 +1,12 @@ +{% extends "!layout.html" %} + +{% block extrahead %} + +{% endblock %} + +{% block rootrellink %} +
  • lld Home | 
  • +{% endblock %} Property changes on: vendor/lld/lld-release_900-r372316/docs/_templates/layout.html ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/html \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/llvm-theme/layout.html =================================================================== --- vendor/lld/lld-release_900-r372316/docs/llvm-theme/layout.html (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/llvm-theme/layout.html (revision 352529) @@ -0,0 +1,22 @@ +{# + sphinxdoc/layout.html + ~~~~~~~~~~~~~~~~~~~~~ + + Sphinx layout template for the sphinxdoc theme. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{% extends "basic/layout.html" %} + +{% block relbar1 %} + +{{ super() }} +{% endblock %} + +{# put the sidebar before the body #} +{% block sidebar1 %}{{ sidebar() }}{% endblock %} +{% block sidebar2 %}{% endblock %} Property changes on: vendor/lld/lld-release_900-r372316/docs/llvm-theme/layout.html ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/html \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/llvm.css =================================================================== --- vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/llvm.css (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/llvm.css (revision 352529) @@ -0,0 +1,345 @@ +/* + * sphinxdoc.css_t + * ~~~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- sphinxdoc theme. Originally created by + * Armin Ronacher for Werkzeug. + * + * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', + 'Verdana', sans-serif; + font-size: 14px; + letter-spacing: -0.01em; + line-height: 150%; + text-align: center; + background-color: #BFD1D4; + color: black; + padding: 0; + border: 1px solid #aaa; + + margin: 0px 80px 0px 80px; + min-width: 740px; +} + +div.logo { + background-color: white; + text-align: left; + padding: 10px 10px 15px 15px; +} + +div.document { + background-color: white; + text-align: left; + background-image: url(contents.png); + background-repeat: repeat-x; +} + +div.bodywrapper { + margin: 0 240px 0 0; + border-right: 1px solid #ccc; +} + +div.body { + margin: 0; + padding: 0.5em 20px 20px 20px; +} + +div.related { + font-size: 1em; +} + +div.related ul { + background-image: url(navigation.png); + height: 2em; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} + +div.related ul li { + margin: 0; + padding: 0; + height: 2em; + float: left; +} + +div.related ul li.right { + float: right; + margin-right: 5px; +} + +div.related ul li a { + margin: 0; + padding: 0 5px 0 5px; + line-height: 1.75em; + color: #EE9816; +} + +div.related ul li a:hover { + color: #3CA8E7; +} + +div.sphinxsidebarwrapper { + padding: 0; +} + +div.sphinxsidebar { + margin: 0; + padding: 0.5em 15px 15px 0; + width: 210px; + float: right; + font-size: 1em; + text-align: left; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4 { + margin: 1em 0 0.5em 0; + font-size: 1em; + padding: 0.1em 0 0.1em 0.5em; + color: white; + border: 1px solid #86989B; + background-color: #AFC1C4; +} + +div.sphinxsidebar h3 a { + color: white; +} + +div.sphinxsidebar ul { + padding-left: 1.5em; + margin-top: 7px; + padding: 0; + line-height: 130%; +} + +div.sphinxsidebar ul ul { + margin-left: 20px; +} + +div.footer { + background-color: #E3EFF1; + color: #86989B; + padding: 3px 8px 3px 0; + clear: both; + font-size: 0.8em; + text-align: right; +} + +div.footer a { + color: #86989B; + text-decoration: underline; +} + +/* -- body styles ----------------------------------------------------------- */ + +p { + margin: 0.8em 0 0.5em 0; +} + +a { + color: #CA7900; + text-decoration: none; +} + +a:hover { + color: #2491CF; +} + +div.body a { + text-decoration: underline; +} + +h1 { + margin: 0; + padding: 0.7em 0 0.3em 0; + font-size: 1.5em; + color: #11557C; +} + +h2 { + margin: 1.3em 0 0.2em 0; + font-size: 1.35em; + padding: 0; +} + +h3 { + margin: 1em 0 -0.3em 0; + font-size: 1.2em; +} + +div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { + color: black!important; +} + +h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { + display: none; + margin: 0 0 0 0.3em; + padding: 0 0.2em 0 0.2em; + color: #aaa!important; +} + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, +h5:hover a.anchor, h6:hover a.anchor { + display: inline; +} + +h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, +h5 a.anchor:hover, h6 a.anchor:hover { + color: #777; + background-color: #eee; +} + +a.headerlink { + color: #c60f0f!important; + font-size: 1em; + margin-left: 6px; + padding: 0 4px 0 4px; + text-decoration: none!important; +} + +a.headerlink:hover { + background-color: #ccc; + color: white!important; +} + +cite, code, tt { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.01em; +} + +tt { + background-color: #f2f2f2; + border-bottom: 1px solid #ddd; + color: #333; +} + +tt.descname, tt.descclassname, tt.xref { + border: 0; +} + +hr { + border: 1px solid #abc; + margin: 2em; +} + +a tt { + border: 0; + color: #CA7900; +} + +a tt:hover { + color: #2491CF; +} + +pre { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.015em; + line-height: 120%; + padding: 0.5em; + border: 1px solid #ccc; + background-color: #f8f8f8; +} + +pre a { + color: inherit; + text-decoration: underline; +} + +td.linenos pre { + padding: 0.5em 0; +} + +div.quotebar { + background-color: #f8f8f8; + max-width: 250px; + float: right; + padding: 2px 7px; + border: 1px solid #ccc; +} + +div.topic { + background-color: #f8f8f8; +} + +table { + border-collapse: collapse; + margin: 0 -0.5em 0 -0.5em; +} + +table td, table th { + padding: 0.2em 0.5em 0.2em 0.5em; +} + +div.admonition, div.warning { + font-size: 0.9em; + margin: 1em 0 1em 0; + border: 1px solid #86989B; + background-color: #f7f7f7; + padding: 0; +} + +div.admonition p, div.warning p { + margin: 0.5em 1em 0.5em 1em; + padding: 0; +} + +div.admonition pre, div.warning pre { + margin: 0.4em 1em 0.4em 1em; +} + +div.admonition p.admonition-title, +div.warning p.admonition-title { + margin: 0; + padding: 0.1em 0 0.1em 0.5em; + color: white; + border-bottom: 1px solid #86989B; + font-weight: bold; + background-color: #AFC1C4; +} + +div.warning { + border: 1px solid #940000; +} + +div.warning p.admonition-title { + background-color: #CF0000; + border-bottom-color: #940000; +} + +div.admonition ul, div.admonition ol, +div.warning ul, div.warning ol { + margin: 0.1em 0.5em 0.5em 3em; + padding: 0; +} + +div.versioninfo { + margin: 1em 0 0 0; + border: 1px solid #ccc; + background-color: #DDEAF0; + padding: 8px; + line-height: 1.3em; + font-size: 0.9em; +} + +.viewcode-back { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', + 'Verdana', sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; +} Property changes on: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/llvm.css ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/css \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/contents.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = image/png Property changes on: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/contents.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +image/png \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/logo.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = image/png Property changes on: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/logo.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +image/png \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/navigation.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = image/png Property changes on: vendor/lld/lld-release_900-r372316/docs/llvm-theme/static/navigation.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +image/png \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/llvm-theme/theme.conf =================================================================== --- vendor/lld/lld-release_900-r372316/docs/llvm-theme/theme.conf (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/llvm-theme/theme.conf (revision 352529) @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = llvm.css +pygments_style = friendly Property changes on: vendor/lld/lld-release_900-r372316/docs/llvm-theme/theme.conf ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/Driver.rst =================================================================== --- vendor/lld/lld-release_900-r372316/docs/Driver.rst (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/Driver.rst (revision 352529) @@ -0,0 +1,82 @@ +====== +Driver +====== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +.. contents:: + :local: + +Introduction +============ + +This document describes the lld driver. The purpose of this document is to +describe both the motivation and design goals for the driver, as well as details +of the internal implementation. + +Overview +======== + +The lld driver is designed to support a number of different command line +interfaces. The main interfaces we plan to support are binutils' ld, Apple's +ld, and Microsoft's link.exe. + +Flavors +------- + +Each of these different interfaces is referred to as a flavor. There is also an +extra flavor "core" which is used to exercise the core functionality of the +linker it the test suite. + +* gnu +* darwin +* link +* core + +Selecting a Flavor +^^^^^^^^^^^^^^^^^^ + +There are two different ways to tell lld which flavor to be. They are checked in +order, so the second overrides the first. The first is to symlink :program:`lld` +as :program:`lld-{flavor}` or just :program:`{flavor}`. You can also specify +it as the first command line argument using ``-flavor``:: + + $ lld -flavor gnu + +There is a shortcut for ``-flavor core`` as ``-core``. + + +Adding an Option to an existing Flavor +====================================== + +#. Add the option to the desired :file:`lib/Driver/{flavor}Options.td`. + +#. Add to :cpp:class:`lld::FlavorLinkingContext` a getter and setter method + for the option. + +#. Modify :cpp:func:`lld::FlavorDriver::parse` in :file: + `lib/Driver/{Flavor}Driver.cpp` to call the targetInfo setter + for corresponding to the option. + +#. Modify {Flavor}Reader and {Flavor}Writer to use the new targtInfo option. + + +Adding a Flavor +=============== + +#. Add an entry for the flavor in :file:`include/lld/Common/Driver.h` to + :cpp:class:`lld::UniversalDriver::Flavor`. + +#. Add an entry in :file:`lib/Driver/UniversalDriver.cpp` to + :cpp:func:`lld::Driver::strToFlavor` and + :cpp:func:`lld::UniversalDriver::link`. + This allows the flavor to be selected via symlink and `-flavor`. + +#. Add a tablegen file called :file:`lib/Driver/{flavor}Options.td` that + describes the options. If the options are a superset of another driver, that + driver's td file can simply be included. The :file:`{flavor}Options.td` file + must also be added to :file:`lib/Driver/CMakeLists.txt`. + +#. Add a ``{flavor}Driver`` as a subclass of :cpp:class:`lld::Driver` + in :file:`lib/Driver/{flavor}Driver.cpp`. Index: vendor/lld/lld-release_900-r372316/docs/AtomLLD.rst =================================================================== --- vendor/lld/lld-release_900-r372316/docs/AtomLLD.rst (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/AtomLLD.rst (revision 352529) @@ -0,0 +1,62 @@ +ATOM-based lld +============== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +ATOM-based lld is a new set of modular code for creating linker tools. +Currently it supports Mach-O. + +* End-User Features: + + * Compatible with existing linker options + * Reads standard Object Files + * Writes standard Executable Files + * Remove clang's reliance on "the system linker" + * Uses the LLVM `"UIUC" BSD-Style license`__. + +* Applications: + + * Modular design + * Support cross linking + * Easy to add new CPU support + * Can be built as static tool or library + +* Design and Implementation: + + * Extensive unit tests + * Internal linker model can be dumped/read to textual format + * Additional linking features can be plugged in as "passes" + * OS specific and CPU specific code factored out + +Why a new linker? +----------------- + +The fact that clang relies on whatever linker tool you happen to have installed +means that clang has been very conservative adopting features which require a +recent linker. + +In the same way that the MC layer of LLVM has removed clang's reliance on the +system assembler tool, the lld project will remove clang's reliance on the +system linker tool. + + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + design + getting_started + development + open_projects + sphinx_intro + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`search` + +__ http://llvm.org/docs/DeveloperPolicy.html#license Index: vendor/lld/lld-release_900-r372316/docs/design.rst =================================================================== --- vendor/lld/lld-release_900-r372316/docs/design.rst (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/design.rst (revision 352529) @@ -0,0 +1,421 @@ +.. _design: + +Linker Design +============= + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +Introduction +------------ + +lld is a new generation of linker. It is not "section" based like traditional +linkers which mostly just interlace sections from multiple object files into the +output file. Instead, lld is based on "Atoms". Traditional section based +linking work well for simple linking, but their model makes advanced linking +features difficult to implement. Features like dead code stripping, reordering +functions for locality, and C++ coalescing require the linker to work at a finer +grain. + +An atom is an indivisible chunk of code or data. An atom has a set of +attributes, such as: name, scope, content-type, alignment, etc. An atom also +has a list of References. A Reference contains: a kind, an optional offset, an +optional addend, and an optional target atom. + +The Atom model allows the linker to use standard graph theory models for linking +data structures. Each atom is a node, and each Reference is an edge. The +feature of dead code stripping is implemented by following edges to mark all +live atoms, and then delete the non-live atoms. + + +Atom Model +---------- + +An atom is an indivisible chunk of code or data. Typically each user written +function or global variable is an atom. In addition, the compiler may emit +other atoms, such as for literal c-strings or floating point constants, or for +runtime data structures like dwarf unwind info or pointers to initializers. + +A simple "hello world" object file would be modeled like this: + +.. image:: hello.png + +There are three atoms: main, a proxy for printf, and an anonymous atom +containing the c-string literal "hello world". The Atom "main" has two +references. One is the call site for the call to printf, and the other is a +reference for the instruction that loads the address of the c-string literal. + +There are only four different types of atoms: + + * DefinedAtom + 95% of all atoms. This is a chunk of code or data + + * UndefinedAtom + This is a place holder in object files for a reference to some atom + outside the translation unit.During core linking it is usually replaced + by (coalesced into) another Atom. + + * SharedLibraryAtom + If a required symbol name turns out to be defined in a dynamic shared + library (and not some object file). A SharedLibraryAtom is the + placeholder Atom used to represent that fact. + + It is similar to an UndefinedAtom, but it also tracks information + about the associated shared library. + + * AbsoluteAtom + This is for embedded support where some stuff is implemented in ROM at + some fixed address. This atom has no content. It is just an address + that the Writer needs to fix up any references to point to. + + +File Model +---------- + +The linker views the input files as basically containers of Atoms and +References, and just a few attributes of their own. The linker works with three +kinds of files: object files, static libraries, and dynamic shared libraries. +Each kind of file has reader object which presents the file in the model +expected by the linker. + +Object File +~~~~~~~~~~~ + +An object file is just a container of atoms. When linking an object file, a +reader is instantiated which parses the object file and instantiates a set of +atoms representing all content in the .o file. The linker adds all those atoms +to a master graph. + +Static Library (Archive) +~~~~~~~~~~~~~~~~~~~~~~~~ + +This is the traditional unix static archive which is just a collection of object +files with a "table of contents". When linking with a static library, by default +nothing is added to the master graph of atoms. Instead, if after merging all +atoms from object files into a master graph, if any "undefined" atoms are left +remaining in the master graph, the linker reads the table of contents for each +static library to see if any have the needed definitions. If so, the set of +atoms from the specified object file in the static library is added to the +master graph of atoms. + +Dynamic Library (Shared Object) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dynamic libraries are different than object files and static libraries in that +they don't directly add any content. Their purpose is to check at build time +that the remaining undefined references can be resolved at runtime, and provide +a list of dynamic libraries (SO_NEEDED) that will be needed at runtime. The way +this is modeled in the linker is that a dynamic library contributes no atoms to +the initial graph of atoms. Instead, (like static libraries) if there are +"undefined" atoms in the master graph of all atoms, then each dynamic library is +checked to see if exports the required symbol. If so, a "shared library" atom is +instantiated by the by the reader which the linker uses to replace the +"undefined" atom. + +Linking Steps +------------- + +Through the use of abstract Atoms, the core of linking is architecture +independent and file format independent. All command line parsing is factored +out into a separate "options" abstraction which enables the linker to be driven +with different command line sets. + +The overall steps in linking are: + + #. Command line processing + + #. Parsing input files + + #. Resolving + + #. Passes/Optimizations + + #. Generate output file + +The Resolving and Passes steps are done purely on the master graph of atoms, so +they have no notion of file formats such as mach-o or ELF. + + +Input Files +~~~~~~~~~~~ + +Existing developer tools using different file formats for object files. +A goal of lld is to be file format independent. This is done +through a plug-in model for reading object files. The lld::Reader is the base +class for all object file readers. A Reader follows the factory method pattern. +A Reader instantiates an lld::File object (which is a graph of Atoms) from a +given object file (on disk or in-memory). + +Every Reader subclass defines its own "options" class (for instance the mach-o +Reader defines the class ReaderOptionsMachO). This options class is the +one-and-only way to control how the Reader operates when parsing an input file +into an Atom graph. For instance, you may want the Reader to only accept +certain architectures. The options class can be instantiated from command +line options, or it can be subclassed and the ivars programmatically set. + +Resolving +~~~~~~~~~ + +The resolving step takes all the atoms' graphs from each object file and +combines them into one master object graph. Unfortunately, it is not as simple +as appending the atom list from each file into one big list. There are many +cases where atoms need to be coalesced. That is, two or more atoms need to be +coalesced into one atom. This is necessary to support: C language "tentative +definitions", C++ weak symbols for templates and inlines defined in headers, +replacing undefined atoms with actual definition atoms, and for merging copies +of constants like c-strings and floating point constants. + +The linker support coalescing by-name and by-content. By-name is used for +tentative definitions and weak symbols. By-content is used for constant data +that can be merged. + +The resolving process maintains some global linking "state", including a "symbol +table" which is a map from llvm::StringRef to lld::Atom*. With these data +structures, the linker iterates all atoms in all input files. For each atom, it +checks if the atom is named and has a global or hidden scope. If so, the atom +is added to the symbol table map. If there already is a matching atom in that +table, that means the current atom needs to be coalesced with the found atom, or +it is a multiple definition error. + +When all initial input file atoms have been processed by the resolver, a scan is +made to see if there are any undefined atoms in the graph. If there are, the +linker scans all libraries (both static and dynamic) looking for definitions to +replace the undefined atoms. It is an error if any undefined atoms are left +remaining. + +Dead code stripping (if requested) is done at the end of resolving. The linker +does a simple mark-and-sweep. It starts with "root" atoms (like "main" in a main +executable) and follows each references and marks each Atom that it visits as +"live". When done, all atoms not marked "live" are removed. + +The result of the Resolving phase is the creation of an lld::File object. The +goal is that the lld::File model is **the** internal representation +throughout the linker. The file readers parse (mach-o, ELF, COFF) into an +lld::File. The file writers (mach-o, ELF, COFF) taken an lld::File and produce +their file kind, and every Pass only operates on an lld::File. This is not only +a simpler, consistent model, but it enables the state of the linker to be dumped +at any point in the link for testing purposes. + + +Passes +~~~~~~ + +The Passes step is an open ended set of routines that each get a change to +modify or enhance the current lld::File object. Some example Passes are: + + * stub (PLT) generation + + * GOT instantiation + + * order_file optimization + + * branch island generation + + * branch shim generation + + * Objective-C optimizations (Darwin specific) + + * TLV instantiation (Darwin specific) + + * DTrace probe processing (Darwin specific) + + * compact unwind encoding (Darwin specific) + + +Some of these passes are specific to Darwin's runtime environments. But many of +the passes are applicable to any OS (such as generating branch island for out of +range branch instructions). + +The general structure of a pass is to iterate through the atoms in the current +lld::File object, inspecting each atom and doing something. For instance, the +stub pass, looks for call sites to shared library atoms (e.g. call to printf). +It then instantiates a "stub" atom (PLT entry) and a "lazy pointer" atom for +each proxy atom needed, and these new atoms are added to the current lld::File +object. Next, all the noted call sites to shared library atoms have their +References altered to point to the stub atom instead of the shared library atom. + + +Generate Output File +~~~~~~~~~~~~~~~~~~~~ + +Once the passes are done, the output file writer is given current lld::File +object. The writer's job is to create the executable content file wrapper and +place the content of the atoms into it. + +lld uses a plug-in model for writing output files. All concrete writers (e.g. +ELF, mach-o, etc) are subclasses of the lld::Writer class. + +Unlike the Reader class which has just one method to instantiate an lld::File, +the Writer class has multiple methods. The crucial method is to generate the +output file, but there are also methods which allow the Writer to contribute +Atoms to the resolver and specify passes to run. + +An example of contributing +atoms is that if the Writer knows a main executable is being linked and such +an executable requires a specially named entry point (e.g. "_main"), the Writer +can add an UndefinedAtom with that special name to the resolver. This will +cause the resolver to issue an error if that symbol is not defined. + +Sometimes a Writer supports lazily created symbols, such as names for the start +of sections. To support this, the Writer can create a File object which vends +no initial atoms, but does lazily supply atoms by name as needed. + +Every Writer subclass defines its own "options" class (for instance the mach-o +Writer defines the class WriterOptionsMachO). This options class is the +one-and-only way to control how the Writer operates when producing an output +file from an Atom graph. For instance, you may want the Writer to optimize +the output for certain OS versions, or strip local symbols, etc. The options +class can be instantiated from command line options, or it can be subclassed +and the ivars programmatically set. + + +lld::File representations +------------------------- + +Just as LLVM has three representations of its IR model, lld has two +representations of its File/Atom/Reference model: + + * In memory, abstract C++ classes (lld::Atom, lld::Reference, and lld::File). + + * textual (in YAML) + + +Textual representations in YAML +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In designing a textual format we want something easy for humans to read and easy +for the linker to parse. Since an atom has lots of attributes most of which are +usually just the default, we should define default values for every attribute so +that those can be omitted from the text representation. Here is the atoms for a +simple hello world program expressed in YAML:: + + target-triple: x86_64-apple-darwin11 + + atoms: + - name: _main + scope: global + type: code + content: [ 55, 48, 89, e5, 48, 8d, 3d, 00, 00, 00, 00, 30, c0, e8, 00, 00, + 00, 00, 31, c0, 5d, c3 ] + fixups: + - offset: 07 + kind: pcrel32 + target: 2 + - offset: 0E + kind: call32 + target: _fprintf + + - type: c-string + content: [ 73, 5A, 00 ] + + ... + +The biggest use for the textual format will be writing test cases. Writing test +cases in C is problematic because the compiler may vary its output over time for +its own optimization reasons which my inadvertently disable or break the linker +feature trying to be tested. By writing test cases in the linkers own textual +format, we can exactly specify every attribute of every atom and thus target +specific linker logic. + +The textual/YAML format follows the ReaderWriter patterns used in lld. The lld +library comes with the classes: ReaderYAML and WriterYAML. + + +Testing +------- + +The lld project contains a test suite which is being built up as new code is +added to lld. All new lld functionality should have a tests added to the test +suite. The test suite is `lit `_ driven. Each +test is a text file with comments telling lit how to run the test and check the +result To facilitate testing, the lld project builds a tool called lld-core. +This tool reads a YAML file (default from stdin), parses it into one or more +lld::File objects in memory and then feeds those lld::File objects to the +resolver phase. + + +Resolver testing +~~~~~~~~~~~~~~~~ + +Basic testing is the "core linking" or resolving phase. That is where the +linker merges object files. All test cases are written in YAML. One feature of +YAML is that it allows multiple "documents" to be encoding in one YAML stream. +That means one text file can appear to the linker as multiple .o files - the +normal case for the linker. + +Here is a simple example of a core linking test case. It checks that an +undefined atom from one file will be replaced by a definition from another +file:: + + # RUN: lld-core %s | FileCheck %s + + # + # Test that undefined atoms are replaced with defined atoms. + # + + --- + atoms: + - name: foo + definition: undefined + --- + atoms: + - name: foo + scope: global + type: code + ... + + # CHECK: name: foo + # CHECK: scope: global + # CHECK: type: code + # CHECK-NOT: name: foo + # CHECK: ... + + +Passes testing +~~~~~~~~~~~~~~ + +Since Passes just operate on an lld::File object, the lld-core tool has the +option to run a particular pass (after resolving). Thus, you can write a YAML +test case with carefully crafted input to exercise areas of a Pass and the check +the resulting lld::File object as represented in YAML. + + +Design Issues +------------- + +There are a number of open issues in the design of lld. The plan is to wait and +make these design decisions when we need to. + + +Debug Info +~~~~~~~~~~ + +Currently, the lld model says nothing about debug info. But the most popular +debug format is DWARF and there is some impedance mismatch with the lld model +and DWARF. In lld there are just Atoms and only Atoms that need to be in a +special section at runtime have an associated section. Also, Atoms do not have +addresses. The way DWARF is spec'ed different parts of DWARF are supposed to go +into specially named sections and the DWARF references function code by address. + +CPU and OS specific functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, lld has an abstract "Platform" that deals with any CPU or OS specific +differences in linking. We just keep adding virtual methods to the base +Platform class as we find linking areas that might need customization. At some +point we'll need to structure this better. + + +File Attributes +~~~~~~~~~~~~~~~ + +Currently, lld::File just has a path and a way to iterate its atoms. We will +need to add more attributes on a File. For example, some equivalent to the +target triple. There is also a number of cached or computed attributes that +could make various Passes more efficient. For instance, on Darwin there are a +number of Objective-C optimizations that can be done by a Pass. But it would +improve the plain C case if the Objective-C optimization Pass did not have to +scan all atoms looking for any Objective-C data structures. This could be done +if the lld::File object had an attribute that said if the file had any +Objective-C data in it. The Resolving phase would then be required to "merge" +that attribute as object files are added. Index: vendor/lld/lld-release_900-r372316/docs/development.rst =================================================================== --- vendor/lld/lld-release_900-r372316/docs/development.rst (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/development.rst (revision 352529) @@ -0,0 +1,45 @@ +.. _development: + +Development +=========== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +lld is developed as part of the `LLVM `_ project. + +Creating a Reader +----------------- + +See the :ref:`Creating a Reader ` guide. + + +Modifying the Driver +-------------------- + +See :doc:`Driver`. + + +Debugging +--------- + +You can run lld with ``-mllvm -debug`` command line options to enable debugging +printouts. If you want to enable debug information for some specific pass, you +can run it with ``-mllvm '-debug-only='``, where pass is a name used in +the ``DEBUG_WITH_TYPE()`` macro. + + + +Documentation +------------- + +The project documentation is written in reStructuredText and generated using the +`Sphinx `_ documentation generator. For more +information on writing documentation for the project, see the +:ref:`sphinx_intro`. + +.. toctree:: + :hidden: + + Readers + Driver Index: vendor/lld/lld-release_900-r372316/docs/_static/favicon.ico =================================================================== Cannot display: file marked as a binary type. svn:mime-type = image/x-icon Property changes on: vendor/lld/lld-release_900-r372316/docs/_static/favicon.ico ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +image/x-icon \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/hello.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = image/png Property changes on: vendor/lld/lld-release_900-r372316/docs/hello.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +image/png \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/docs/make.bat =================================================================== --- vendor/lld/lld-release_900-r372316/docs/make.bat (nonexistent) +++ vendor/lld/lld-release_900-r372316/docs/make.bat (revision 352529) @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\lld.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\lld.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end Index: vendor/lld/lld-release_900-r372316/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/CMakeLists.txt (revision 352529) @@ -0,0 +1,225 @@ +# Check if lld is built as a standalone project. +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(lld) + cmake_minimum_required(VERSION 3.4.3) + + set(CMAKE_INCLUDE_CURRENT_DIR ON) + set(LLD_BUILT_STANDALONE TRUE) + + find_program(LLVM_CONFIG_PATH "llvm-config" DOC "Path to llvm-config binary") + if(NOT LLVM_CONFIG_PATH) + message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH") + endif() + + execute_process(COMMAND "${LLVM_CONFIG_PATH}" + "--obj-root" + "--includedir" + "--cmakedir" + "--src-root" + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(HAD_ERROR) + message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") + endif() + + string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}") + + list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT) + list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR) + list(GET LLVM_CONFIG_OUTPUT 2 LLVM_CMAKE_PATH) + list(GET LLVM_CONFIG_OUTPUT 3 MAIN_SRC_DIR) + + set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree") + set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include") + set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") + + file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR) + + if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") + message(FATAL_ERROR "LLVMConfig.cmake not found") + endif() + include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake") + + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") + + set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") + include_directories("${LLVM_BINARY_DIR}/include" ${LLVM_INCLUDE_DIRS}) + link_directories(${LLVM_LIBRARY_DIRS}) + + set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) + find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) + + include(AddLLVM) + include(TableGen) + include(HandleLLVMOptions) + + if(LLVM_INCLUDE_TESTS) + include(FindPythonInterp) + if(NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR +"Unable to find Python interpreter, required for testing. + +Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") + endif() + + if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7) + message(FATAL_ERROR "Python 2.7 or newer is required") + endif() + + # Check prebuilt llvm/utils. + if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} + AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX}) + set(LLVM_UTILS_PROVIDED ON) + endif() + + if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + # Note: path not really used, except for checking if lit was found + set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + if(NOT LLVM_UTILS_PROVIDED) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not) + set(LLVM_UTILS_PROVIDED ON) + set(LLD_TEST_DEPS FileCheck not) + endif() + set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest) + if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h + AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} + AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt) + add_subdirectory(${UNITTEST_DIR} utils/unittest) + endif() + else() + # Seek installed Lit. + find_program(LLVM_LIT + NAMES llvm-lit lit.py lit + PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit" + DOC "Path to lit.py") + endif() + + if(LLVM_LIT) + # Define the default arguments to use with 'lit', and an option for the user + # to override. + set(LIT_ARGS_DEFAULT "-sv") + if (MSVC OR XCODE) + set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") + endif() + set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + + # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. + if(WIN32 AND NOT CYGWIN) + set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") + endif() + else() + set(LLVM_INCLUDE_TESTS OFF) + endif() + endif() +endif() + +set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include ) +set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +# Compute the LLD version from the LLVM version. +string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION + ${PACKAGE_VERSION}) +message(STATUS "LLD version: ${LLD_VERSION}") + +string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" LLD_VERSION_MAJOR + ${LLD_VERSION}) +string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR + ${LLD_VERSION}) + +# Determine LLD revision and repository. +# TODO: Figure out a way to get the revision and the repository on windows. +if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" ) + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR} + OUTPUT_VARIABLE LLD_REVISION) + + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR} + OUTPUT_VARIABLE LLD_REPOSITORY) + if ( LLD_REPOSITORY ) + # Replace newline characters with spaces + string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY}) + # Remove leading spaces + STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" ) + # Remove trailing spaces + string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY}) + endif() + + if ( LLD_REVISION ) + # Replace newline characters with spaces + string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION}) + # Remove leading spaces + STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" ) + # Remove trailing spaces + string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION}) + endif() +endif () + +# Configure the Version.inc file. +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in + ${CMAKE_CURRENT_BINARY_DIR}/include/lld/Common/Version.inc) + + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite " +"the makefiles distributed with LLVM. Please create a directory and run cmake " +"from there, passing the path to this source directory as the last argument. " +"This process created the file `CMakeCache.txt' and the directory " +"`CMakeFiles'. Please delete them.") +endif() + +list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules") + +include(AddLLD) + +option(LLD_USE_VTUNE + "Enable VTune user task tracking." + OFF) +if (LLD_USE_VTUNE) + find_package(VTune) + if (VTUNE_FOUND) + include_directories(${VTune_INCLUDE_DIRS}) + list(APPEND LLVM_COMMON_LIBS ${VTune_LIBRARIES}) + add_definitions(-DLLD_HAS_VTUNE) + endif() +endif() + +option(LLD_BUILD_TOOLS + "Build the lld tools. If OFF, just generate build targets." ON) + +if (MSVC) + add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.' + add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header. +endif() + +include_directories(BEFORE + ${CMAKE_CURRENT_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + install(DIRECTORY include/ + DESTINATION include + FILES_MATCHING + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) +endif() + +add_subdirectory(Common) +add_subdirectory(lib) +add_subdirectory(tools/lld) + +if (LLVM_INCLUDE_TESTS) + add_subdirectory(test) + add_subdirectory(unittests) +endif() + +add_subdirectory(docs) +add_subdirectory(COFF) +add_subdirectory(ELF) +add_subdirectory(MinGW) +add_subdirectory(wasm) Property changes on: vendor/lld/lld-release_900-r372316/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Config.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Config.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Config.h (revision 352529) @@ -0,0 +1,232 @@ +//===- Config.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_COFF_CONFIG_H +#define LLD_COFF_CONFIG_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/CachePruning.h" +#include +#include +#include +#include + +namespace lld { +namespace coff { + +using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; +using llvm::COFF::WindowsSubsystem; +using llvm::StringRef; +class DefinedAbsolute; +class DefinedRelative; +class StringChunk; +class Symbol; +class InputFile; + +// Short aliases. +static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; +static const auto ARM64 = llvm::COFF::IMAGE_FILE_MACHINE_ARM64; +static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT; +static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386; + +// Represents an /export option. +struct Export { + StringRef name; // N in /export:N or /export:E=N + StringRef extName; // E in /export:E=N + Symbol *sym = nullptr; + uint16_t ordinal = 0; + bool noname = false; + bool data = false; + bool isPrivate = false; + bool constant = false; + + // If an export is a form of /export:foo=dllname.bar, that means + // that foo should be exported as an alias to bar in the DLL. + // forwardTo is set to "dllname.bar" part. Usually empty. + StringRef forwardTo; + StringChunk *forwardChunk = nullptr; + + // True if this /export option was in .drectves section. + bool directives = false; + StringRef symbolName; + StringRef exportName; // Name in DLL + + bool operator==(const Export &e) { + return (name == e.name && extName == e.extName && + ordinal == e.ordinal && noname == e.noname && + data == e.data && isPrivate == e.isPrivate); + } +}; + +enum class DebugType { + None = 0x0, + CV = 0x1, /// CodeView + PData = 0x2, /// Procedure Data + Fixup = 0x4, /// Relocation Table +}; + +enum class GuardCFLevel { + Off, + NoLongJmp, // Emit gfids but no longjmp tables + Full, // Enable all protections. +}; + +// Global configuration. +struct Configuration { + enum ManifestKind { SideBySide, Embed, No }; + bool is64() { return machine == AMD64 || machine == ARM64; } + + llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN; + size_t wordsize; + bool verbose = false; + WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; + Symbol *entry = nullptr; + bool noEntry = false; + std::string outputFile; + std::string importName; + bool demangle = true; + bool doGC = true; + bool doICF = true; + bool tailMerge; + bool relocatable = true; + bool forceMultiple = false; + bool forceMultipleRes = false; + bool forceUnresolved = false; + bool debug = false; + bool debugDwarf = false; + bool debugGHashes = false; + bool debugSymtab = false; + bool showTiming = false; + bool showSummary = false; + unsigned debugTypes = static_cast(DebugType::None); + std::vector natvisFiles; + llvm::SmallString<128> pdbAltPath; + llvm::SmallString<128> pdbPath; + llvm::SmallString<128> pdbSourcePath; + std::vector argv; + + // Symbols in this set are considered as live by the garbage collector. + std::vector gcroot; + + std::set noDefaultLibs; + bool noDefaultLibAll = false; + + // True if we are creating a DLL. + bool dll = false; + StringRef implib; + std::vector exports; + std::set delayLoads; + std::map dllOrder; + Symbol *delayLoadHelper = nullptr; + + bool saveTemps = false; + + // /guard:cf + GuardCFLevel guardCF = GuardCFLevel::Off; + + // Used for SafeSEH. + bool safeSEH = false; + Symbol *sehTable = nullptr; + Symbol *sehCount = nullptr; + + // Used for /opt:lldlto=N + unsigned ltoo = 2; + + // Used for /opt:lldltojobs=N + unsigned thinLTOJobs = 0; + // Used for /opt:lldltopartitions=N + unsigned ltoPartitions = 1; + + // Used for /opt:lldltocache=path + StringRef ltoCache; + // Used for /opt:lldltocachepolicy=policy + llvm::CachePruningPolicy ltoCachePolicy; + + // Used for /merge:from=to (e.g. /merge:.rdata=.text) + std::map merge; + + // Used for /section=.name,{DEKPRSW} to set section attributes. + std::map section; + + // Options for manifest files. + ManifestKind manifest = No; + int manifestID = 1; + StringRef manifestDependency; + bool manifestUAC = true; + std::vector manifestInput; + StringRef manifestLevel = "'asInvoker'"; + StringRef manifestUIAccess = "'false'"; + StringRef manifestFile; + + // Used for /aligncomm. + std::map alignComm; + + // Used for /failifmismatch. + std::map> mustMatch; + + // Used for /alternatename. + std::map alternateNames; + + // Used for /order. + llvm::StringMap order; + + // Used for /lldmap. + std::string mapFile; + + // Used for /thinlto-index-only: + llvm::StringRef thinLTOIndexOnlyArg; + + // Used for /thinlto-object-prefix-replace: + std::pair thinLTOPrefixReplace; + + // Used for /thinlto-object-suffix-replace: + std::pair thinLTOObjectSuffixReplace; + + uint64_t align = 4096; + uint64_t imageBase = -1; + uint64_t fileAlign = 512; + uint64_t stackReserve = 1024 * 1024; + uint64_t stackCommit = 4096; + uint64_t heapReserve = 1024 * 1024; + uint64_t heapCommit = 4096; + uint32_t majorImageVersion = 0; + uint32_t minorImageVersion = 0; + uint32_t majorOSVersion = 6; + uint32_t minorOSVersion = 0; + uint32_t timestamp = 0; + uint32_t functionPadMin = 0; + bool dynamicBase = true; + bool allowBind = true; + bool nxCompat = true; + bool allowIsolation = true; + bool terminalServerAware = true; + bool largeAddressAware = false; + bool highEntropyVA = false; + bool appContainer = false; + bool mingw = false; + bool warnMissingOrderSymbol = true; + bool warnLocallyDefinedImported = true; + bool warnDebugInfoUnusable = true; + bool incremental = true; + bool integrityCheck = false; + bool killAt = false; + bool repro = false; + bool swaprunCD = false; + bool swaprunNet = false; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; +}; + +extern Configuration *config; + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/Config.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Driver.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Driver.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Driver.cpp (revision 352529) @@ -0,0 +1,1917 @@ +//===- Driver.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Driver.h" +#include "Config.h" +#include "DebugTypes.h" +#include "ICF.h" +#include "InputFiles.h" +#include "MarkLive.h" +#include "MinGW.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Writer.h" +#include "lld/Common/Args.h" +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Threads.h" +#include "lld/Common/Timer.h" +#include "lld/Common/Version.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/COFFModuleDefinition.h" +#include "llvm/Object/WindowsMachineFlag.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/TarWriter.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ToolDrivers/llvm-lib/LibDriver.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::COFF; +using llvm::sys::Process; + +namespace lld { +namespace coff { + +static Timer inputFileTimer("Input File Reading", Timer::root()); + +Configuration *config; +LinkerDriver *driver; + +bool link(ArrayRef args, bool canExitEarly, raw_ostream &diag) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorOS = &diag; + errorHandler().colorDiagnostics = diag.has_colors(); + errorHandler().errorLimitExceededMsg = + "too many errors emitted, stopping now" + " (use /errorlimit:0 to see all errors)"; + errorHandler().exitEarly = canExitEarly; + config = make(); + + symtab = make(); + + driver = make(); + driver->link(args); + + // Call exit() if we can to avoid calling destructors. + if (canExitEarly) + exitLld(errorCount() ? 1 : 0); + + freeArena(); + ObjFile::instances.clear(); + ImportFile::instances.clear(); + BitcodeFile::instances.clear(); + memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); + return !errorCount(); +} + +// Parse options of the form "old;new". +static std::pair getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) + return {"", ""}; + + StringRef s = arg->getValue(); + std::pair ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; +} + +// Drop directory components and replace extension with ".exe" or ".dll". +static std::string getOutputPath(StringRef path) { + auto p = path.find_last_of("\\/"); + StringRef s = (p == StringRef::npos) ? path : path.substr(p + 1); + const char* e = config->dll ? ".dll" : ".exe"; + return (s.substr(0, s.rfind('.')) + e).str(); +} + +// Returns true if S matches /crtend.?\.o$/. +static bool isCrtend(StringRef s) { + if (!s.endswith(".o")) + return false; + s = s.drop_back(2); + if (s.endswith("crtend")) + return true; + return !s.empty() && s.drop_back().endswith("crtend"); +} + +// ErrorOr is not default constructible, so it cannot be used as the type +// parameter of a future. +// FIXME: We could open the file in createFutureForFile and avoid needing to +// return an error here, but for the moment that would cost us a file descriptor +// (a limited resource on Windows) for the duration that the future is pending. +using MBErrPair = std::pair, std::error_code>; + +// Create a std::future that opens and maps a file using the best strategy for +// the host platform. +static std::future createFutureForFile(std::string path) { +#if _WIN32 + // On Windows, file I/O is relatively slow so it is best to do this + // asynchronously. + auto strategy = std::launch::async; +#else + auto strategy = std::launch::deferred; +#endif + return std::async(strategy, [=]() { + auto mbOrErr = MemoryBuffer::getFile(path, + /*FileSize*/ -1, + /*RequiresNullTerminator*/ false); + if (!mbOrErr) + return MBErrPair{nullptr, mbOrErr.getError()}; + return MBErrPair{std::move(*mbOrErr), std::error_code()}; + }); +} + +// Symbol names are mangled by prepending "_" on x86. +static StringRef mangle(StringRef sym) { + assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (config->machine == I386) + return saver.save("_" + sym); + return sym; +} + +static bool findUnderscoreMangle(StringRef sym) { + Symbol *s = symtab->findMangle(mangle(sym)); + return s && !isa(s); +} + +MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr mb) { + MemoryBufferRef mbref = *mb; + make>(std::move(mb)); // take ownership + + if (driver->tar) + driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()), + mbref.getBuffer()); + return mbref; +} + +void LinkerDriver::addBuffer(std::unique_ptr mb, + bool wholeArchive) { + StringRef filename = mb->getBufferIdentifier(); + + MemoryBufferRef mbref = takeBuffer(std::move(mb)); + filePaths.push_back(filename); + + // File type is detected by contents, not by file extension. + switch (identify_magic(mbref.getBuffer())) { + case file_magic::windows_resource: + resources.push_back(mbref); + break; + case file_magic::archive: + if (wholeArchive) { + std::unique_ptr file = + CHECK(Archive::create(mbref), filename + ": failed to parse archive"); + Archive *archive = file.get(); + make>(std::move(file)); // take ownership + + for (MemoryBufferRef m : getArchiveMembers(archive)) + addArchiveBuffer(m, "", filename, 0); + return; + } + symtab->addFile(make(mbref)); + break; + case file_magic::bitcode: + symtab->addFile(make(mbref, "", 0)); + break; + case file_magic::coff_object: + case file_magic::coff_import_library: + symtab->addFile(make(mbref)); + break; + case file_magic::pdb: + loadTypeServerSource(mbref); + break; + case file_magic::coff_cl_gl_object: + error(filename + ": is not a native COFF file. Recompile without /GL"); + break; + case file_magic::pecoff_executable: + if (filename.endswith_lower(".dll")) { + error(filename + ": bad file type. Did you specify a DLL instead of an " + "import library?"); + break; + } + LLVM_FALLTHROUGH; + default: + error(mbref.getBufferIdentifier() + ": unknown file type"); + break; + } +} + +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { + auto future = + std::make_shared>(createFutureForFile(path)); + std::string pathStr = path; + enqueueTask([=]() { + auto mbOrErr = future->get(); + if (mbOrErr.second) { + std::string msg = + "could not open '" + pathStr + "': " + mbOrErr.second.message(); + // Check if the filename is a typo for an option flag. OptTable thinks + // that all args that are not known options and that start with / are + // filenames, but e.g. `/nodefaultlibs` is more likely a typo for + // the option `/nodefaultlib` than a reference to a file in the root + // directory. + std::string nearest; + if (COFFOptTable().findNearest(pathStr, nearest) > 1) + error(msg); + else + error(msg + "; did you mean '" + nearest + "'"); + } else + driver->addBuffer(std::move(mbOrErr.first), wholeArchive); + }); +} + +void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, + StringRef parentName, + uint64_t offsetInArchive) { + file_magic magic = identify_magic(mb.getBuffer()); + if (magic == file_magic::coff_import_library) { + InputFile *imp = make(mb); + imp->parentName = parentName; + symtab->addFile(imp); + return; + } + + InputFile *obj; + if (magic == file_magic::coff_object) { + obj = make(mb); + } else if (magic == file_magic::bitcode) { + obj = make(mb, parentName, offsetInArchive); + } else { + error("unknown file type: " + mb.getBufferIdentifier()); + return; + } + + obj->parentName = parentName; + symtab->addFile(obj); + log("Loaded " + toString(obj) + " for " + symName); +} + +void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, + const Archive::Symbol &sym, + StringRef parentName) { + + auto reportBufferError = [=](Error &&e, StringRef childName) { + fatal("could not get the buffer for the member defining symbol " + + toCOFFString(sym) + ": " + parentName + "(" + childName + "): " + + toString(std::move(e))); + }; + + if (!c.getParent()->isThin()) { + uint64_t offsetInArchive = c.getChildOffset(); + Expected mbOrErr = c.getMemoryBufferRef(); + if (!mbOrErr) + reportBufferError(mbOrErr.takeError(), check(c.getFullName())); + MemoryBufferRef mb = mbOrErr.get(); + enqueueTask([=]() { + driver->addArchiveBuffer(mb, toCOFFString(sym), parentName, + offsetInArchive); + }); + return; + } + + std::string childName = CHECK( + c.getFullName(), + "could not get the filename for the member defining symbol " + + toCOFFString(sym)); + auto future = std::make_shared>( + createFutureForFile(childName)); + enqueueTask([=]() { + auto mbOrErr = future->get(); + if (mbOrErr.second) + reportBufferError(errorCodeToError(mbOrErr.second), childName); + driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), + toCOFFString(sym), parentName, + /*OffsetInArchive=*/0); + }); +} + +static bool isDecorated(StringRef sym) { + return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") || + (!config->mingw && sym.contains('@')); +} + +// Parses .drectve section contents and returns a list of files +// specified by /defaultlib. +void LinkerDriver::parseDirectives(InputFile *file) { + StringRef s = file->getDirectives(); + if (s.empty()) + return; + + log("Directives: " + toString(file) + ": " + s); + + ArgParser parser; + // .drectve is always tokenized using Windows shell rules. + // /EXPORT: option can appear too many times, processing in fastpath. + opt::InputArgList args; + std::vector exports; + std::tie(args, exports) = parser.parseDirectives(s); + + for (StringRef e : exports) { + // If a common header file contains dllexported function + // declarations, many object files may end up with having the + // same /EXPORT options. In order to save cost of parsing them, + // we dedup them first. + if (!directivesExports.insert(e).second) + continue; + + Export exp = parseExport(e); + if (config->machine == I386 && config->mingw) { + if (!isDecorated(exp.name)) + exp.name = saver.save("_" + exp.name); + if (!exp.extName.empty() && !isDecorated(exp.extName)) + exp.extName = saver.save("_" + exp.extName); + } + exp.directives = true; + config->exports.push_back(exp); + } + + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_aligncomm: + parseAligncomm(arg->getValue()); + break; + case OPT_alternatename: + parseAlternateName(arg->getValue()); + break; + case OPT_defaultlib: + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false); + break; + case OPT_entry: + config->entry = addUndefined(mangle(arg->getValue())); + break; + case OPT_failifmismatch: + checkFailIfMismatch(arg->getValue(), file); + break; + case OPT_incl: + addUndefined(arg->getValue()); + break; + case OPT_merge: + parseMerge(arg->getValue()); + break; + case OPT_nodefaultlib: + config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); + break; + case OPT_section: + parseSection(arg->getValue()); + break; + case OPT_subsystem: + parseSubsystem(arg->getValue(), &config->subsystem, + &config->majorOSVersion, &config->minorOSVersion); + break; + // Only add flags here that link.exe accepts in + // `#pragma comment(linker, "/flag")`-generated sections. + case OPT_editandcontinue: + case OPT_guardsym: + case OPT_throwingnew: + break; + default: + error(arg->getSpelling() + " is not allowed in .drectve"); + } + } +} + +// Find file from search paths. You can omit ".obj", this function takes +// care of that. Note that the returned path is not guaranteed to exist. +StringRef LinkerDriver::doFindFile(StringRef filename) { + bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); + if (hasPathSep) + return filename; + bool hasExt = filename.contains('.'); + for (StringRef dir : searchPaths) { + SmallString<128> path = dir; + sys::path::append(path, filename); + if (sys::fs::exists(path.str())) + return saver.save(path.str()); + if (!hasExt) { + path.append(".obj"); + if (sys::fs::exists(path.str())) + return saver.save(path.str()); + } + } + return filename; +} + +static Optional getUniqueID(StringRef path) { + sys::fs::UniqueID ret; + if (sys::fs::getUniqueID(path, ret)) + return None; + return ret; +} + +// Resolves a file path. This never returns the same path +// (in that case, it returns None). +Optional LinkerDriver::findFile(StringRef filename) { + StringRef path = doFindFile(filename); + + if (Optional id = getUniqueID(path)) { + bool seen = !visitedFiles.insert(*id).second; + if (seen) + return None; + } + + if (path.endswith_lower(".lib")) + visitedLibs.insert(sys::path::filename(path)); + return path; +} + +// MinGW specific. If an embedded directive specified to link to +// foo.lib, but it isn't found, try libfoo.a instead. +StringRef LinkerDriver::doFindLibMinGW(StringRef filename) { + if (filename.contains('/') || filename.contains('\\')) + return filename; + + SmallString<128> s = filename; + sys::path::replace_extension(s, ".a"); + StringRef libName = saver.save("lib" + s.str()); + return doFindFile(libName); +} + +// Find library file from search path. +StringRef LinkerDriver::doFindLib(StringRef filename) { + // Add ".lib" to Filename if that has no file extension. + bool hasExt = filename.contains('.'); + if (!hasExt) + filename = saver.save(filename + ".lib"); + StringRef ret = doFindFile(filename); + // For MinGW, if the find above didn't turn up anything, try + // looking for a MinGW formatted library name. + if (config->mingw && ret == filename) + return doFindLibMinGW(filename); + return ret; +} + +// Resolves a library path. /nodefaultlib options are taken into +// consideration. This never returns the same path (in that case, +// it returns None). +Optional LinkerDriver::findLib(StringRef filename) { + if (config->noDefaultLibAll) + return None; + if (!visitedLibs.insert(filename.lower()).second) + return None; + + StringRef path = doFindLib(filename); + if (config->noDefaultLibs.count(path.lower())) + return None; + + if (Optional id = getUniqueID(path)) + if (!visitedFiles.insert(*id).second) + return None; + return path; +} + +// Parses LIB environment which contains a list of search paths. +void LinkerDriver::addLibSearchPaths() { + Optional envOpt = Process::GetEnv("LIB"); + if (!envOpt.hasValue()) + return; + StringRef env = saver.save(*envOpt); + while (!env.empty()) { + StringRef path; + std::tie(path, env) = env.split(';'); + searchPaths.push_back(path); + } +} + +Symbol *LinkerDriver::addUndefined(StringRef name) { + Symbol *b = symtab->addUndefined(name); + if (!b->isGCRoot) { + b->isGCRoot = true; + config->gcroot.push_back(b); + } + return b; +} + +StringRef LinkerDriver::mangleMaybe(Symbol *s) { + // If the plain symbol name has already been resolved, do nothing. + Undefined *unmangled = dyn_cast(s); + if (!unmangled) + return ""; + + // Otherwise, see if a similar, mangled symbol exists in the symbol table. + Symbol *mangled = symtab->findMangle(unmangled->getName()); + if (!mangled) + return ""; + + // If we find a similar mangled symbol, make this an alias to it and return + // its name. + log(unmangled->getName() + " aliased to " + mangled->getName()); + unmangled->weakAlias = symtab->addUndefined(mangled->getName()); + return mangled->getName(); +} + +// Windows specific -- find default entry point name. +// +// There are four different entry point functions for Windows executables, +// each of which corresponds to a user-defined "main" function. This function +// infers an entry point from a user-defined "main" function. +StringRef LinkerDriver::findDefaultEntry() { + assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN && + "must handle /subsystem before calling this"); + + if (config->mingw) + return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI + ? "WinMainCRTStartup" + : "mainCRTStartup"); + + if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { + if (findUnderscoreMangle("wWinMain")) { + if (!findUnderscoreMangle("WinMain")) + return mangle("wWinMainCRTStartup"); + warn("found both wWinMain and WinMain; using latter"); + } + return mangle("WinMainCRTStartup"); + } + if (findUnderscoreMangle("wmain")) { + if (!findUnderscoreMangle("main")) + return mangle("wmainCRTStartup"); + warn("found both wmain and main; using latter"); + } + return mangle("mainCRTStartup"); +} + +WindowsSubsystem LinkerDriver::inferSubsystem() { + if (config->dll) + return IMAGE_SUBSYSTEM_WINDOWS_GUI; + if (config->mingw) + return IMAGE_SUBSYSTEM_WINDOWS_CUI; + // Note that link.exe infers the subsystem from the presence of these + // functions even if /entry: or /nodefaultlib are passed which causes them + // to not be called. + bool haveMain = findUnderscoreMangle("main"); + bool haveWMain = findUnderscoreMangle("wmain"); + bool haveWinMain = findUnderscoreMangle("WinMain"); + bool haveWWinMain = findUnderscoreMangle("wWinMain"); + if (haveMain || haveWMain) { + if (haveWinMain || haveWWinMain) { + warn(std::string("found ") + (haveMain ? "main" : "wmain") + " and " + + (haveWinMain ? "WinMain" : "wWinMain") + + "; defaulting to /subsystem:console"); + } + return IMAGE_SUBSYSTEM_WINDOWS_CUI; + } + if (haveWinMain || haveWWinMain) + return IMAGE_SUBSYSTEM_WINDOWS_GUI; + return IMAGE_SUBSYSTEM_UNKNOWN; +} + +static uint64_t getDefaultImageBase() { + if (config->is64()) + return config->dll ? 0x180000000 : 0x140000000; + return config->dll ? 0x10000000 : 0x400000; +} + +static std::string createResponseFile(const opt::InputArgList &args, + ArrayRef filePaths, + ArrayRef searchPaths) { + SmallString<0> data; + raw_svector_ostream os(data); + + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_linkrepro: + case OPT_INPUT: + case OPT_defaultlib: + case OPT_libpath: + case OPT_manifest: + case OPT_manifest_colon: + case OPT_manifestdependency: + case OPT_manifestfile: + case OPT_manifestinput: + case OPT_manifestuac: + break; + case OPT_implib: + case OPT_pdb: + case OPT_out: + os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n"; + break; + default: + os << toString(*arg) << "\n"; + } + } + + for (StringRef path : searchPaths) { + std::string relPath = relativeToRoot(path); + os << "/libpath:" << quote(relPath) << "\n"; + } + + for (StringRef path : filePaths) + os << quote(relativeToRoot(path)) << "\n"; + + return data.str(); +} + +enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab }; + +static DebugKind parseDebugKind(const opt::InputArgList &args) { + auto *a = args.getLastArg(OPT_debug, OPT_debug_opt); + if (!a) + return DebugKind::None; + if (a->getNumValues() == 0) + return DebugKind::Full; + + DebugKind debug = StringSwitch(a->getValue()) + .CaseLower("none", DebugKind::None) + .CaseLower("full", DebugKind::Full) + .CaseLower("fastlink", DebugKind::FastLink) + // LLD extensions + .CaseLower("ghash", DebugKind::GHash) + .CaseLower("dwarf", DebugKind::Dwarf) + .CaseLower("symtab", DebugKind::Symtab) + .Default(DebugKind::Unknown); + + if (debug == DebugKind::FastLink) { + warn("/debug:fastlink unsupported; using /debug:full"); + return DebugKind::Full; + } + if (debug == DebugKind::Unknown) { + error("/debug: unknown option: " + Twine(a->getValue())); + return DebugKind::None; + } + return debug; +} + +static unsigned parseDebugTypes(const opt::InputArgList &args) { + unsigned debugTypes = static_cast(DebugType::None); + + if (auto *a = args.getLastArg(OPT_debugtype)) { + SmallVector types; + StringRef(a->getValue()) + .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + + for (StringRef type : types) { + unsigned v = StringSwitch(type.lower()) + .Case("cv", static_cast(DebugType::CV)) + .Case("pdata", static_cast(DebugType::PData)) + .Case("fixup", static_cast(DebugType::Fixup)) + .Default(0); + if (v == 0) { + warn("/debugtype: unknown option '" + type + "'"); + continue; + } + debugTypes |= v; + } + return debugTypes; + } + + // Default debug types + debugTypes = static_cast(DebugType::CV); + if (args.hasArg(OPT_driver)) + debugTypes |= static_cast(DebugType::PData); + if (args.hasArg(OPT_profile)) + debugTypes |= static_cast(DebugType::Fixup); + + return debugTypes; +} + +static std::string getMapFile(const opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file); + if (!arg) + return ""; + if (arg->getOption().getID() == OPT_lldmap_file) + return arg->getValue(); + + assert(arg->getOption().getID() == OPT_lldmap); + StringRef outFile = config->outputFile; + return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); +} + +static std::string getImplibPath() { + if (!config->implib.empty()) + return config->implib; + SmallString<128> out = StringRef(config->outputFile); + sys::path::replace_extension(out, ".lib"); + return out.str(); +} + +// +// The import name is caculated as the following: +// +// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY +// -----+----------------+---------------------+------------------ +// LINK | {value} | {value}.{.dll/.exe} | {output name} +// LIB | {value} | {value}.dll | {output name}.dll +// +static std::string getImportName(bool asLib) { + SmallString<128> out; + + if (config->importName.empty()) { + out.assign(sys::path::filename(config->outputFile)); + if (asLib) + sys::path::replace_extension(out, ".dll"); + } else { + out.assign(config->importName); + if (!sys::path::has_extension(out)) + sys::path::replace_extension(out, + (config->dll || asLib) ? ".dll" : ".exe"); + } + + return out.str(); +} + +static void createImportLibrary(bool asLib) { + std::vector exports; + for (Export &e1 : config->exports) { + COFFShortExport e2; + e2.Name = e1.name; + e2.SymbolName = e1.symbolName; + e2.ExtName = e1.extName; + e2.Ordinal = e1.ordinal; + e2.Noname = e1.noname; + e2.Data = e1.data; + e2.Private = e1.isPrivate; + e2.Constant = e1.constant; + exports.push_back(e2); + } + + auto handleError = [](Error &&e) { + handleAllErrors(std::move(e), + [](ErrorInfoBase &eib) { error(eib.message()); }); + }; + std::string libName = getImportName(asLib); + std::string path = getImplibPath(); + + if (!config->incremental) { + handleError(writeImportLibrary(libName, path, exports, config->machine, + config->mingw)); + return; + } + + // If the import library already exists, replace it only if the contents + // have changed. + ErrorOr> oldBuf = MemoryBuffer::getFile( + path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); + if (!oldBuf) { + handleError(writeImportLibrary(libName, path, exports, config->machine, + config->mingw)); + return; + } + + SmallString<128> tmpName; + if (std::error_code ec = + sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName)) + fatal("cannot create temporary file for import library " + path + ": " + + ec.message()); + + if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine, + config->mingw)) { + handleError(std::move(e)); + return; + } + + std::unique_ptr newBuf = check(MemoryBuffer::getFile( + tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); + if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { + oldBuf->reset(); + handleError(errorCodeToError(sys::fs::rename(tmpName, path))); + } else { + sys::fs::remove(tmpName); + } +} + +static void parseModuleDefs(StringRef path) { + std::unique_ptr mb = CHECK( + MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); + COFFModuleDefinition m = check(parseCOFFModuleDefinition( + mb->getMemBufferRef(), config->machine, config->mingw)); + + if (config->outputFile.empty()) + config->outputFile = saver.save(m.OutputFile); + config->importName = saver.save(m.ImportName); + if (m.ImageBase) + config->imageBase = m.ImageBase; + if (m.StackReserve) + config->stackReserve = m.StackReserve; + if (m.StackCommit) + config->stackCommit = m.StackCommit; + if (m.HeapReserve) + config->heapReserve = m.HeapReserve; + if (m.HeapCommit) + config->heapCommit = m.HeapCommit; + if (m.MajorImageVersion) + config->majorImageVersion = m.MajorImageVersion; + if (m.MinorImageVersion) + config->minorImageVersion = m.MinorImageVersion; + if (m.MajorOSVersion) + config->majorOSVersion = m.MajorOSVersion; + if (m.MinorOSVersion) + config->minorOSVersion = m.MinorOSVersion; + + for (COFFShortExport e1 : m.Exports) { + Export e2; + // In simple cases, only Name is set. Renamed exports are parsed + // and set as "ExtName = Name". If Name has the form "OtherDll.Func", + // it shouldn't be a normal exported function but a forward to another + // DLL instead. This is supported by both MS and GNU linkers. + if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) { + e2.name = saver.save(e1.ExtName); + e2.forwardTo = saver.save(e1.Name); + config->exports.push_back(e2); + continue; + } + e2.name = saver.save(e1.Name); + e2.extName = saver.save(e1.ExtName); + e2.ordinal = e1.Ordinal; + e2.noname = e1.Noname; + e2.data = e1.Data; + e2.isPrivate = e1.Private; + e2.constant = e1.Constant; + config->exports.push_back(e2); + } +} + +void LinkerDriver::enqueueTask(std::function task) { + taskQueue.push_back(std::move(task)); +} + +bool LinkerDriver::run() { + ScopedTimer t(inputFileTimer); + + bool didWork = !taskQueue.empty(); + while (!taskQueue.empty()) { + taskQueue.front()(); + taskQueue.pop_front(); + } + return didWork; +} + +// Parse an /order file. If an option is given, the linker places +// COMDAT sections in the same order as their names appear in the +// given file. +static void parseOrderFile(StringRef arg) { + // For some reason, the MSVC linker requires a filename to be + // preceded by "@". + if (!arg.startswith("@")) { + error("malformed /order option: '@' missing"); + return; + } + + // Get a list of all comdat sections for error checking. + DenseSet set; + for (Chunk *c : symtab->getChunks()) + if (auto *sec = dyn_cast(c)) + if (sec->sym) + set.insert(sec->sym->getName()); + + // Open a file. + StringRef path = arg.substr(1); + std::unique_ptr mb = CHECK( + MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); + + // Parse a file. An order file contains one symbol per line. + // All symbols that were not present in a given order file are + // considered to have the lowest priority 0 and are placed at + // end of an output section. + for (std::string s : args::getLines(mb->getMemBufferRef())) { + if (config->machine == I386 && !isDecorated(s)) + s = "_" + s; + + if (set.count(s) == 0) { + if (config->warnMissingOrderSymbol) + warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]"); + } + else + config->order[s] = INT_MIN + config->order.size(); + } +} + +static void markAddrsig(Symbol *s) { + if (auto *d = dyn_cast_or_null(s)) + if (SectionChunk *c = dyn_cast_or_null(d->getChunk())) + c->keepUnique = true; +} + +static void findKeepUniqueSections() { + // Exported symbols could be address-significant in other executables or DSOs, + // so we conservatively mark them as address-significant. + for (Export &r : config->exports) + markAddrsig(r.sym); + + // Visit the address-significance table in each object file and mark each + // referenced symbol as address-significant. + for (ObjFile *obj : ObjFile::instances) { + ArrayRef syms = obj->getSymbols(); + if (obj->addrsigSec) { + ArrayRef contents; + cantFail( + obj->getCOFFObj()->getSectionContents(obj->addrsigSec, contents)); + const uint8_t *cur = contents.begin(); + while (cur != contents.end()) { + unsigned size; + const char *err; + uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err); + if (err) + fatal(toString(obj) + ": could not decode addrsig section: " + err); + if (symIndex >= syms.size()) + fatal(toString(obj) + ": invalid symbol index in addrsig section"); + markAddrsig(syms[symIndex]); + cur += size; + } + } else { + // If an object file does not have an address-significance table, + // conservatively mark all of its symbols as address-significant. + for (Symbol *s : syms) + markAddrsig(s); + } + } +} + +// link.exe replaces each %foo% in altPath with the contents of environment +// variable foo, and adds the two magic env vars _PDB (expands to the basename +// of pdb's output path) and _EXT (expands to the extension of the output +// binary). +// lld only supports %_PDB% and %_EXT% and warns on references to all other env +// vars. +static void parsePDBAltPath(StringRef altPath) { + SmallString<128> buf; + StringRef pdbBasename = + sys::path::filename(config->pdbPath, sys::path::Style::windows); + StringRef binaryExtension = + sys::path::extension(config->outputFile, sys::path::Style::windows); + if (!binaryExtension.empty()) + binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. + + // Invariant: + // +--------- cursor ('a...' might be the empty string). + // | +----- firstMark + // | | +- secondMark + // v v v + // a...%...%... + size_t cursor = 0; + while (cursor < altPath.size()) { + size_t firstMark, secondMark; + if ((firstMark = altPath.find('%', cursor)) == StringRef::npos || + (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) { + // Didn't find another full fragment, treat rest of string as literal. + buf.append(altPath.substr(cursor)); + break; + } + + // Found a full fragment. Append text in front of first %, and interpret + // text between first and second % as variable name. + buf.append(altPath.substr(cursor, firstMark - cursor)); + StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1); + if (var.equals_lower("%_pdb%")) + buf.append(pdbBasename); + else if (var.equals_lower("%_ext%")) + buf.append(binaryExtension); + else { + warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + + var + " as literal"); + buf.append(var); + } + + cursor = secondMark + 1; + } + + config->pdbAltPath = buf; +} + +/// Check that at most one resource obj file was used. +/// Call after ObjFile::Instances is complete. +static void diagnoseMultipleResourceObjFiles() { + // The .rsrc$01 section in a resource obj file contains a tree description + // of resources. Merging multiple resource obj files would require merging + // the trees instead of using usual linker section merging semantics. + // Since link.exe disallows linking more than one resource obj file with + // LNK4078, mirror that. The normal use of resource files is to give the + // linker many .res files, which are then converted to a single resource obj + // file internally, so this is not a big restriction in practice. + ObjFile *resourceObjFile = nullptr; + for (ObjFile *f : ObjFile::instances) { + if (!f->isResourceObjFile) + continue; + + if (!resourceObjFile) { + resourceObjFile = f; + continue; + } + + error(toString(f) + + ": more than one resource obj file not allowed, already got " + + toString(resourceObjFile)); + } +} + +// In MinGW, if no symbols are chosen to be exported, then all symbols are +// automatically exported by default. This behavior can be forced by the +// -export-all-symbols option, so that it happens even when exports are +// explicitly specified. The automatic behavior can be disabled using the +// -exclude-all-symbols option, so that lld-link behaves like link.exe rather +// than MinGW in the case that nothing is explicitly exported. +void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { + if (!config->dll) + return; + + if (!args.hasArg(OPT_export_all_symbols)) { + if (!config->exports.empty()) + return; + if (args.hasArg(OPT_exclude_all_symbols)) + return; + } + + AutoExporter exporter; + + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional path = doFindFile(arg->getValue())) + exporter.addWholeArchive(*path); + + symtab->forEachSymbol([&](Symbol *s) { + auto *def = dyn_cast(s); + if (!exporter.shouldExport(def)) + return; + + Export e; + e.name = def->getName(); + e.sym = def; + if (Chunk *c = def->getChunk()) + if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + e.data = true; + config->exports.push_back(e); + }); +} + +static const char *libcallRoutineNames[] = { +#define HANDLE_LIBCALL(code, name) name, +#include "llvm/IR/RuntimeLibcalls.def" +#undef HANDLE_LIBCALL +}; + +void LinkerDriver::link(ArrayRef argsArr) { + // Needed for LTO. + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + InitializeAllAsmPrinters(); + + // If the first command line argument is "/lib", link.exe acts like lib.exe. + // We call our own implementation of lib.exe that understands bitcode files. + if (argsArr.size() > 1 && StringRef(argsArr[1]).equals_lower("/lib")) { + if (llvm::libDriverMain(argsArr.slice(1)) != 0) + fatal("lib failed"); + return; + } + + // Parse command line options. + ArgParser parser; + opt::InputArgList args = parser.parseLINK(argsArr); + + // Parse and evaluate -mllvm options. + std::vector v; + v.push_back("lld-link (LLVM option parsing)"); + for (auto *arg : args.filtered(OPT_mllvm)) + v.push_back(arg->getValue()); + cl::ParseCommandLineOptions(v.size(), v.data()); + + // Handle /errorlimit early, because error() depends on it. + if (auto *arg = args.getLastArg(OPT_errorlimit)) { + int n = 20; + StringRef s = arg->getValue(); + if (s.getAsInteger(10, n)) + error(arg->getSpelling() + " number expected, but got " + s); + errorHandler().errorLimit = n; + } + + // Handle /help + if (args.hasArg(OPT_help)) { + printHelp(argsArr[0]); + return; + } + + lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true); + + if (args.hasArg(OPT_show_timing)) + config->showTiming = true; + + config->showSummary = args.hasArg(OPT_summary); + + ScopedTimer t(Timer::root()); + // Handle --version, which is an lld extension. This option is a bit odd + // because it doesn't start with "/", but we deliberately chose "--" to + // avoid conflict with /version and for compatibility with clang-cl. + if (args.hasArg(OPT_dash_dash_version)) { + outs() << getLLDVersion() << "\n"; + return; + } + + // Handle /lldmingw early, since it can potentially affect how other + // options are handled. + config->mingw = args.hasArg(OPT_lldmingw); + + if (auto *arg = args.getLastArg(OPT_linkrepro)) { + SmallString<64> path = StringRef(arg->getValue()); + sys::path::append(path, "repro.tar"); + + Expected> errOrWriter = + TarWriter::create(path, "repro"); + + if (errOrWriter) { + tar = std::move(*errOrWriter); + } else { + error("/linkrepro: failed to open " + path + ": " + + toString(errOrWriter.takeError())); + } + } + + if (!args.hasArg(OPT_INPUT)) { + if (args.hasArg(OPT_deffile)) + config->noEntry = true; + else + fatal("no input files"); + } + + // Construct search path list. + searchPaths.push_back(""); + for (auto *arg : args.filtered(OPT_libpath)) + searchPaths.push_back(arg->getValue()); + addLibSearchPaths(); + + // Handle /ignore + for (auto *arg : args.filtered(OPT_ignore)) { + SmallVector vec; + StringRef(arg->getValue()).split(vec, ','); + for (StringRef s : vec) { + if (s == "4037") + config->warnMissingOrderSymbol = false; + else if (s == "4099") + config->warnDebugInfoUnusable = false; + else if (s == "4217") + config->warnLocallyDefinedImported = false; + // Other warning numbers are ignored. + } + } + + // Handle /out + if (auto *arg = args.getLastArg(OPT_out)) + config->outputFile = arg->getValue(); + + // Handle /verbose + if (args.hasArg(OPT_verbose)) + config->verbose = true; + errorHandler().verbose = config->verbose; + + // Handle /force or /force:unresolved + if (args.hasArg(OPT_force, OPT_force_unresolved)) + config->forceUnresolved = true; + + // Handle /force or /force:multiple + if (args.hasArg(OPT_force, OPT_force_multiple)) + config->forceMultiple = true; + + // Handle /force or /force:multipleres + if (args.hasArg(OPT_force, OPT_force_multipleres)) + config->forceMultipleRes = true; + + // Handle /debug + DebugKind debug = parseDebugKind(args); + if (debug == DebugKind::Full || debug == DebugKind::Dwarf || + debug == DebugKind::GHash) { + config->debug = true; + config->incremental = true; + } + + // Handle /demangle + config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); + + // Handle /debugtype + config->debugTypes = parseDebugTypes(args); + + // Handle /pdb + bool shouldCreatePDB = + (debug == DebugKind::Full || debug == DebugKind::GHash); + if (shouldCreatePDB) { + if (auto *arg = args.getLastArg(OPT_pdb)) + config->pdbPath = arg->getValue(); + if (auto *arg = args.getLastArg(OPT_pdbaltpath)) + config->pdbAltPath = arg->getValue(); + if (args.hasArg(OPT_natvis)) + config->natvisFiles = args.getAllArgValues(OPT_natvis); + + if (auto *arg = args.getLastArg(OPT_pdb_source_path)) + config->pdbSourcePath = arg->getValue(); + } + + // Handle /noentry + if (args.hasArg(OPT_noentry)) { + if (args.hasArg(OPT_dll)) + config->noEntry = true; + else + error("/noentry must be specified with /dll"); + } + + // Handle /dll + if (args.hasArg(OPT_dll)) { + config->dll = true; + config->manifestID = 2; + } + + // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase + // because we need to explicitly check whether that option or its inverse was + // present in the argument list in order to handle /fixed. + auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); + if (dynamicBaseArg && + dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) + config->dynamicBase = false; + + // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the + // default setting for any other project type.", but link.exe defaults to + // /FIXED:NO for exe outputs as well. Match behavior, not docs. + bool fixed = args.hasFlag(OPT_fixed, OPT_fixed_no, false); + if (fixed) { + if (dynamicBaseArg && + dynamicBaseArg->getOption().getID() == OPT_dynamicbase) { + error("/fixed must not be specified with /dynamicbase"); + } else { + config->relocatable = false; + config->dynamicBase = false; + } + } + + // Handle /appcontainer + config->appContainer = + args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); + + // Handle /machine + if (auto *arg = args.getLastArg(OPT_machine)) { + config->machine = getMachineType(arg->getValue()); + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) + fatal(Twine("unknown /machine argument: ") + arg->getValue()); + } + + // Handle /nodefaultlib: + for (auto *arg : args.filtered(OPT_nodefaultlib)) + config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); + + // Handle /nodefaultlib + if (args.hasArg(OPT_nodefaultlib_all)) + config->noDefaultLibAll = true; + + // Handle /base + if (auto *arg = args.getLastArg(OPT_base)) + parseNumbers(arg->getValue(), &config->imageBase); + + // Handle /filealign + if (auto *arg = args.getLastArg(OPT_filealign)) { + parseNumbers(arg->getValue(), &config->fileAlign); + if (!isPowerOf2_64(config->fileAlign)) + error("/filealign: not a power of two: " + Twine(config->fileAlign)); + } + + // Handle /stack + if (auto *arg = args.getLastArg(OPT_stack)) + parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit); + + // Handle /guard:cf + if (auto *arg = args.getLastArg(OPT_guard)) + parseGuard(arg->getValue()); + + // Handle /heap + if (auto *arg = args.getLastArg(OPT_heap)) + parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit); + + // Handle /version + if (auto *arg = args.getLastArg(OPT_version)) + parseVersion(arg->getValue(), &config->majorImageVersion, + &config->minorImageVersion); + + // Handle /subsystem + if (auto *arg = args.getLastArg(OPT_subsystem)) + parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion, + &config->minorOSVersion); + + // Handle /timestamp + if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) { + if (arg->getOption().getID() == OPT_repro) { + config->timestamp = 0; + config->repro = true; + } else { + config->repro = false; + StringRef value(arg->getValue()); + if (value.getAsInteger(0, config->timestamp)) + fatal(Twine("invalid timestamp: ") + value + + ". Expected 32-bit integer"); + } + } else { + config->repro = false; + config->timestamp = time(nullptr); + } + + // Handle /alternatename + for (auto *arg : args.filtered(OPT_alternatename)) + parseAlternateName(arg->getValue()); + + // Handle /include + for (auto *arg : args.filtered(OPT_incl)) + addUndefined(arg->getValue()); + + // Handle /implib + if (auto *arg = args.getLastArg(OPT_implib)) + config->implib = arg->getValue(); + + // Handle /opt. + bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); + unsigned icfLevel = + args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on + unsigned tailMerge = 1; + for (auto *arg : args.filtered(OPT_opt)) { + std::string str = StringRef(arg->getValue()).lower(); + SmallVector vec; + StringRef(str).split(vec, ','); + for (StringRef s : vec) { + if (s == "ref") { + doGC = true; + } else if (s == "noref") { + doGC = false; + } else if (s == "icf" || s.startswith("icf=")) { + icfLevel = 2; + } else if (s == "noicf") { + icfLevel = 0; + } else if (s == "lldtailmerge") { + tailMerge = 2; + } else if (s == "nolldtailmerge") { + tailMerge = 0; + } else if (s.startswith("lldlto=")) { + StringRef optLevel = s.substr(7); + if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3) + error("/opt:lldlto: invalid optimization level: " + optLevel); + } else if (s.startswith("lldltojobs=")) { + StringRef jobs = s.substr(11); + if (jobs.getAsInteger(10, config->thinLTOJobs) || + config->thinLTOJobs == 0) + error("/opt:lldltojobs: invalid job count: " + jobs); + } else if (s.startswith("lldltopartitions=")) { + StringRef n = s.substr(17); + if (n.getAsInteger(10, config->ltoPartitions) || + config->ltoPartitions == 0) + error("/opt:lldltopartitions: invalid partition count: " + n); + } else if (s != "lbr" && s != "nolbr") + error("/opt: unknown option: " + s); + } + } + + // Limited ICF is enabled if GC is enabled and ICF was never mentioned + // explicitly. + // FIXME: LLD only implements "limited" ICF, i.e. it only merges identical + // code. If the user passes /OPT:ICF explicitly, LLD should merge identical + // comdat readonly data. + if (icfLevel == 1 && !doGC) + icfLevel = 0; + config->doGC = doGC; + config->doICF = icfLevel > 0; + config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2; + + // Handle /lldsavetemps + if (args.hasArg(OPT_lldsavetemps)) + config->saveTemps = true; + + // Handle /kill-at + if (args.hasArg(OPT_kill_at)) + config->killAt = true; + + // Handle /lldltocache + if (auto *arg = args.getLastArg(OPT_lldltocache)) + config->ltoCache = arg->getValue(); + + // Handle /lldsavecachepolicy + if (auto *arg = args.getLastArg(OPT_lldltocachepolicy)) + config->ltoCachePolicy = CHECK( + parseCachePruningPolicy(arg->getValue()), + Twine("/lldltocachepolicy: invalid cache policy: ") + arg->getValue()); + + // Handle /failifmismatch + for (auto *arg : args.filtered(OPT_failifmismatch)) + checkFailIfMismatch(arg->getValue(), nullptr); + + // Handle /merge + for (auto *arg : args.filtered(OPT_merge)) + parseMerge(arg->getValue()); + + // Add default section merging rules after user rules. User rules take + // precedence, but we will emit a warning if there is a conflict. + parseMerge(".idata=.rdata"); + parseMerge(".didat=.rdata"); + parseMerge(".edata=.rdata"); + parseMerge(".xdata=.rdata"); + parseMerge(".bss=.data"); + + if (config->mingw) { + parseMerge(".ctors=.rdata"); + parseMerge(".dtors=.rdata"); + parseMerge(".CRT=.rdata"); + } + + // Handle /section + for (auto *arg : args.filtered(OPT_section)) + parseSection(arg->getValue()); + + // Handle /align + if (auto *arg = args.getLastArg(OPT_align)) { + parseNumbers(arg->getValue(), &config->align); + if (!isPowerOf2_64(config->align)) + error("/align: not a power of two: " + StringRef(arg->getValue())); + } + + // Handle /aligncomm + for (auto *arg : args.filtered(OPT_aligncomm)) + parseAligncomm(arg->getValue()); + + // Handle /manifestdependency. This enables /manifest unless /manifest:no is + // also passed. + if (auto *arg = args.getLastArg(OPT_manifestdependency)) { + config->manifestDependency = arg->getValue(); + config->manifest = Configuration::SideBySide; + } + + // Handle /manifest and /manifest: + if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) { + if (arg->getOption().getID() == OPT_manifest) + config->manifest = Configuration::SideBySide; + else + parseManifest(arg->getValue()); + } + + // Handle /manifestuac + if (auto *arg = args.getLastArg(OPT_manifestuac)) + parseManifestUAC(arg->getValue()); + + // Handle /manifestfile + if (auto *arg = args.getLastArg(OPT_manifestfile)) + config->manifestFile = arg->getValue(); + + // Handle /manifestinput + for (auto *arg : args.filtered(OPT_manifestinput)) + config->manifestInput.push_back(arg->getValue()); + + if (!config->manifestInput.empty() && + config->manifest != Configuration::Embed) { + fatal("/manifestinput: requires /manifest:embed"); + } + + config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); + config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_arg); + config->thinLTOIndexOnlyArg = + args.getLastArgValue(OPT_thinlto_index_only_arg); + config->thinLTOPrefixReplace = + getOldNewOptions(args, OPT_thinlto_prefix_replace); + config->thinLTOObjectSuffixReplace = + getOldNewOptions(args, OPT_thinlto_object_suffix_replace); + // Handle miscellaneous boolean flags. + config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); + config->allowIsolation = + args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); + config->incremental = + args.hasFlag(OPT_incremental, OPT_incremental_no, + !config->doGC && !config->doICF && !args.hasArg(OPT_order) && + !args.hasArg(OPT_profile)); + config->integrityCheck = + args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); + config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); + for (auto *arg : args.filtered(OPT_swaprun)) + parseSwaprun(arg->getValue()); + config->terminalServerAware = + !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); + config->debugDwarf = debug == DebugKind::Dwarf; + config->debugGHashes = debug == DebugKind::GHash; + config->debugSymtab = debug == DebugKind::Symtab; + + config->mapFile = getMapFile(args); + + if (config->incremental && args.hasArg(OPT_profile)) { + warn("ignoring '/incremental' due to '/profile' specification"); + config->incremental = false; + } + + if (config->incremental && args.hasArg(OPT_order)) { + warn("ignoring '/incremental' due to '/order' specification"); + config->incremental = false; + } + + if (config->incremental && config->doGC) { + warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to " + "disable"); + config->incremental = false; + } + + if (config->incremental && config->doICF) { + warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to " + "disable"); + config->incremental = false; + } + + if (errorCount()) + return; + + std::set wholeArchives; + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional path = doFindFile(arg->getValue())) + if (Optional id = getUniqueID(*path)) + wholeArchives.insert(*id); + + // A predicate returning true if a given path is an argument for + // /wholearchive:, or /wholearchive is enabled globally. + // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" + // needs to be handled as "/wholearchive:foo.obj foo.obj". + auto isWholeArchive = [&](StringRef path) -> bool { + if (args.hasArg(OPT_wholearchive_flag)) + return true; + if (Optional id = getUniqueID(path)) + return wholeArchives.count(*id); + return false; + }; + + // Create a list of input files. Files can be given as arguments + // for /defaultlib option. + for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path)); + + for (auto *arg : args.filtered(OPT_defaultlib)) + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false); + + // Windows specific -- Create a resource file containing a manifest file. + if (config->manifest == Configuration::Embed) + addBuffer(createManifestRes(), false); + + // Read all input files given via the command line. + run(); + + if (errorCount()) + return; + + // We should have inferred a machine type by now from the input files, but if + // not we assume x64. + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { + warn("/machine is not specified. x64 is assumed"); + config->machine = AMD64; + } + config->wordsize = config->is64() ? 8 : 4; + + // Handle /safeseh, x86 only, on by default, except for mingw. + if (config->machine == I386 && + args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw)) + config->safeSEH = true; + + // Handle /functionpadmin + for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) + parseFunctionPadMin(arg, config->machine); + + // Input files can be Windows resource files (.res files). We use + // WindowsResource to convert resource files to a regular COFF file, + // then link the resulting file normally. + if (!resources.empty()) + symtab->addFile(make(convertResToCOFF(resources))); + + if (tar) + tar->append("response.txt", + createResponseFile(args, filePaths, + ArrayRef(searchPaths).slice(1))); + + // Handle /largeaddressaware + config->largeAddressAware = args.hasFlag( + OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64()); + + // Handle /highentropyva + config->highEntropyVA = + config->is64() && + args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); + + if (!config->dynamicBase && + (config->machine == ARMNT || config->machine == ARM64)) + error("/dynamicbase:no is not compatible with " + + machineToStr(config->machine)); + + // Handle /export + for (auto *arg : args.filtered(OPT_export)) { + Export e = parseExport(arg->getValue()); + if (config->machine == I386) { + if (!isDecorated(e.name)) + e.name = saver.save("_" + e.name); + if (!e.extName.empty() && !isDecorated(e.extName)) + e.extName = saver.save("_" + e.extName); + } + config->exports.push_back(e); + } + + // Handle /def + if (auto *arg = args.getLastArg(OPT_deffile)) { + // parseModuleDefs mutates Config object. + parseModuleDefs(arg->getValue()); + } + + // Handle generation of import library from a def file. + if (!args.hasArg(OPT_INPUT)) { + fixupExports(); + createImportLibrary(/*asLib=*/true); + return; + } + + // Windows specific -- if no /subsystem is given, we need to infer + // that from entry point name. Must happen before /entry handling, + // and after the early return when just writing an import library. + if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { + config->subsystem = inferSubsystem(); + if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) + fatal("subsystem must be defined"); + } + + // Handle /entry and /dll + if (auto *arg = args.getLastArg(OPT_entry)) { + config->entry = addUndefined(mangle(arg->getValue())); + } else if (!config->entry && !config->noEntry) { + if (args.hasArg(OPT_dll)) { + StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12" + : "_DllMainCRTStartup"; + config->entry = addUndefined(s); + } else { + // Windows specific -- If entry point name is not given, we need to + // infer that from user-defined entry name. + StringRef s = findDefaultEntry(); + if (s.empty()) + fatal("entry point must be defined"); + config->entry = addUndefined(s); + log("Entry name inferred: " + s); + } + } + + // Handle /delayload + for (auto *arg : args.filtered(OPT_delayload)) { + config->delayLoads.insert(StringRef(arg->getValue()).lower()); + if (config->machine == I386) { + config->delayLoadHelper = addUndefined("___delayLoadHelper2@8"); + } else { + config->delayLoadHelper = addUndefined("__delayLoadHelper2"); + } + } + + // Set default image name if neither /out or /def set it. + if (config->outputFile.empty()) { + config->outputFile = + getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue()); + } + + // Fail early if an output file is not writable. + if (auto e = tryCreateFile(config->outputFile)) { + error("cannot open output file " + config->outputFile + ": " + e.message()); + return; + } + + if (shouldCreatePDB) { + // Put the PDB next to the image if no /pdb flag was passed. + if (config->pdbPath.empty()) { + config->pdbPath = config->outputFile; + sys::path::replace_extension(config->pdbPath, ".pdb"); + } + + // The embedded PDB path should be the absolute path to the PDB if no + // /pdbaltpath flag was passed. + if (config->pdbAltPath.empty()) { + config->pdbAltPath = config->pdbPath; + + // It's important to make the path absolute and remove dots. This path + // will eventually be written into the PE header, and certain Microsoft + // tools won't work correctly if these assumptions are not held. + sys::fs::make_absolute(config->pdbAltPath); + sys::path::remove_dots(config->pdbAltPath); + } else { + // Don't do this earlier, so that Config->OutputFile is ready. + parsePDBAltPath(config->pdbAltPath); + } + } + + // Set default image base if /base is not given. + if (config->imageBase == uint64_t(-1)) + config->imageBase = getDefaultImageBase(); + + symtab->addSynthetic(mangle("__ImageBase"), nullptr); + if (config->machine == I386) { + symtab->addAbsolute("___safe_se_handler_table", 0); + symtab->addAbsolute("___safe_se_handler_count", 0); + } + + symtab->addAbsolute(mangle("__guard_fids_count"), 0); + symtab->addAbsolute(mangle("__guard_fids_table"), 0); + symtab->addAbsolute(mangle("__guard_flags"), 0); + symtab->addAbsolute(mangle("__guard_iat_count"), 0); + symtab->addAbsolute(mangle("__guard_iat_table"), 0); + symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); + symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); + // Needed for MSVC 2017 15.5 CRT. + symtab->addAbsolute(mangle("__enclave_config"), 0); + + if (config->mingw) { + symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); + symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); + symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); + symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); + } + + // This code may add new undefined symbols to the link, which may enqueue more + // symbol resolution tasks, so we need to continue executing tasks until we + // converge. + do { + // Windows specific -- if entry point is not found, + // search for its mangled names. + if (config->entry) + mangleMaybe(config->entry); + + // Windows specific -- Make sure we resolve all dllexported symbols. + for (Export &e : config->exports) { + if (!e.forwardTo.empty()) + continue; + e.sym = addUndefined(e.name); + if (!e.directives) + e.symbolName = mangleMaybe(e.sym); + } + + // Add weak aliases. Weak aliases is a mechanism to give remaining + // undefined symbols final chance to be resolved successfully. + for (auto pair : config->alternateNames) { + StringRef from = pair.first; + StringRef to = pair.second; + Symbol *sym = symtab->find(from); + if (!sym) + continue; + if (auto *u = dyn_cast(sym)) + if (!u->weakAlias) + u->weakAlias = symtab->addUndefined(to); + } + + // If any inputs are bitcode files, the LTO code generator may create + // references to library functions that are not explicit in the bitcode + // file's symbol table. If any of those library functions are defined in a + // bitcode file in an archive member, we need to arrange to use LTO to + // compile those archive members by adding them to the link beforehand. + if (!BitcodeFile::instances.empty()) + for (const char *s : libcallRoutineNames) + symtab->addLibcall(s); + + // Windows specific -- if __load_config_used can be resolved, resolve it. + if (symtab->findUnderscore("_load_config_used")) + addUndefined(mangle("_load_config_used")); + } while (run()); + + if (errorCount()) + return; + + // Do LTO by compiling bitcode input files to a set of native COFF files then + // link those files (unless -thinlto-index-only was given, in which case we + // resolve symbols and write indices, but don't generate native code or link). + symtab->addCombinedLTOObjects(); + + // If -thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in addCombinedLTOObject, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return; + + // If we generated native object files from bitcode files, this resolves + // references to the symbols we use from them. + run(); + + if (args.hasArg(OPT_include_optional)) { + // Handle /includeoptional + for (auto *arg : args.filtered(OPT_include_optional)) + if (dyn_cast_or_null(symtab->find(arg->getValue()))) + addUndefined(arg->getValue()); + while (run()); + } + + if (config->mingw) { + // Load any further object files that might be needed for doing automatic + // imports. + // + // For cases with no automatically imported symbols, this iterates once + // over the symbol table and doesn't do anything. + // + // For the normal case with a few automatically imported symbols, this + // should only need to be run once, since each new object file imported + // is an import library and wouldn't add any new undefined references, + // but there's nothing stopping the __imp_ symbols from coming from a + // normal object file as well (although that won't be used for the + // actual autoimport later on). If this pass adds new undefined references, + // we won't iterate further to resolve them. + symtab->loadMinGWAutomaticImports(); + run(); + } + + // Make sure we have resolved all symbols. + symtab->reportRemainingUndefines(); + if (errorCount()) + return; + + if (config->mingw) { + // In MinGW, all symbols are automatically exported if no symbols + // are chosen to be exported. + maybeExportMinGWSymbols(args); + + // Make sure the crtend.o object is the last object file. This object + // file can contain terminating section chunks that need to be placed + // last. GNU ld processes files and static libraries explicitly in the + // order provided on the command line, while lld will pull in needed + // files from static libraries only after the last object file on the + // command line. + for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end(); + i != e; i++) { + ObjFile *file = *i; + if (isCrtend(file->getName())) { + ObjFile::instances.erase(i); + ObjFile::instances.push_back(file); + break; + } + } + } + + // Windows specific -- when we are creating a .dll file, we also + // need to create a .lib file. + if (!config->exports.empty() || config->dll) { + fixupExports(); + createImportLibrary(/*asLib=*/false); + assignExportOrdinals(); + } + + // Handle /output-def (MinGW specific). + if (auto *arg = args.getLastArg(OPT_output_def)) + writeDefFile(arg->getValue()); + + // Set extra alignment for .comm symbols + for (auto pair : config->alignComm) { + StringRef name = pair.first; + uint32_t alignment = pair.second; + + Symbol *sym = symtab->find(name); + if (!sym) { + warn("/aligncomm symbol " + name + " not found"); + continue; + } + + // If the symbol isn't common, it must have been replaced with a regular + // symbol, which will carry its own alignment. + auto *dc = dyn_cast(sym); + if (!dc) + continue; + + CommonChunk *c = dc->getChunk(); + c->setAlignment(std::max(c->getAlignment(), alignment)); + } + + // Windows specific -- Create a side-by-side manifest file. + if (config->manifest == Configuration::SideBySide) + createSideBySideManifest(); + + // Handle /order. We want to do this at this moment because we + // need a complete list of comdat sections to warn on nonexistent + // functions. + if (auto *arg = args.getLastArg(OPT_order)) + parseOrderFile(arg->getValue()); + + // Identify unreferenced COMDAT sections. + if (config->doGC) + markLive(symtab->getChunks()); + + // Needs to happen after the last call to addFile(). + diagnoseMultipleResourceObjFiles(); + + // Identify identical COMDAT sections to merge them. + if (config->doICF) { + findKeepUniqueSections(); + doICF(symtab->getChunks()); + } + + // Write the result. + writeResult(); + + // Stop early so we can print the results. + Timer::root().stop(); + if (config->showTiming) + Timer::root().print(); +} + +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/Driver.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Driver.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Driver.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Driver.h (revision 352529) @@ -0,0 +1,202 @@ +//===- Driver.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_COFF_DRIVER_H +#define LLD_COFF_DRIVER_H + +#include "Config.h" +#include "SymbolTable.h" +#include "lld/Common/LLVM.h" +#include "lld/Common/Reproduce.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFF.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/TarWriter.h" +#include +#include +#include + +namespace lld { +namespace coff { + +class LinkerDriver; +extern LinkerDriver *driver; + +using llvm::COFF::MachineTypes; +using llvm::COFF::WindowsSubsystem; +using llvm::Optional; + +class COFFOptTable : public llvm::opt::OptTable { +public: + COFFOptTable(); +}; + +class ArgParser { +public: + // Concatenate LINK environment variable and given arguments and parse them. + llvm::opt::InputArgList parseLINK(std::vector args); + + // Tokenizes a given string and then parses as command line options. + llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); } + + // Tokenizes a given string and then parses as command line options in + // .drectve section. /EXPORT options are returned in second element + // to be processed in fastpath. + std::pair> + parseDirectives(StringRef s); + +private: + // Parses command line options. + llvm::opt::InputArgList parse(llvm::ArrayRef args); + + std::vector tokenize(StringRef s); + + COFFOptTable table; +}; + +class LinkerDriver { +public: + void link(llvm::ArrayRef args); + + // Used by the resolver to parse .drectve section contents. + void parseDirectives(InputFile *file); + + // Used by ArchiveFile to enqueue members. + void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym, + StringRef parentName); + + MemoryBufferRef takeBuffer(std::unique_ptr mb); + + void enqueuePath(StringRef path, bool wholeArchive); + +private: + std::unique_ptr tar; // for /linkrepro + + // Opens a file. Path has to be resolved already. + MemoryBufferRef openFile(StringRef path); + + // Searches a file from search paths. + Optional findFile(StringRef filename); + Optional findLib(StringRef filename); + StringRef doFindFile(StringRef filename); + StringRef doFindLib(StringRef filename); + StringRef doFindLibMinGW(StringRef filename); + + // Parses LIB environment which contains a list of search paths. + void addLibSearchPaths(); + + // Library search path. The first element is always "" (current directory). + std::vector searchPaths; + + void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args); + + // We don't want to add the same file more than once. + // Files are uniquified by their filesystem and file number. + std::set visitedFiles; + + std::set visitedLibs; + + Symbol *addUndefined(StringRef sym); + + StringRef mangleMaybe(Symbol *s); + + // Windows specific -- "main" is not the only main function in Windows. + // You can choose one from these four -- {w,}{WinMain,main}. + // There are four different entry point functions for them, + // {w,}{WinMain,main}CRTStartup, respectively. The linker needs to + // choose the right one depending on which "main" function is defined. + // This function looks up the symbol table and resolve corresponding + // entry point name. + StringRef findDefaultEntry(); + WindowsSubsystem inferSubsystem(); + + void addBuffer(std::unique_ptr mb, bool wholeArchive); + void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, + StringRef parentName, uint64_t offsetInArchive); + + void enqueueTask(std::function task); + bool run(); + + std::list> taskQueue; + std::vector filePaths; + std::vector resources; + + llvm::StringSet<> directivesExports; +}; + +// Functions below this line are defined in DriverUtils.cpp. + +void printHelp(const char *argv0); + +// Parses a string in the form of "[,]". +void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); + +void parseGuard(StringRef arg); + +// Parses a string in the form of "[.]". +// Minor's default value is 0. +void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); + +// Parses a string in the form of "[,[.]]". +void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor); + +void parseAlternateName(StringRef); +void parseMerge(StringRef); +void parseSection(StringRef); +void parseAligncomm(StringRef); + +// Parses a string in the form of "[:]" +void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + +// Parses a string in the form of "EMBED[,=]|NO". +void parseManifest(StringRef arg); + +// Parses a string in the form of "level=|uiAccess=" +void parseManifestUAC(StringRef arg); + +// Parses a string in the form of "cd|net[,(cd|net)]*" +void parseSwaprun(StringRef arg); + +// Create a resource file containing a manifest XML. +std::unique_ptr createManifestRes(); +void createSideBySideManifest(); + +// Used for dllexported symbols. +Export parseExport(StringRef arg); +void fixupExports(); +void assignExportOrdinals(); + +// Parses a string in the form of "key=value" and check +// if value matches previous values for the key. +// This feature used in the directive section to reject +// incompatible objects. +void checkFailIfMismatch(StringRef arg, InputFile *source); + +// Convert Windows resource files (.res files) to a .obj file. +MemoryBufferRef convertResToCOFF(ArrayRef mbs); + +void runMSVCLinker(std::string rsp, ArrayRef objects); + +// Create enum with OPT_xxx values for each option in Options.td +enum { + OPT_INVALID = 0, +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/Driver.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/InputFiles.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/InputFiles.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/InputFiles.cpp (revision 352529) @@ -0,0 +1,881 @@ +//===- InputFiles.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Chunks.h" +#include "Config.h" +#include "DebugTypes.h" +#include "Driver.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm-c/lto.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Target/TargetOptions.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::COFF; +using namespace llvm::codeview; +using namespace llvm::object; +using namespace llvm::support::endian; + +using llvm::Triple; +using llvm::support::ulittle32_t; + +namespace lld { +namespace coff { + +std::vector ObjFile::instances; +std::vector ImportFile::instances; +std::vector BitcodeFile::instances; + +/// Checks that Source is compatible with being a weak alias to Target. +/// If Source is Undefined and has no weak alias set, makes it a weak +/// alias to Target. +static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, + Symbol *source, Symbol *target) { + if (auto *u = dyn_cast(source)) { + if (u->weakAlias && u->weakAlias != target) { + // Weak aliases as produced by GCC are named in the form + // .weak.., where is the name + // of another symbol emitted near the weak symbol. + // Just use the definition from the first object file that defined + // this weak symbol. + if (config->mingw) + return; + symtab->reportDuplicate(source, f); + } + u->weakAlias = target; + } +} + +ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} + +void ArchiveFile::parse() { + // Parse a MemoryBufferRef as an archive file. + file = CHECK(Archive::create(mb), this); + + // Read the symbol table to construct Lazy objects. + for (const Archive::Symbol &sym : file->symbols()) + symtab->addLazy(this, sym); +} + +// Returns a buffer pointing to a member file containing a given symbol. +void ArchiveFile::addMember(const Archive::Symbol &sym) { + const Archive::Child &c = + CHECK(sym.getMember(), + "could not get the member for symbol " + toCOFFString(sym)); + + // Return an empty buffer if we have already returned the same buffer. + if (!seen.insert(c.getChildOffset()).second) + return; + + driver->enqueueArchiveMember(c, sym, getName()); +} + +std::vector getArchiveMembers(Archive *file) { + std::vector v; + Error err = Error::success(); + for (const ErrorOr &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, + file->getFileName() + ": could not get the child of the archive"); + MemoryBufferRef mbref = + CHECK(c.getMemoryBufferRef(), + file->getFileName() + + ": could not get the buffer for a child of the archive"); + v.push_back(mbref); + } + if (err) + fatal(file->getFileName() + + ": Archive::children failed: " + toString(std::move(err))); + return v; +} + +void ObjFile::parse() { + // Parse a memory buffer as a COFF file. + std::unique_ptr bin = CHECK(createBinary(mb), this); + + if (auto *obj = dyn_cast(bin.get())) { + bin.release(); + coffObj.reset(obj); + } else { + fatal(toString(this) + " is not a COFF file"); + } + + // Read section and symbol tables. + initializeChunks(); + initializeSymbols(); + initializeFlags(); + initializeDependencies(); +} + +const coff_section* ObjFile::getSection(uint32_t i) { + const coff_section *sec; + if (auto ec = coffObj->getSection(i, sec)) + fatal("getSection failed: #" + Twine(i) + ": " + ec.message()); + return sec; +} + +// We set SectionChunk pointers in the SparseChunks vector to this value +// temporarily to mark comdat sections as having an unknown resolution. As we +// walk the object file's symbol table, once we visit either a leader symbol or +// an associative section definition together with the parent comdat's leader, +// we set the pointer to either nullptr (to mark the section as discarded) or a +// valid SectionChunk for that section. +static SectionChunk *const pendingComdat = reinterpret_cast(1); + +void ObjFile::initializeChunks() { + uint32_t numSections = coffObj->getNumberOfSections(); + chunks.reserve(numSections); + sparseChunks.resize(numSections + 1); + for (uint32_t i = 1; i < numSections + 1; ++i) { + const coff_section *sec = getSection(i); + if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT) + sparseChunks[i] = pendingComdat; + else + sparseChunks[i] = readSection(i, nullptr, ""); + } +} + +SectionChunk *ObjFile::readSection(uint32_t sectionNumber, + const coff_aux_section_definition *def, + StringRef leaderName) { + const coff_section *sec = getSection(sectionNumber); + + StringRef name; + if (Expected e = coffObj->getSectionName(sec)) + name = *e; + else + fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " + + toString(e.takeError())); + + if (name == ".drectve") { + ArrayRef data; + cantFail(coffObj->getSectionContents(sec, data)); + directives = StringRef((const char *)data.data(), data.size()); + return nullptr; + } + + if (name == ".llvm_addrsig") { + addrsigSec = sec; + return nullptr; + } + + // Object files may have DWARF debug info or MS CodeView debug info + // (or both). + // + // DWARF sections don't need any special handling from the perspective + // of the linker; they are just a data section containing relocations. + // We can just link them to complete debug info. + // + // CodeView needs linker support. We need to interpret debug info, + // and then write it to a separate .pdb file. + + // Ignore DWARF debug info unless /debug is given. + if (!config->debug && name.startswith(".debug_")) + return nullptr; + + if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) + return nullptr; + auto *c = make(this, sec); + if (def) + c->checksum = def->CheckSum; + + // link.exe uses the presence of .rsrc$01 for LNK4078, so match that. + if (name == ".rsrc$01") + isResourceObjFile = true; + + // CodeView sections are stored to a different vector because they are not + // linked in the regular manner. + if (c->isCodeView()) + debugChunks.push_back(c); + else if (name == ".gfids$y") + guardFidChunks.push_back(c); + else if (name == ".gljmp$y") + guardLJmpChunks.push_back(c); + else if (name == ".sxdata") + sXDataChunks.push_back(c); + else if (config->tailMerge && sec->NumberOfRelocations == 0 && + name == ".rdata" && leaderName.startswith("??_C@")) + // COFF sections that look like string literal sections (i.e. no + // relocations, in .rdata, leader symbol name matches the MSVC name mangling + // for string literals) are subject to string tail merging. + MergeChunk::addSection(c); + else + chunks.push_back(c); + + return c; +} + +void ObjFile::readAssociativeDefinition( + COFFSymbolRef sym, const coff_aux_section_definition *def) { + readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); +} + +void ObjFile::readAssociativeDefinition(COFFSymbolRef sym, + const coff_aux_section_definition *def, + uint32_t parentIndex) { + SectionChunk *parent = sparseChunks[parentIndex]; + int32_t sectionNumber = sym.getSectionNumber(); + + auto diag = [&]() { + StringRef name, parentName; + coffObj->getSymbolName(sym, name); + + const coff_section *parentSec = getSection(parentIndex); + if (Expected e = coffObj->getSectionName(parentSec)) + parentName = *e; + error(toString(this) + ": associative comdat " + name + " (sec " + + Twine(sectionNumber) + ") has invalid reference to section " + + parentName + " (sec " + Twine(parentIndex) + ")"); + }; + + if (parent == pendingComdat) { + // This can happen if an associative comdat refers to another associative + // comdat that appears after it (invalid per COFF spec) or to a section + // without any symbols. + diag(); + return; + } + + // Check whether the parent is prevailing. If it is, so are we, and we read + // the section; otherwise mark it as discarded. + if (parent) { + SectionChunk *c = readSection(sectionNumber, def, ""); + sparseChunks[sectionNumber] = c; + if (c) { + c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE; + parent->addAssociative(c); + } + } else { + sparseChunks[sectionNumber] = nullptr; + } +} + +void ObjFile::recordPrevailingSymbolForMingw( + COFFSymbolRef sym, DenseMap &prevailingSectionMap) { + // For comdat symbols in executable sections, where this is the copy + // of the section chunk we actually include instead of discarding it, + // add the symbol to a map to allow using it for implicitly + // associating .[px]data$ sections to it. + int32_t sectionNumber = sym.getSectionNumber(); + SectionChunk *sc = sparseChunks[sectionNumber]; + if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (getMachineType() == I386) + name.consume_front("_"); + prevailingSectionMap[name] = sectionNumber; + } +} + +void ObjFile::maybeAssociateSEHForMingw( + COFFSymbolRef sym, const coff_aux_section_definition *def, + const DenseMap &prevailingSectionMap) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (name.consume_front(".pdata$") || name.consume_front(".xdata$") || + name.consume_front(".eh_frame$")) { + // For MinGW, treat .[px]data$ and .eh_frame$ as implicitly + // associative to the symbol . + auto parentSym = prevailingSectionMap.find(name); + if (parentSym != prevailingSectionMap.end()) + readAssociativeDefinition(sym, def, parentSym->second); + } +} + +Symbol *ObjFile::createRegular(COFFSymbolRef sym) { + SectionChunk *sc = sparseChunks[sym.getSectionNumber()]; + if (sym.isExternal()) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (sc) + return symtab->addRegular(this, name, sym.getGeneric(), sc); + // For MinGW symbols named .weak.* that point to a discarded section, + // don't create an Undefined symbol. If nothing ever refers to the symbol, + // everything should be fine. If something actually refers to the symbol + // (e.g. the undefined weak alias), linking will fail due to undefined + // references at the end. + if (config->mingw && name.startswith(".weak.")) + return nullptr; + return symtab->addUndefined(name, this, false); + } + if (sc) + return make(this, /*Name*/ "", /*IsCOMDAT*/ false, + /*IsExternal*/ false, sym.getGeneric(), sc); + return nullptr; +} + +void ObjFile::initializeSymbols() { + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + symbols.resize(numSymbols); + + SmallVector, 8> weakAliases; + std::vector pendingIndexes; + pendingIndexes.reserve(numSymbols); + + DenseMap prevailingSectionMap; + std::vector comdatDefs( + coffObj->getNumberOfSections() + 1); + + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + bool prevailingComdat; + if (coffSym.isUndefined()) { + symbols[i] = createUndefined(coffSym); + } else if (coffSym.isWeakExternal()) { + symbols[i] = createUndefined(coffSym); + uint32_t tagIndex = coffSym.getAux()->TagIndex; + weakAliases.emplace_back(symbols[i], tagIndex); + } else if (Optional optSym = + createDefined(coffSym, comdatDefs, prevailingComdat)) { + symbols[i] = *optSym; + if (config->mingw && prevailingComdat) + recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap); + } else { + // createDefined() returns None if a symbol belongs to a section that + // was pending at the point when the symbol was read. This can happen in + // two cases: + // 1) section definition symbol for a comdat leader; + // 2) symbol belongs to a comdat section associated with another section. + // In both of these cases, we can expect the section to be resolved by + // the time we finish visiting the remaining symbols in the symbol + // table. So we postpone the handling of this symbol until that time. + pendingIndexes.push_back(i); + } + i += coffSym.getNumberOfAuxSymbols(); + } + + for (uint32_t i : pendingIndexes) { + COFFSymbolRef sym = check(coffObj->getSymbol(i)); + if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { + if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) + readAssociativeDefinition(sym, def); + else if (config->mingw) + maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); + } + if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { + StringRef name; + coffObj->getSymbolName(sym, name); + log("comdat section " + name + + " without leader and unassociated, discarding"); + continue; + } + symbols[i] = createRegular(sym); + } + + for (auto &kv : weakAliases) { + Symbol *sym = kv.first; + uint32_t idx = kv.second; + checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); + } +} + +Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { + StringRef name; + coffObj->getSymbolName(sym, name); + return symtab->addUndefined(name, this, sym.isWeakExternal()); +} + +void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, + bool &prevailing, DefinedRegular *leader) { + if (prevailing) + return; + // There's already an existing comdat for this symbol: `Leader`. + // Use the comdats's selection field to determine if the new + // symbol in `Sym` should be discarded, produce a duplicate symbol + // error, etc. + + SectionChunk *leaderChunk = nullptr; + COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY; + + if (leader->data) { + leaderChunk = leader->getChunk(); + leaderSelection = leaderChunk->selection; + } else { + // FIXME: comdats from LTO files don't know their selection; treat them + // as "any". + selection = leaderSelection; + } + + if ((selection == IMAGE_COMDAT_SELECT_ANY && + leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) || + (selection == IMAGE_COMDAT_SELECT_LARGEST && + leaderSelection == IMAGE_COMDAT_SELECT_ANY)) { + // cl.exe picks "any" for vftables when building with /GR- and + // "largest" when building with /GR. To be able to link object files + // compiled with each flag, "any" and "largest" are merged as "largest". + leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST; + } + + // Other than that, comdat selections must match. This is a bit more + // strict than link.exe which allows merging "any" and "largest" if "any" + // is the first symbol the linker sees, and it allows merging "largest" + // with everything (!) if "largest" is the first symbol the linker sees. + // Making this symmetric independent of which selection is seen first + // seems better though. + // (This behavior matches ModuleLinker::getComdatResult().) + if (selection != leaderSelection) { + log(("conflicting comdat type for " + toString(*leader) + ": " + + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + + " and " + Twine((int)selection) + " in " + toString(this)) + .str()); + symtab->reportDuplicate(leader, this); + return; + } + + switch (selection) { + case IMAGE_COMDAT_SELECT_NODUPLICATES: + symtab->reportDuplicate(leader, this); + break; + + case IMAGE_COMDAT_SELECT_ANY: + // Nothing to do. + break; + + case IMAGE_COMDAT_SELECT_SAME_SIZE: + if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) + symtab->reportDuplicate(leader, this); + break; + + case IMAGE_COMDAT_SELECT_EXACT_MATCH: { + SectionChunk newChunk(this, getSection(sym)); + // link.exe only compares section contents here and doesn't complain + // if the two comdat sections have e.g. different alignment. + // Match that. + if (leaderChunk->getContents() != newChunk.getContents()) + symtab->reportDuplicate(leader, this); + break; + } + + case IMAGE_COMDAT_SELECT_ASSOCIATIVE: + // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE. + // (This means lld-link doesn't produce duplicate symbol errors for + // associative comdats while link.exe does, but associate comdats + // are never extern in practice.) + llvm_unreachable("createDefined not called for associative comdats"); + + case IMAGE_COMDAT_SELECT_LARGEST: + if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) { + // Replace the existing comdat symbol with the new one. + StringRef name; + coffObj->getSymbolName(sym, name); + // FIXME: This is incorrect: With /opt:noref, the previous sections + // make it into the final executable as well. Correct handling would + // be to undo reading of the whole old section that's being replaced, + // or doing one pass that determines what the final largest comdat + // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading + // only the largest one. + replaceSymbol(leader, this, name, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym.getGeneric(), + nullptr); + prevailing = true; + } + break; + + case IMAGE_COMDAT_SELECT_NEWEST: + llvm_unreachable("should have been rejected earlier"); + } +} + +Optional ObjFile::createDefined( + COFFSymbolRef sym, + std::vector &comdatDefs, + bool &prevailing) { + prevailing = false; + auto getName = [&]() { + StringRef s; + coffObj->getSymbolName(sym, s); + return s; + }; + + if (sym.isCommon()) { + auto *c = make(sym); + chunks.push_back(c); + return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(), + c); + } + + if (sym.isAbsolute()) { + StringRef name = getName(); + + // Skip special symbols. + if (name == "@comp.id") + return nullptr; + if (name == "@feat.00") { + feat00Flags = sym.getValue(); + return nullptr; + } + + if (sym.isExternal()) + return symtab->addAbsolute(name, sym); + return make(name, sym); + } + + int32_t sectionNumber = sym.getSectionNumber(); + if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) + return nullptr; + + if (llvm::COFF::isReservedSectionNumber(sectionNumber)) + fatal(toString(this) + ": " + getName() + + " should not refer to special section " + Twine(sectionNumber)); + + if ((uint32_t)sectionNumber >= sparseChunks.size()) + fatal(toString(this) + ": " + getName() + + " should not refer to non-existent section " + Twine(sectionNumber)); + + // Comdat handling. + // A comdat symbol consists of two symbol table entries. + // The first symbol entry has the name of the section (e.g. .text), fixed + // values for the other fields, and one auxilliary record. + // The second symbol entry has the name of the comdat symbol, called the + // "comdat leader". + // When this function is called for the first symbol entry of a comdat, + // it sets comdatDefs and returns None, and when it's called for the second + // symbol entry it reads comdatDefs and then sets it back to nullptr. + + // Handle comdat leader. + if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) { + comdatDefs[sectionNumber] = nullptr; + DefinedRegular *leader; + + if (sym.isExternal()) { + std::tie(leader, prevailing) = + symtab->addComdat(this, getName(), sym.getGeneric()); + } else { + leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, + /*IsExternal*/ false, sym.getGeneric()); + prevailing = true; + } + + if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES || + // Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe + // doesn't understand IMAGE_COMDAT_SELECT_NEWEST either. + def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) { + fatal("unknown comdat type " + std::to_string((int)def->Selection) + + " for " + getName() + " in " + toString(this)); + } + COMDATType selection = (COMDATType)def->Selection; + + if (leader->isCOMDAT) + handleComdatSelection(sym, selection, prevailing, leader); + + if (prevailing) { + SectionChunk *c = readSection(sectionNumber, def, getName()); + sparseChunks[sectionNumber] = c; + c->sym = cast(leader); + c->selection = selection; + cast(leader)->data = &c->repl; + } else { + sparseChunks[sectionNumber] = nullptr; + } + return leader; + } + + // Prepare to handle the comdat leader symbol by setting the section's + // ComdatDefs pointer if we encounter a non-associative comdat. + if (sparseChunks[sectionNumber] == pendingComdat) { + if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { + if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) + comdatDefs[sectionNumber] = def; + } + return None; + } + + return createRegular(sym); +} + +MachineTypes ObjFile::getMachineType() { + if (coffObj) + return static_cast(coffObj->getMachine()); + return IMAGE_FILE_MACHINE_UNKNOWN; +} + +ArrayRef ObjFile::getDebugSection(StringRef secName) { + if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName)) + return sec->consumeDebugMagic(); + return {}; +} + +// OBJ files systematically store critical informations in a .debug$S stream, +// even if the TU was compiled with no debug info. At least two records are +// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the +// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is +// currently used to initialize the hotPatchable member. +void ObjFile::initializeFlags() { + ArrayRef data = getDebugSection(".debug$S"); + if (data.empty()) + return; + + DebugSubsectionArray subsections; + + BinaryStreamReader reader(data, support::little); + ExitOnError exitOnErr; + exitOnErr(reader.readArray(subsections, data.size())); + + for (const DebugSubsectionRecord &ss : subsections) { + if (ss.kind() != DebugSubsectionKind::Symbols) + continue; + + unsigned offset = 0; + + // Only parse the first two records. We are only looking for S_OBJNAME + // and S_COMPILE3, and they usually appear at the beginning of the + // stream. + for (unsigned i = 0; i < 2; ++i) { + Expected sym = readSymbolFromStream(ss.getRecordData(), offset); + if (!sym) { + consumeError(sym.takeError()); + return; + } + if (sym->kind() == SymbolKind::S_COMPILE3) { + auto cs = + cantFail(SymbolDeserializer::deserializeAs(sym.get())); + hotPatchable = + (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None; + } + if (sym->kind() == SymbolKind::S_OBJNAME) { + auto objName = cantFail(SymbolDeserializer::deserializeAs( + sym.get())); + pchSignature = objName.Signature; + } + offset += sym->length(); + } + } +} + +// Depending on the compilation flags, OBJs can refer to external files, +// necessary to merge this OBJ into the final PDB. We currently support two +// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu. +// And PDB type servers, when compiling with /Zi. This function extracts these +// dependencies and makes them available as a TpiSource interface (see +// DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular +// output even with /Yc and /Yu and with /Zi. +void ObjFile::initializeDependencies() { + if (!config->debug) + return; + + bool isPCH = false; + + ArrayRef data = getDebugSection(".debug$P"); + if (!data.empty()) + isPCH = true; + else + data = getDebugSection(".debug$T"); + + if (data.empty()) + return; + + CVTypeArray types; + BinaryStreamReader reader(data, support::little); + cantFail(reader.readArray(types, reader.getLength())); + + CVTypeArray::Iterator firstType = types.begin(); + if (firstType == types.end()) + return; + + debugTypes.emplace(types); + + if (isPCH) { + debugTypesObj = makePrecompSource(this); + return; + } + + if (firstType->kind() == LF_TYPESERVER2) { + TypeServer2Record ts = cantFail( + TypeDeserializer::deserializeAs(firstType->data())); + debugTypesObj = makeUseTypeServerSource(this, &ts); + return; + } + + if (firstType->kind() == LF_PRECOMP) { + PrecompRecord precomp = cantFail( + TypeDeserializer::deserializeAs(firstType->data())); + debugTypesObj = makeUsePrecompSource(this, &precomp); + return; + } + + debugTypesObj = makeTpiSource(this); +} + +StringRef ltrim1(StringRef s, const char *chars) { + if (!s.empty() && strchr(chars, s[0])) + return s.substr(1); + return s; +} + +void ImportFile::parse() { + const char *buf = mb.getBufferStart(); + const auto *hdr = reinterpret_cast(buf); + + // Check if the total size is valid. + if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData) + fatal("broken import library"); + + // Read names and create an __imp_ symbol. + StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); + StringRef impName = saver.save("__imp_" + name); + const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; + dllName = StringRef(nameStart); + StringRef extName; + switch (hdr->getNameType()) { + case IMPORT_ORDINAL: + extName = ""; + break; + case IMPORT_NAME: + extName = name; + break; + case IMPORT_NAME_NOPREFIX: + extName = ltrim1(name, "?@_"); + break; + case IMPORT_NAME_UNDECORATE: + extName = ltrim1(name, "?@_"); + extName = extName.substr(0, extName.find('@')); + break; + } + + this->hdr = hdr; + externalName = extName; + + impSym = symtab->addImportData(impName, this); + // If this was a duplicate, we logged an error but may continue; + // in this case, impSym is nullptr. + if (!impSym) + return; + + if (hdr->getType() == llvm::COFF::IMPORT_CONST) + static_cast(symtab->addImportData(name, this)); + + // If type is function, we need to create a thunk which jump to an + // address pointed by the __imp_ symbol. (This allows you to call + // DLL functions just like regular non-DLL functions.) + if (hdr->getType() == llvm::COFF::IMPORT_CODE) + thunkSym = symtab->addImportThunk( + name, cast_or_null(impSym), hdr->Machine); +} + +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + std::string path = mb.getBufferIdentifier().str(); + if (config->thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + + // ThinLTO assumes that all MemoryBufferRefs given to it have a unique + // name. If two archives define two members with the same name, this + // causes a collision which result in only one of the objects being taken + // into consideration at LTO time (which very likely causes undefined + // symbols later in the link stage). So we append file offset to make + // filename unique. + MemoryBufferRef mbref( + mb.getBuffer(), + saver.save(archiveName + path + + (archiveName.empty() ? "" : utostr(offsetInArchive)))); + + obj = check(lto::InputFile::create(mbref)); +} + +void BitcodeFile::parse() { + std::vector> comdat(obj->getComdatTable().size()); + for (size_t i = 0; i != obj->getComdatTable().size(); ++i) + // FIXME: lto::InputFile doesn't keep enough data to do correct comdat + // selection handling. + comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i])); + for (const lto::InputFile::Symbol &objSym : obj->symbols()) { + StringRef symName = saver.save(objSym.getName()); + int comdatIndex = objSym.getComdatIndex(); + Symbol *sym; + if (objSym.isUndefined()) { + sym = symtab->addUndefined(symName, this, false); + } else if (objSym.isCommon()) { + sym = symtab->addCommon(this, symName, objSym.getCommonSize()); + } else if (objSym.isWeak() && objSym.isIndirect()) { + // Weak external. + sym = symtab->addUndefined(symName, this, true); + std::string fallback = objSym.getCOFFWeakExternalFallback(); + Symbol *alias = symtab->addUndefined(saver.save(fallback)); + checkAndSetWeakAlias(symtab, this, sym, alias); + } else if (comdatIndex != -1) { + if (symName == obj->getComdatTable()[comdatIndex]) + sym = comdat[comdatIndex].first; + else if (comdat[comdatIndex].second) + sym = symtab->addRegular(this, symName); + else + sym = symtab->addUndefined(symName, this, false); + } else { + sym = symtab->addRegular(this, symName); + } + symbols.push_back(sym); + if (objSym.isUsed()) + config->gcroot.push_back(sym); + } + directives = obj->getCOFFLinkerOpts(); +} + +MachineTypes BitcodeFile::getMachineType() { + switch (Triple(obj->getTargetTriple()).getArch()) { + case Triple::x86_64: + return AMD64; + case Triple::x86: + return I386; + case Triple::arm: + return ARMNT; + case Triple::aarch64: + return ARM64; + default: + return IMAGE_FILE_MACHINE_UNKNOWN; + } +} + +std::string replaceThinLTOSuffix(StringRef path) { + StringRef suffix = config->thinLTOObjectSuffixReplace.first; + StringRef repl = config->thinLTOObjectSuffixReplace.second; + + if (path.consume_back(suffix)) + return (path + repl).str(); + return path; +} +} // namespace coff +} // namespace lld + +// Returns the last element of a path, which is supposed to be a filename. +static StringRef getBasename(StringRef path) { + return sys::path::filename(path, sys::path::Style::windows); +} + +// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". +std::string lld::toString(const coff::InputFile *file) { + if (!file) + return ""; + if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) + return file->getName(); + + return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + + ")") + .str(); +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/InputFiles.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/InputFiles.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/InputFiles.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/InputFiles.h (revision 352529) @@ -0,0 +1,321 @@ +//===- InputFiles.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_COFF_INPUT_FILES_H +#define LLD_COFF_INPUT_FILES_H + +#include "Config.h" +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/LTO/LTO.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/StringSaver.h" +#include +#include +#include + +namespace llvm { +namespace pdb { +class DbiModuleDescriptorBuilder; +} +} + +namespace lld { +namespace coff { + +std::vector getArchiveMembers(llvm::object::Archive *file); + +using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; +using llvm::COFF::MachineTypes; +using llvm::object::Archive; +using llvm::object::COFFObjectFile; +using llvm::object::COFFSymbolRef; +using llvm::object::coff_import_header; +using llvm::object::coff_section; + +class Chunk; +class Defined; +class DefinedImportData; +class DefinedImportThunk; +class DefinedRegular; +class Lazy; +class SectionChunk; +class Symbol; +class Undefined; +class TpiSource; + +// The root class of input files. +class InputFile { +public: + enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; + Kind kind() const { return fileKind; } + virtual ~InputFile() {} + + // Returns the filename. + StringRef getName() const { return mb.getBufferIdentifier(); } + + // Reads a file (the constructor doesn't do that). + virtual void parse() = 0; + + // Returns the CPU type this file was compiled to. + virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; } + + MemoryBufferRef mb; + + // An archive file name if this file is created from an archive. + StringRef parentName; + + // Returns .drectve section contents if exist. + StringRef getDirectives() { return directives; } + +protected: + InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {} + + StringRef directives; + +private: + const Kind fileKind; +}; + +// .lib or .a file. +class ArchiveFile : public InputFile { +public: + explicit ArchiveFile(MemoryBufferRef m); + static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } + void parse() override; + + // Enqueues an archive member load for the given symbol. If we've already + // enqueued a load for the same archive member, this function does nothing, + // which ensures that we don't load the same member more than once. + void addMember(const Archive::Symbol &sym); + +private: + std::unique_ptr file; + llvm::DenseSet seen; +}; + +// .obj or .o file. This may be a member of an archive file. +class ObjFile : public InputFile { +public: + explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } + void parse() override; + MachineTypes getMachineType() override; + ArrayRef getChunks() { return chunks; } + ArrayRef getDebugChunks() { return debugChunks; } + ArrayRef getSXDataChunks() { return sXDataChunks; } + ArrayRef getGuardFidChunks() { return guardFidChunks; } + ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } + ArrayRef getSymbols() { return symbols; } + + ArrayRef getDebugSection(StringRef secName); + + // Returns a Symbol object for the symbolIndex'th symbol in the + // underlying object file. + Symbol *getSymbol(uint32_t symbolIndex) { + return symbols[symbolIndex]; + } + + // Returns the underlying COFF file. + COFFObjectFile *getCOFFObj() { return coffObj.get(); } + + // Add a symbol for a range extension thunk. Return the new symbol table + // index. This index can be used to modify a relocation. + uint32_t addRangeThunkSymbol(Symbol *thunk) { + symbols.push_back(thunk); + return symbols.size() - 1; + } + + static std::vector instances; + + // Flags in the absolute @feat.00 symbol if it is present. These usually + // indicate if an object was compiled with certain security features enabled + // like stack guard, safeseh, /guard:cf, or other things. + uint32_t feat00Flags = 0; + + // True if this object file is compatible with SEH. COFF-specific and + // x86-only. COFF spec 5.10.1. The .sxdata section. + bool hasSafeSEH() { return feat00Flags & 0x1; } + + // True if this file was compiled with /guard:cf. + bool hasGuardCF() { return feat00Flags & 0x800; } + + // Pointer to the PDB module descriptor builder. Various debug info records + // will reference object files by "module index", which is here. Things like + // source files and section contributions are also recorded here. Will be null + // if we are not producing a PDB. + llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr; + + const coff_section *addrsigSec = nullptr; + + // When using Microsoft precompiled headers, this is the PCH's key. + // The same key is used by both the precompiled object, and objects using the + // precompiled object. Any difference indicates out-of-date objects. + llvm::Optional pchSignature; + + // Whether this is an object file created from .res files. + bool isResourceObjFile = false; + + // Whether this file was compiled with /hotpatch. + bool hotPatchable = false; + + // Whether the object was already merged into the final PDB. + bool mergedIntoPDB = false; + + // If the OBJ has a .debug$T stream, this tells how it will be handled. + TpiSource *debugTypesObj = nullptr; + + // The .debug$T stream if there's one. + llvm::Optional debugTypes; + +private: + const coff_section* getSection(uint32_t i); + const coff_section *getSection(COFFSymbolRef sym) { + return getSection(sym.getSectionNumber()); + } + + void initializeChunks(); + void initializeSymbols(); + void initializeFlags(); + void initializeDependencies(); + + SectionChunk * + readSection(uint32_t sectionNumber, + const llvm::object::coff_aux_section_definition *def, + StringRef leaderName); + + void readAssociativeDefinition( + COFFSymbolRef coffSym, + const llvm::object::coff_aux_section_definition *def); + + void readAssociativeDefinition( + COFFSymbolRef coffSym, + const llvm::object::coff_aux_section_definition *def, + uint32_t parentSection); + + void recordPrevailingSymbolForMingw( + COFFSymbolRef coffSym, + llvm::DenseMap &prevailingSectionMap); + + void maybeAssociateSEHForMingw( + COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def, + const llvm::DenseMap &prevailingSectionMap); + + // Given a new symbol Sym with comdat selection Selection, if the new + // symbol is not (yet) Prevailing and the existing comdat leader set to + // Leader, emits a diagnostic if the new symbol and its selection doesn't + // match the existing symbol and its selection. If either old or new + // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace + // the existing leader. In that case, Prevailing is set to true. + void handleComdatSelection(COFFSymbolRef sym, + llvm::COFF::COMDATType &selection, + bool &prevailing, DefinedRegular *leader); + + llvm::Optional + createDefined(COFFSymbolRef sym, + std::vector + &comdatDefs, + bool &prevailingComdat); + Symbol *createRegular(COFFSymbolRef sym); + Symbol *createUndefined(COFFSymbolRef sym); + + std::unique_ptr coffObj; + + // List of all chunks defined by this file. This includes both section + // chunks and non-section chunks for common symbols. + std::vector chunks; + + // CodeView debug info sections. + std::vector debugChunks; + + // Chunks containing symbol table indices of exception handlers. Only used for + // 32-bit x86. + std::vector sXDataChunks; + + // Chunks containing symbol table indices of address taken symbols and longjmp + // targets. These are not linked into the final binary when /guard:cf is set. + std::vector guardFidChunks; + std::vector guardLJmpChunks; + + // This vector contains the same chunks as Chunks, but they are + // indexed such that you can get a SectionChunk by section index. + // Nonexistent section indices are filled with null pointers. + // (Because section number is 1-based, the first slot is always a + // null pointer.) + std::vector sparseChunks; + + // This vector contains a list of all symbols defined or referenced by this + // file. They are indexed such that you can get a Symbol by symbol + // index. Nonexistent indices (which are occupied by auxiliary + // symbols in the real symbol table) are filled with null pointers. + std::vector symbols; +}; + +// This type represents import library members that contain DLL names +// and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7 +// for details about the format. +class ImportFile : public InputFile { +public: + explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {} + + static bool classof(const InputFile *f) { return f->kind() == ImportKind; } + + static std::vector instances; + + Symbol *impSym = nullptr; + Symbol *thunkSym = nullptr; + std::string dllName; + +private: + void parse() override; + +public: + StringRef externalName; + const coff_import_header *hdr; + Chunk *location = nullptr; + + // We want to eliminate dllimported symbols if no one actually refers them. + // These "Live" bits are used to keep track of which import library members + // are actually in use. + // + // If the Live bit is turned off by MarkLive, Writer will ignore dllimported + // symbols provided by this import library member. We also track whether the + // imported symbol is used separately from whether the thunk is used in order + // to avoid creating unnecessary thunks. + bool live = !config->doGC; + bool thunkLive = !config->doGC; +}; + +// Used for LTO. +class BitcodeFile : public InputFile { +public: + BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive); + static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + ArrayRef getSymbols() { return symbols; } + MachineTypes getMachineType() override; + static std::vector instances; + std::unique_ptr obj; + +private: + void parse() override; + + std::vector symbols; +}; + +std::string replaceThinLTOSuffix(StringRef path); +} // namespace coff + +std::string toString(const coff::InputFile *file); +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/InputFiles.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/SymbolTable.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/SymbolTable.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/SymbolTable.cpp (revision 352529) @@ -0,0 +1,615 @@ +//===- SymbolTable.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "SymbolTable.h" +#include "Config.h" +#include "Driver.h" +#include "LTO.h" +#include "PDB.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Timer.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Object/WindowsMachineFlag.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +namespace lld { +namespace coff { + +static Timer ltoTimer("LTO", Timer::root()); + +SymbolTable *symtab; + +void SymbolTable::addFile(InputFile *file) { + log("Reading " + toString(file)); + file->parse(); + + MachineTypes mt = file->getMachineType(); + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { + config->machine = mt; + } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { + error(toString(file) + ": machine type " + machineToStr(mt) + + " conflicts with " + machineToStr(config->machine)); + return; + } + + if (auto *f = dyn_cast(file)) { + ObjFile::instances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + BitcodeFile::instances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + ImportFile::instances.push_back(f); + } + + driver->parseDirectives(file); +} + +static void errorOrWarn(const Twine &s) { + if (config->forceUnresolved) + warn(s); + else + error(s); +} + +// Returns the symbol in SC whose value is <= Addr that is closest to Addr. +// This is generally the global variable or function whose definition contains +// Addr. +static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { + DefinedRegular *candidate = nullptr; + + for (Symbol *s : sc->file->getSymbols()) { + auto *d = dyn_cast_or_null(s); + if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr || + (candidate && d->getValue() < candidate->getValue())) + continue; + + candidate = d; + } + + return candidate; +} + +// Given a file and the index of a symbol in that file, returns a description +// of all references to that symbol from that file. If no debug information is +// available, returns just the name of the file, else one string per actual +// reference as described in the debug info. +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { + struct Location { + Symbol *sym; + std::pair fileLine; + }; + std::vector locations; + + for (Chunk *c : file->getChunks()) { + auto *sc = dyn_cast(c); + if (!sc) + continue; + for (const coff_relocation &r : sc->getRelocs()) { + if (r.SymbolTableIndex != symIndex) + continue; + std::pair fileLine = + getFileLine(sc, r.VirtualAddress); + Symbol *sym = getSymbol(sc, r.VirtualAddress); + if (!fileLine.first.empty() || sym) + locations.push_back({sym, fileLine}); + } + } + + if (locations.empty()) + return std::vector({"\n>>> referenced by " + toString(file)}); + + std::vector symbolLocations(locations.size()); + size_t i = 0; + for (Location loc : locations) { + llvm::raw_string_ostream os(symbolLocations[i++]); + os << "\n>>> referenced by "; + if (!loc.fileLine.first.empty()) + os << loc.fileLine.first << ":" << loc.fileLine.second + << "\n>>> "; + os << toString(file); + if (loc.sym) + os << ":(" << toString(*loc.sym) << ')'; + } + return symbolLocations; +} + +// For an undefined symbol, stores all files referencing it and the index of +// the undefined symbol in each file. +struct UndefinedDiag { + Symbol *sym; + struct File { + ObjFile *oFile; + uint64_t symIndex; + }; + std::vector files; +}; + +static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { + std::string out; + llvm::raw_string_ostream os(out); + os << "undefined symbol: " << toString(*undefDiag.sym); + + const size_t maxUndefReferences = 10; + size_t i = 0, numRefs = 0; + for (const UndefinedDiag::File &ref : undefDiag.files) { + std::vector symbolLocations = + getSymbolLocations(ref.oFile, ref.symIndex); + numRefs += symbolLocations.size(); + for (const std::string &s : symbolLocations) { + if (i >= maxUndefReferences) + break; + os << s; + i++; + } + } + if (i < numRefs) + os << "\n>>> referenced " << numRefs - i << " more times"; + errorOrWarn(os.str()); +} + +void SymbolTable::loadMinGWAutomaticImports() { + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) + continue; + if (!sym->isUsedInRegularObj) + continue; + + StringRef name = undef->getName(); + + if (name.startswith("__imp_")) + continue; + // If we have an undefined symbol, but we have a Lazy representing a + // symbol we could load from file, make sure to load that. + Lazy *l = dyn_cast_or_null(find(("__imp_" + name).str())); + if (!l || l->pendingArchiveLoad) + continue; + + log("Loading lazy " + l->getName() + " from " + l->file->getName() + + " for automatic import"); + l->pendingArchiveLoad = true; + l->file->addMember(l->sym); + } +} + +bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { + if (name.startswith("__imp_")) + return false; + Defined *imp = dyn_cast_or_null(find(("__imp_" + name).str())); + if (!imp) + return false; + + // Replace the reference directly to a variable with a reference + // to the import address table instead. This obviously isn't right, + // but we mark the symbol as isRuntimePseudoReloc, and a later pass + // will add runtime pseudo relocations for every relocation against + // this Symbol. The runtime pseudo relocation framework expects the + // reference itself to point at the IAT entry. + size_t impSize = 0; + if (isa(imp)) { + log("Automatically importing " + name + " from " + + cast(imp)->getDLLName()); + impSize = sizeof(DefinedImportData); + } else if (isa(imp)) { + log("Automatically importing " + name + " from " + + toString(cast(imp)->file)); + impSize = sizeof(DefinedRegular); + } else { + warn("unable to automatically import " + name + " from " + imp->getName() + + " from " + toString(cast(imp)->file) + + "; unexpected symbol type"); + return false; + } + sym->replaceKeepingName(imp, impSize); + sym->isRuntimePseudoReloc = true; + + // There may exist symbols named .refptr. which only consist + // of a single pointer to . If it turns out is + // automatically imported, we don't need to keep the .refptr. + // pointer at all, but redirect all accesses to it to the IAT entry + // for __imp_ instead, and drop the whole .refptr. chunk. + DefinedRegular *refptr = + dyn_cast_or_null(find((".refptr." + name).str())); + if (refptr && refptr->getChunk()->getSize() == config->wordsize) { + SectionChunk *sc = dyn_cast_or_null(refptr->getChunk()); + if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { + log("Replacing .refptr." + name + " with " + imp->getName()); + refptr->getChunk()->live = false; + refptr->replaceKeepingName(imp, impSize); + } + } + return true; +} + +void SymbolTable::reportRemainingUndefines() { + SmallPtrSet undefs; + DenseMap localImports; + + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) + continue; + if (!sym->isUsedInRegularObj) + continue; + + StringRef name = undef->getName(); + + // A weak alias may have been resolved, so check for that. + if (Defined *d = undef->getWeakAlias()) { + // We want to replace Sym with D. However, we can't just blindly + // copy sizeof(SymbolUnion) bytes from D to Sym because D may be an + // internal symbol, and internal symbols are stored as "unparented" + // Symbols. For that reason we need to check which type of symbol we + // are dealing with and copy the correct number of bytes. + if (isa(d)) + memcpy(sym, d, sizeof(DefinedRegular)); + else if (isa(d)) + memcpy(sym, d, sizeof(DefinedAbsolute)); + else + memcpy(sym, d, sizeof(SymbolUnion)); + continue; + } + + // If we can resolve a symbol by removing __imp_ prefix, do that. + // This odd rule is for compatibility with MSVC linker. + if (name.startswith("__imp_")) { + Symbol *imp = find(name.substr(strlen("__imp_"))); + if (imp && isa(imp)) { + auto *d = cast(imp); + replaceSymbol(sym, name, d); + localImportChunks.push_back(cast(sym)->getChunk()); + localImports[sym] = d; + continue; + } + } + + // We don't want to report missing Microsoft precompiled headers symbols. + // A proper message will be emitted instead in PDBLinker::aquirePrecompObj + if (name.contains("_PchSym_")) + continue; + + if (config->mingw && handleMinGWAutomaticImport(sym, name)) + continue; + + // Remaining undefined symbols are not fatal if /force is specified. + // They are replaced with dummy defined symbols. + if (config->forceUnresolved) + replaceSymbol(sym, name, 0); + undefs.insert(sym); + } + + if (undefs.empty() && localImports.empty()) + return; + + for (Symbol *b : config->gcroot) { + if (undefs.count(b)) + errorOrWarn(": undefined symbol: " + toString(*b)); + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(b)) + warn(": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); + } + + std::vector undefDiags; + DenseMap firstDiag; + + for (ObjFile *file : ObjFile::instances) { + size_t symIndex = (size_t)-1; + for (Symbol *sym : file->getSymbols()) { + ++symIndex; + if (!sym) + continue; + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, symIndex}}}); + } else { + undefDiags[it->second].files.push_back({file, symIndex}); + } + } + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(sym)) + warn(toString(file) + + ": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); + } + } + + for (const UndefinedDiag& undefDiag : undefDiags) + reportUndefinedSymbol(undefDiag); +} + +std::pair SymbolTable::insert(StringRef name) { + bool inserted = false; + Symbol *&sym = symMap[CachedHashStringRef(name)]; + if (!sym) { + sym = reinterpret_cast(make()); + sym->isUsedInRegularObj = false; + sym->pendingArchiveLoad = false; + inserted = true; + } + return {sym, inserted}; +} + +std::pair SymbolTable::insert(StringRef name, InputFile *file) { + std::pair result = insert(name); + if (!file || !isa(file)) + result.first->isUsedInRegularObj = true; + return result; +} + +Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, + bool isWeakAlias) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, f); + if (wasInserted || (isa(s) && isWeakAlias)) { + replaceSymbol(s, name); + return s; + } + if (auto *l = dyn_cast(s)) { + if (!s->pendingArchiveLoad) { + s->pendingArchiveLoad = true; + l->file->addMember(l->sym); + } + } + return s; +} + +void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) { + StringRef name = sym.getName(); + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name); + if (wasInserted) { + replaceSymbol(s, f, sym); + return; + } + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) + return; + s->pendingArchiveLoad = true; + f->addMember(sym); +} + +void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { + std::string msg = "duplicate symbol: " + toString(*existing) + " in " + + toString(existing->getFile()) + " and in " + + toString(newFile); + + if (config->forceMultiple) + warn(msg); + else + error(msg); +} + +Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, sym); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; +} + +Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, va); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; +} + +Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, c); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; +} + +Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, + const coff_symbol_generic *sym, + SectionChunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) + replaceSymbol(s, f, n, /*IsCOMDAT*/ false, + /*IsExternal*/ true, sym, c); + else + reportDuplicate(s, f); + return s; +} + +std::pair +SymbolTable::addComdat(InputFile *f, StringRef n, + const coff_symbol_generic *sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) { + replaceSymbol(s, f, n, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym, nullptr); + return {cast(s), true}; + } + auto *existingSymbol = cast(s); + if (!existingSymbol->isCOMDAT) + reportDuplicate(s, f); + return {existingSymbol, false}; +} + +Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, + const coff_symbol_generic *sym, CommonChunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) + replaceSymbol(s, f, n, size, sym, c); + else if (auto *dc = dyn_cast(s)) + if (size > dc->getSize()) + replaceSymbol(s, f, n, size, sym, c); + return s; +} + +Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) { + replaceSymbol(s, n, f); + return s; + } + + reportDuplicate(s, f); + return nullptr; +} + +Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, + uint16_t machine) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) { + replaceSymbol(s, name, id, machine); + return s; + } + + reportDuplicate(s, id->file); + return nullptr; +} + +void SymbolTable::addLibcall(StringRef name) { + Symbol *sym = findUnderscore(name); + if (!sym) + return; + + if (Lazy *l = dyn_cast(sym)) { + MemoryBufferRef mb = l->getMemberBuffer(); + if (identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode) + addUndefined(sym->getName()); + } +} + +std::vector SymbolTable::getChunks() { + std::vector res; + for (ObjFile *file : ObjFile::instances) { + ArrayRef v = file->getChunks(); + res.insert(res.end(), v.begin(), v.end()); + } + return res; +} + +Symbol *SymbolTable::find(StringRef name) { + return symMap.lookup(CachedHashStringRef(name)); +} + +Symbol *SymbolTable::findUnderscore(StringRef name) { + if (config->machine == I386) + return find(("_" + name).str()); + return find(name); +} + +// Return all symbols that start with Prefix, possibly ignoring the first +// character of Prefix or the first character symbol. +std::vector SymbolTable::getSymsWithPrefix(StringRef prefix) { + std::vector syms; + for (auto pair : symMap) { + StringRef name = pair.first.val(); + if (name.startswith(prefix) || name.startswith(prefix.drop_front()) || + name.drop_front().startswith(prefix) || + name.drop_front().startswith(prefix.drop_front())) { + syms.push_back(pair.second); + } + } + return syms; +} + +Symbol *SymbolTable::findMangle(StringRef name) { + if (Symbol *sym = find(name)) + if (!isa(sym)) + return sym; + + // Efficient fuzzy string lookup is impossible with a hash table, so iterate + // the symbol table once and collect all possibly matching symbols into this + // vector. Then compare each possibly matching symbol with each possible + // mangling. + std::vector syms = getSymsWithPrefix(name); + auto findByPrefix = [&syms](const Twine &t) -> Symbol * { + std::string prefix = t.str(); + for (auto *s : syms) + if (s->getName().startswith(prefix)) + return s; + return nullptr; + }; + + // For non-x86, just look for C++ functions. + if (config->machine != I386) + return findByPrefix("?" + name + "@@Y"); + + if (!name.startswith("_")) + return nullptr; + // Search for x86 stdcall function. + if (Symbol *s = findByPrefix(name + "@")) + return s; + // Search for x86 fastcall function. + if (Symbol *s = findByPrefix("@" + name.substr(1) + "@")) + return s; + // Search for x86 vectorcall function. + if (Symbol *s = findByPrefix(name.substr(1) + "@@")) + return s; + // Search for x86 C++ non-member function. + return findByPrefix("?" + name.substr(1) + "@@Y"); +} + +Symbol *SymbolTable::addUndefined(StringRef name) { + return addUndefined(name, nullptr, false); +} + +std::vector SymbolTable::compileBitcodeFiles() { + lto.reset(new BitcodeCompiler); + for (BitcodeFile *f : BitcodeFile::instances) + lto->add(*f); + return lto->compile(); +} + +void SymbolTable::addCombinedLTOObjects() { + if (BitcodeFile::instances.empty()) + return; + + ScopedTimer t(ltoTimer); + for (StringRef object : compileBitcodeFiles()) { + auto *obj = make(MemoryBufferRef(object, "lto.tmp")); + obj->parse(); + ObjFile::instances.push_back(obj); + } +} + +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/SymbolTable.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/SymbolTable.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/SymbolTable.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/SymbolTable.h (revision 352529) @@ -0,0 +1,132 @@ +//===- SymbolTable.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_COFF_SYMBOL_TABLE_H +#define LLD_COFF_SYMBOL_TABLE_H + +#include "InputFiles.h" +#include "LTO.h" +#include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +struct LTOCodeGenerator; +} + +namespace lld { +namespace coff { + +class Chunk; +class CommonChunk; +class Defined; +class DefinedAbsolute; +class DefinedRegular; +class DefinedRelative; +class Lazy; +class SectionChunk; +class Symbol; + +// SymbolTable is a bucket of all known symbols, including defined, +// undefined, or lazy symbols (the last one is symbols in archive +// files whose archive members are not yet loaded). +// +// We put all symbols of all files to a SymbolTable, and the +// SymbolTable selects the "best" symbols if there are name +// conflicts. For example, obviously, a defined symbol is better than +// an undefined symbol. Or, if there's a conflict between a lazy and a +// undefined, it'll read an archive member to read a real definition +// to replace the lazy symbol. The logic is implemented in the +// add*() functions, which are called by input files as they are parsed. +// There is one add* function per symbol type. +class SymbolTable { +public: + void addFile(InputFile *file); + + // Try to resolve any undefined symbols and update the symbol table + // accordingly, then print an error message for any remaining undefined + // symbols. + void reportRemainingUndefines(); + + void loadMinGWAutomaticImports(); + bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); + + // Returns a list of chunks of selected symbols. + std::vector getChunks(); + + // Returns a symbol for a given name. Returns a nullptr if not found. + Symbol *find(StringRef name); + Symbol *findUnderscore(StringRef name); + + // Occasionally we have to resolve an undefined symbol to its + // mangled symbol. This function tries to find a mangled name + // for U from the symbol table, and if found, set the symbol as + // a weak alias for U. + Symbol *findMangle(StringRef name); + + // Build a set of COFF objects representing the combined contents of + // BitcodeFiles and add them to the symbol table. Called after all files are + // added and before the writer writes results to a file. + void addCombinedLTOObjects(); + std::vector compileBitcodeFiles(); + + // Creates an Undefined symbol for a given name. + Symbol *addUndefined(StringRef name); + + Symbol *addSynthetic(StringRef n, Chunk *c); + Symbol *addAbsolute(StringRef n, uint64_t va); + + Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); + void addLazy(ArchiveFile *f, const Archive::Symbol &sym); + Symbol *addAbsolute(StringRef n, COFFSymbolRef s); + Symbol *addRegular(InputFile *f, StringRef n, + const llvm::object::coff_symbol_generic *s = nullptr, + SectionChunk *c = nullptr); + std::pair + addComdat(InputFile *f, StringRef n, + const llvm::object::coff_symbol_generic *s = nullptr); + Symbol *addCommon(InputFile *f, StringRef n, uint64_t size, + const llvm::object::coff_symbol_generic *s = nullptr, + CommonChunk *c = nullptr); + Symbol *addImportData(StringRef n, ImportFile *f); + Symbol *addImportThunk(StringRef name, DefinedImportData *s, + uint16_t machine); + void addLibcall(StringRef name); + + void reportDuplicate(Symbol *existing, InputFile *newFile); + + // A list of chunks which to be added to .rdata. + std::vector localImportChunks; + + // Iterates symbols in non-determinstic hash table order. + template void forEachSymbol(T callback) { + for (auto &pair : symMap) + callback(pair.second); + } + +private: + /// Inserts symbol if not already present. + std::pair insert(StringRef name); + /// Same as insert(Name), but also sets isUsedInRegularObj. + std::pair insert(StringRef name, InputFile *f); + + std::vector getSymsWithPrefix(StringRef prefix); + + llvm::DenseMap symMap; + std::unique_ptr lto; +}; + +extern SymbolTable *symtab; + +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/SymbolTable.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Symbols.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Symbols.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Symbols.cpp (revision 352529) @@ -0,0 +1,131 @@ +//===- Symbols.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Symbols.h" +#include "InputFiles.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld::coff; + +namespace lld { + +static_assert(sizeof(SymbolUnion) <= 48, + "symbols should be optimized for memory usage"); + +// Returns a symbol name for an error message. +static std::string demangle(StringRef symName) { + if (config->demangle) + if (Optional s = demangleMSVC(symName)) + return *s; + return symName; +} +std::string toString(coff::Symbol &b) { return demangle(b.getName()); } +std::string toCOFFString(const Archive::Symbol &b) { + return demangle(b.getName()); +} + +namespace coff { + +StringRef Symbol::getName() { + // COFF symbol names are read lazily for a performance reason. + // Non-external symbol names are never used by the linker except for logging + // or debugging. Their internal references are resolved not by name but by + // symbol index. And because they are not external, no one can refer them by + // name. Object files contain lots of non-external symbols, and creating + // StringRefs for them (which involves lots of strlen() on the string table) + // is a waste of time. + if (nameData == nullptr) { + auto *d = cast(this); + StringRef nameStr; + cast(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr); + nameData = nameStr.data(); + nameSize = nameStr.size(); + assert(nameSize == nameStr.size() && "name length truncated"); + } + return StringRef(nameData, nameSize); +} + +InputFile *Symbol::getFile() { + if (auto *sym = dyn_cast(this)) + return sym->file; + if (auto *sym = dyn_cast(this)) + return sym->file; + return nullptr; +} + +bool Symbol::isLive() const { + if (auto *r = dyn_cast(this)) + return r->getChunk()->live; + if (auto *imp = dyn_cast(this)) + return imp->file->live; + if (auto *imp = dyn_cast(this)) + return imp->wrappedSym->file->thunkLive; + // Assume any other kind of symbol is live. + return true; +} + +// MinGW specific. +void Symbol::replaceKeepingName(Symbol *other, size_t size) { + StringRef origName = getName(); + memcpy(this, other, size); + nameData = origName.data(); + nameSize = origName.size(); +} + +COFFSymbolRef DefinedCOFF::getCOFFSymbol() { + size_t symSize = cast(file)->getCOFFObj()->getSymbolTableEntrySize(); + if (symSize == sizeof(coff_symbol16)) + return COFFSymbolRef(reinterpret_cast(sym)); + assert(symSize == sizeof(coff_symbol32)); + return COFFSymbolRef(reinterpret_cast(sym)); +} + +uint16_t DefinedAbsolute::numOutputSections; + +static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { + if (machine == AMD64) + return make(s); + if (machine == I386) + return make(s); + if (machine == ARM64) + return make(s); + assert(machine == ARMNT); + return make(s); +} + +DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s, + uint16_t machine) + : Defined(DefinedImportThunkKind, name), wrappedSym(s), + data(makeImportThunk(s, machine)) {} + +Defined *Undefined::getWeakAlias() { + // A weak alias may be a weak alias to another symbol, so check recursively. + for (Symbol *a = weakAlias; a; a = cast(a)->weakAlias) + if (auto *d = dyn_cast(a)) + return d; + return nullptr; +} + +MemoryBufferRef Lazy::getMemberBuffer() { + Archive::Child c = + CHECK(sym.getMember(), + "could not get the member for symbol " + toCOFFString(sym)); + return CHECK(c.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + toCOFFString(sym)); +} +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/Symbols.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Symbols.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Symbols.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Symbols.h (revision 352529) @@ -0,0 +1,444 @@ +//===- Symbols.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_COFF_SYMBOLS_H +#define LLD_COFF_SYMBOLS_H + +#include "Chunks.h" +#include "Config.h" +#include "lld/Common/LLVM.h" +#include "lld/Common/Memory.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFF.h" +#include +#include +#include + +namespace lld { + +std::string toString(coff::Symbol &b); + +// There are two different ways to convert an Archive::Symbol to a string: +// One for Microsoft name mangling and one for Itanium name mangling. +// Call the functions toCOFFString and toELFString, not just toString. +std::string toCOFFString(const coff::Archive::Symbol &b); + +namespace coff { + +using llvm::object::Archive; +using llvm::object::COFFSymbolRef; +using llvm::object::coff_import_header; +using llvm::object::coff_symbol_generic; + +class ArchiveFile; +class InputFile; +class ObjFile; +class SymbolTable; + +// The base class for real symbol classes. +class Symbol { +public: + enum Kind { + // The order of these is significant. We start with the regular defined + // symbols as those are the most prevalent and the zero tag is the cheapest + // to set. Among the defined kinds, the lower the kind is preferred over + // the higher kind when testing whether one symbol should take precedence + // over another. + DefinedRegularKind = 0, + DefinedCommonKind, + DefinedLocalImportKind, + DefinedImportThunkKind, + DefinedImportDataKind, + DefinedAbsoluteKind, + DefinedSyntheticKind, + + UndefinedKind, + LazyKind, + + LastDefinedCOFFKind = DefinedCommonKind, + LastDefinedKind = DefinedSyntheticKind, + }; + + Kind kind() const { return static_cast(symbolKind); } + + // Returns the symbol name. + StringRef getName(); + + void replaceKeepingName(Symbol *other, size_t size); + + // Returns the file from which this symbol was created. + InputFile *getFile(); + + // Indicates that this symbol will be included in the final image. Only valid + // after calling markLive. + bool isLive() const; + +protected: + friend SymbolTable; + explicit Symbol(Kind k, StringRef n = "") + : symbolKind(k), isExternal(true), isCOMDAT(false), + writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false), + isRuntimePseudoReloc(false), nameSize(n.size()), + nameData(n.empty() ? nullptr : n.data()) {} + + const unsigned symbolKind : 8; + unsigned isExternal : 1; + +public: + // This bit is used by the \c DefinedRegular subclass. + unsigned isCOMDAT : 1; + + // This bit is used by Writer::createSymbolAndStringTable() to prevent + // symbols from being written to the symbol table more than once. + unsigned writtenToSymtab : 1; + + // True if this symbol was referenced by a regular (non-bitcode) object. + unsigned isUsedInRegularObj : 1; + + // True if we've seen both a lazy and an undefined symbol with this symbol + // name, which means that we have enqueued an archive member load and should + // not load any more archive members to resolve the same symbol. + unsigned pendingArchiveLoad : 1; + + /// True if we've already added this symbol to the list of GC roots. + unsigned isGCRoot : 1; + + unsigned isRuntimePseudoReloc : 1; + +protected: + // Symbol name length. Assume symbol lengths fit in a 32-bit integer. + uint32_t nameSize; + + const char *nameData; +}; + +// The base class for any defined symbols, including absolute symbols, +// etc. +class Defined : public Symbol { +public: + Defined(Kind k, StringRef n) : Symbol(k, n) {} + + static bool classof(const Symbol *s) { return s->kind() <= LastDefinedKind; } + + // Returns the RVA (relative virtual address) of this symbol. The + // writer sets and uses RVAs. + uint64_t getRVA(); + + // Returns the chunk containing this symbol. Absolute symbols and __ImageBase + // do not have chunks, so this may return null. + Chunk *getChunk(); +}; + +// Symbols defined via a COFF object file or bitcode file. For COFF files, this +// stores a coff_symbol_generic*, and names of internal symbols are lazily +// loaded through that. For bitcode files, Sym is nullptr and the name is stored +// as a decomposed StringRef. +class DefinedCOFF : public Defined { + friend Symbol; + +public: + DefinedCOFF(Kind k, InputFile *f, StringRef n, const coff_symbol_generic *s) + : Defined(k, n), file(f), sym(s) {} + + static bool classof(const Symbol *s) { + return s->kind() <= LastDefinedCOFFKind; + } + + InputFile *getFile() { return file; } + + COFFSymbolRef getCOFFSymbol(); + + InputFile *file; + +protected: + const coff_symbol_generic *sym; +}; + +// Regular defined symbols read from object file symbol tables. +class DefinedRegular : public DefinedCOFF { +public: + DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT, + bool isExternal = false, + const coff_symbol_generic *s = nullptr, + SectionChunk *c = nullptr) + : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) { + this->isExternal = isExternal; + this->isCOMDAT = isCOMDAT; + } + + static bool classof(const Symbol *s) { + return s->kind() == DefinedRegularKind; + } + + uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; } + SectionChunk *getChunk() const { return *data; } + uint32_t getValue() const { return sym->Value; } + + SectionChunk **data; +}; + +class DefinedCommon : public DefinedCOFF { +public: + DefinedCommon(InputFile *f, StringRef n, uint64_t size, + const coff_symbol_generic *s = nullptr, + CommonChunk *c = nullptr) + : DefinedCOFF(DefinedCommonKind, f, n, s), data(c), size(size) { + this->isExternal = true; + } + + static bool classof(const Symbol *s) { + return s->kind() == DefinedCommonKind; + } + + uint64_t getRVA() { return data->getRVA(); } + CommonChunk *getChunk() { return data; } + +private: + friend SymbolTable; + uint64_t getSize() const { return size; } + CommonChunk *data; + uint64_t size; +}; + +// Absolute symbols. +class DefinedAbsolute : public Defined { +public: + DefinedAbsolute(StringRef n, COFFSymbolRef s) + : Defined(DefinedAbsoluteKind, n), va(s.getValue()) { + isExternal = s.isExternal(); + } + + DefinedAbsolute(StringRef n, uint64_t v) + : Defined(DefinedAbsoluteKind, n), va(v) {} + + static bool classof(const Symbol *s) { + return s->kind() == DefinedAbsoluteKind; + } + + uint64_t getRVA() { return va - config->imageBase; } + void setVA(uint64_t v) { va = v; } + + // Section index relocations against absolute symbols resolve to + // this 16 bit number, and it is the largest valid section index + // plus one. This variable keeps it. + static uint16_t numOutputSections; + +private: + uint64_t va; +}; + +// This symbol is used for linker-synthesized symbols like __ImageBase and +// __safe_se_handler_table. +class DefinedSynthetic : public Defined { +public: + explicit DefinedSynthetic(StringRef name, Chunk *c) + : Defined(DefinedSyntheticKind, name), c(c) {} + + static bool classof(const Symbol *s) { + return s->kind() == DefinedSyntheticKind; + } + + // A null chunk indicates that this is __ImageBase. Otherwise, this is some + // other synthesized chunk, like SEHTableChunk. + uint32_t getRVA() { return c ? c->getRVA() : 0; } + Chunk *getChunk() { return c; } + +private: + Chunk *c; +}; + +// This class represents a symbol defined in an archive file. It is +// created from an archive file header, and it knows how to load an +// object file from an archive to replace itself with a defined +// symbol. If the resolver finds both Undefined and Lazy for +// the same name, it will ask the Lazy to load a file. +class Lazy : public Symbol { +public: + Lazy(ArchiveFile *f, const Archive::Symbol s) + : Symbol(LazyKind, s.getName()), file(f), sym(s) {} + + static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + + MemoryBufferRef getMemberBuffer(); + + ArchiveFile *file; + +private: + friend SymbolTable; + +private: + const Archive::Symbol sym; +}; + +// Undefined symbols. +class Undefined : public Symbol { +public: + explicit Undefined(StringRef n) : Symbol(UndefinedKind, n) {} + + static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; } + + // An undefined symbol can have a fallback symbol which gives an + // undefined symbol a second chance if it would remain undefined. + // If it remains undefined, it'll be replaced with whatever the + // Alias pointer points to. + Symbol *weakAlias = nullptr; + + // If this symbol is external weak, try to resolve it to a defined + // symbol by searching the chain of fallback symbols. Returns the symbol if + // successful, otherwise returns null. + Defined *getWeakAlias(); +}; + +// Windows-specific classes. + +// This class represents a symbol imported from a DLL. This has two +// names for internal use and external use. The former is used for +// name resolution, and the latter is used for the import descriptor +// table in an output. The former has "__imp_" prefix. +class DefinedImportData : public Defined { +public: + DefinedImportData(StringRef n, ImportFile *f) + : Defined(DefinedImportDataKind, n), file(f) { + } + + static bool classof(const Symbol *s) { + return s->kind() == DefinedImportDataKind; + } + + uint64_t getRVA() { return file->location->getRVA(); } + Chunk *getChunk() { return file->location; } + void setLocation(Chunk *addressTable) { file->location = addressTable; } + + StringRef getDLLName() { return file->dllName; } + StringRef getExternalName() { return file->externalName; } + uint16_t getOrdinal() { return file->hdr->OrdinalHint; } + + ImportFile *file; +}; + +// This class represents a symbol for a jump table entry which jumps +// to a function in a DLL. Linker are supposed to create such symbols +// without "__imp_" prefix for all function symbols exported from +// DLLs, so that you can call DLL functions as regular functions with +// a regular name. A function pointer is given as a DefinedImportData. +class DefinedImportThunk : public Defined { +public: + DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); + + static bool classof(const Symbol *s) { + return s->kind() == DefinedImportThunkKind; + } + + uint64_t getRVA() { return data->getRVA(); } + Chunk *getChunk() { return data; } + + DefinedImportData *wrappedSym; + +private: + Chunk *data; +}; + +// If you have a symbol "foo" in your object file, a symbol name +// "__imp_foo" becomes automatically available as a pointer to "foo". +// This class is for such automatically-created symbols. +// Yes, this is an odd feature. We didn't intend to implement that. +// This is here just for compatibility with MSVC. +class DefinedLocalImport : public Defined { +public: + DefinedLocalImport(StringRef n, Defined *s) + : Defined(DefinedLocalImportKind, n), data(make(s)) {} + + static bool classof(const Symbol *s) { + return s->kind() == DefinedLocalImportKind; + } + + uint64_t getRVA() { return data->getRVA(); } + Chunk *getChunk() { return data; } + +private: + LocalImportChunk *data; +}; + +inline uint64_t Defined::getRVA() { + switch (kind()) { + case DefinedAbsoluteKind: + return cast(this)->getRVA(); + case DefinedSyntheticKind: + return cast(this)->getRVA(); + case DefinedImportDataKind: + return cast(this)->getRVA(); + case DefinedImportThunkKind: + return cast(this)->getRVA(); + case DefinedLocalImportKind: + return cast(this)->getRVA(); + case DefinedCommonKind: + return cast(this)->getRVA(); + case DefinedRegularKind: + return cast(this)->getRVA(); + case LazyKind: + case UndefinedKind: + llvm_unreachable("Cannot get the address for an undefined symbol."); + } + llvm_unreachable("unknown symbol kind"); +} + +inline Chunk *Defined::getChunk() { + switch (kind()) { + case DefinedRegularKind: + return cast(this)->getChunk(); + case DefinedAbsoluteKind: + return nullptr; + case DefinedSyntheticKind: + return cast(this)->getChunk(); + case DefinedImportDataKind: + return cast(this)->getChunk(); + case DefinedImportThunkKind: + return cast(this)->getChunk(); + case DefinedLocalImportKind: + return cast(this)->getChunk(); + case DefinedCommonKind: + return cast(this)->getChunk(); + case LazyKind: + case UndefinedKind: + llvm_unreachable("Cannot get the chunk of an undefined symbol."); + } + llvm_unreachable("unknown symbol kind"); +} + +// A buffer class that is large enough to hold any Symbol-derived +// object. We allocate memory using this class and instantiate a symbol +// using the placement new. +union SymbolUnion { + alignas(DefinedRegular) char a[sizeof(DefinedRegular)]; + alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; + alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; + alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; + alignas(Lazy) char e[sizeof(Lazy)]; + alignas(Undefined) char f[sizeof(Undefined)]; + alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; + alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; + alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; +}; + +template +void replaceSymbol(Symbol *s, ArgT &&... arg) { + static_assert(std::is_trivially_destructible(), + "Symbol types must be trivially destructible"); + static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small"); + static_assert(alignof(T) <= alignof(SymbolUnion), + "SymbolUnion not aligned enough"); + assert(static_cast(static_cast(nullptr)) == nullptr && + "Not a Symbol"); + new (s) T(std::forward(arg)...); +} +} // namespace coff + +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/Symbols.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Writer.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Writer.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Writer.cpp (revision 352529) @@ -0,0 +1,1932 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "Config.h" +#include "DLL.h" +#include "InputFiles.h" +#include "MapFile.h" +#include "PDB.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Threads.h" +#include "lld/Common/Timer.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/xxhash.h" +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::COFF; +using namespace llvm::object; +using namespace llvm::support; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::coff; + +/* To re-generate DOSProgram: +$ cat > /tmp/DOSProgram.asm +org 0 + ; Copy cs to ds. + push cs + pop ds + ; Point ds:dx at the $-terminated string. + mov dx, str + ; Int 21/AH=09h: Write string to standard output. + mov ah, 0x9 + int 0x21 + ; Int 21/AH=4Ch: Exit with return code (in AL). + mov ax, 0x4C01 + int 0x21 +str: + db 'This program cannot be run in DOS mode.$' +align 8, db 0 +$ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin +$ xxd -i /tmp/DOSProgram.bin +*/ +static unsigned char dosProgram[] = { + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, + 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, + 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 +}; +static_assert(sizeof(dosProgram) % 8 == 0, + "DOSProgram size must be multiple of 8"); + +static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram); +static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8"); + +static const int numberOfDataDirectory = 16; + +// Global vector of all output sections. After output sections are finalized, +// this can be indexed by Chunk::getOutputSection. +static std::vector outputSections; + +OutputSection *Chunk::getOutputSection() const { + return osidx == 0 ? nullptr : outputSections[osidx - 1]; +} + +namespace { + +class DebugDirectoryChunk : public NonSectionChunk { +public: + DebugDirectoryChunk(const std::vector &r, bool writeRepro) + : records(r), writeRepro(writeRepro) {} + + size_t getSize() const override { + return (records.size() + int(writeRepro)) * sizeof(debug_directory); + } + + void writeTo(uint8_t *b) const override { + auto *d = reinterpret_cast(b); + + for (const Chunk *record : records) { + OutputSection *os = record->getOutputSection(); + uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA()); + fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(), + record->getRVA(), offs); + ++d; + } + + if (writeRepro) { + // FIXME: The COFF spec allows either a 0-sized entry to just say + // "the timestamp field is really a hash", or a 4-byte size field + // followed by that many bytes containing a longer hash (with the + // lowest 4 bytes usually being the timestamp in little-endian order). + // Consider storing the full 8 bytes computed by xxHash64 here. + fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); + } + } + + void setTimeDateStamp(uint32_t timeDateStamp) { + for (support::ulittle32_t *tds : timeDateStamps) + *tds = timeDateStamp; + } + +private: + void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size, + uint64_t rva, uint64_t offs) const { + d->Characteristics = 0; + d->TimeDateStamp = 0; + d->MajorVersion = 0; + d->MinorVersion = 0; + d->Type = debugType; + d->SizeOfData = size; + d->AddressOfRawData = rva; + d->PointerToRawData = offs; + + timeDateStamps.push_back(&d->TimeDateStamp); + } + + mutable std::vector timeDateStamps; + const std::vector &records; + bool writeRepro; +}; + +class CVDebugRecordChunk : public NonSectionChunk { +public: + size_t getSize() const override { + return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1; + } + + void writeTo(uint8_t *b) const override { + // Save off the DebugInfo entry to backfill the file signature (build id) + // in Writer::writeBuildId + buildId = reinterpret_cast(b); + + // variable sized field (PDB Path) + char *p = reinterpret_cast(b + sizeof(*buildId)); + if (!config->pdbAltPath.empty()) + memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size()); + p[config->pdbAltPath.size()] = '\0'; + } + + mutable codeview::DebugInfo *buildId = nullptr; +}; + +// PartialSection represents a group of chunks that contribute to an +// OutputSection. Collating a collection of PartialSections of same name and +// characteristics constitutes the OutputSection. +class PartialSectionKey { +public: + StringRef name; + unsigned characteristics; + + bool operator<(const PartialSectionKey &other) const { + int c = name.compare(other.name); + if (c == 1) + return false; + if (c == 0) + return characteristics < other.characteristics; + return true; + } +}; + +// The writer writes a SymbolTable result to a file. +class Writer { +public: + Writer() : buffer(errorHandler().outputBuffer) {} + void run(); + +private: + void createSections(); + void createMiscChunks(); + void createImportTables(); + void appendImportThunks(); + void locateImportTables(); + void createExportTable(); + void mergeSections(); + void removeUnusedSections(); + void assignAddresses(); + void finalizeAddresses(); + void removeEmptySections(); + void assignOutputSectionIndices(); + void createSymbolAndStringTable(); + void openFile(StringRef outputPath); + template void writeHeader(); + void createSEHTable(); + void createRuntimePseudoRelocs(); + void insertCtorDtorSymbols(); + void createGuardCFTables(); + void markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols); + void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym); + void setSectionPermissions(); + void writeSections(); + void writeBuildId(); + void sortExceptionTable(); + void sortCRTSectionChunks(std::vector &chunks); + void addSyntheticIdata(); + void fixPartialSectionChars(StringRef name, uint32_t chars); + bool fixGnuImportChunks(); + PartialSection *createPartialSection(StringRef name, uint32_t outChars); + PartialSection *findPartialSection(StringRef name, uint32_t outChars); + + llvm::Optional createSymbol(Defined *d); + size_t addEntryToStringTable(StringRef str); + + OutputSection *findSection(StringRef name); + void addBaserels(); + void addBaserelBlocks(std::vector &v); + + uint32_t getSizeOfInitializedData(); + + std::unique_ptr &buffer; + std::map partialSections; + std::vector strtab; + std::vector outputSymtab; + IdataContents idata; + Chunk *importTableStart = nullptr; + uint64_t importTableSize = 0; + Chunk *iatStart = nullptr; + uint64_t iatSize = 0; + DelayLoadContents delayIdata; + EdataContents edata; + bool setNoSEHCharacteristic = false; + + DebugDirectoryChunk *debugDirectory = nullptr; + std::vector debugRecords; + CVDebugRecordChunk *buildId = nullptr; + ArrayRef sectionTable; + + uint64_t fileSize; + uint32_t pointerToSymbolTable = 0; + uint64_t sizeOfImage; + uint64_t sizeOfHeaders; + + OutputSection *textSec; + OutputSection *rdataSec; + OutputSection *buildidSec; + OutputSection *dataSec; + OutputSection *pdataSec; + OutputSection *idataSec; + OutputSection *edataSec; + OutputSection *didatSec; + OutputSection *rsrcSec; + OutputSection *relocSec; + OutputSection *ctorsSec; + OutputSection *dtorsSec; + + // The first and last .pdata sections in the output file. + // + // We need to keep track of the location of .pdata in whichever section it + // gets merged into so that we can sort its contents and emit a correct data + // directory entry for the exception table. This is also the case for some + // other sections (such as .edata) but because the contents of those sections + // are entirely linker-generated we can keep track of their locations using + // the chunks that the linker creates. All .pdata chunks come from input + // files, so we need to keep track of them separately. + Chunk *firstPdata = nullptr; + Chunk *lastPdata; +}; +} // anonymous namespace + +namespace lld { +namespace coff { + +static Timer codeLayoutTimer("Code Layout", Timer::root()); +static Timer diskCommitTimer("Commit Output File", Timer::root()); + +void writeResult() { Writer().run(); } + +void OutputSection::addChunk(Chunk *c) { + chunks.push_back(c); +} + +void OutputSection::insertChunkAtStart(Chunk *c) { + chunks.insert(chunks.begin(), c); +} + +void OutputSection::setPermissions(uint32_t c) { + header.Characteristics &= ~permMask; + header.Characteristics |= c; +} + +void OutputSection::merge(OutputSection *other) { + chunks.insert(chunks.end(), other->chunks.begin(), other->chunks.end()); + other->chunks.clear(); + contribSections.insert(contribSections.end(), other->contribSections.begin(), + other->contribSections.end()); + other->contribSections.clear(); +} + +// Write the section header to a given buffer. +void OutputSection::writeHeaderTo(uint8_t *buf) { + auto *hdr = reinterpret_cast(buf); + *hdr = header; + if (stringTableOff) { + // If name is too long, write offset into the string table as a name. + sprintf(hdr->Name, "/%d", stringTableOff); + } else { + assert(!config->debug || name.size() <= COFF::NameSize || + (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); + strncpy(hdr->Name, name.data(), + std::min(name.size(), (size_t)COFF::NameSize)); + } +} + +void OutputSection::addContributingPartialSection(PartialSection *sec) { + contribSections.push_back(sec); +} + +} // namespace coff +} // namespace lld + +// Check whether the target address S is in range from a relocation +// of type relType at address P. +static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { + if (config->machine == ARMNT) { + int64_t diff = AbsoluteDifference(s, p + 4) + margin; + switch (relType) { + case IMAGE_REL_ARM_BRANCH20T: + return isInt<21>(diff); + case IMAGE_REL_ARM_BRANCH24T: + case IMAGE_REL_ARM_BLX23T: + return isInt<25>(diff); + default: + return true; + } + } else if (config->machine == ARM64) { + int64_t diff = AbsoluteDifference(s, p) + margin; + switch (relType) { + case IMAGE_REL_ARM64_BRANCH26: + return isInt<28>(diff); + case IMAGE_REL_ARM64_BRANCH19: + return isInt<21>(diff); + case IMAGE_REL_ARM64_BRANCH14: + return isInt<16>(diff); + default: + return true; + } + } else { + llvm_unreachable("Unexpected architecture"); + } +} + +// Return the last thunk for the given target if it is in range, +// or create a new one. +static std::pair +getThunk(DenseMap &lastThunks, Defined *target, uint64_t p, + uint16_t type, int margin) { + Defined *&lastThunk = lastThunks[target->getRVA()]; + if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin)) + return {lastThunk, false}; + Chunk *c; + switch (config->machine) { + case ARMNT: + c = make(target); + break; + case ARM64: + c = make(target); + break; + default: + llvm_unreachable("Unexpected architecture"); + } + Defined *d = make("", c); + lastThunk = d; + return {d, true}; +} + +// This checks all relocations, and for any relocation which isn't in range +// it adds a thunk after the section chunk that contains the relocation. +// If the latest thunk for the specific target is in range, that is used +// instead of creating a new thunk. All range checks are done with the +// specified margin, to make sure that relocations that originally are in +// range, but only barely, also get thunks - in case other added thunks makes +// the target go out of range. +// +// After adding thunks, we verify that all relocations are in range (with +// no extra margin requirements). If this failed, we restart (throwing away +// the previously created thunks) and retry with a wider margin. +static bool createThunks(OutputSection *os, int margin) { + bool addressesChanged = false; + DenseMap lastThunks; + DenseMap, uint32_t> thunkSymtabIndices; + size_t thunksSize = 0; + // Recheck Chunks.size() each iteration, since we can insert more + // elements into it. + for (size_t i = 0; i != os->chunks.size(); ++i) { + SectionChunk *sc = dyn_cast_or_null(os->chunks[i]); + if (!sc) + continue; + size_t thunkInsertionSpot = i + 1; + + // Try to get a good enough estimate of where new thunks will be placed. + // Offset this by the size of the new thunks added so far, to make the + // estimate slightly better. + size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize; + ObjFile *file = sc->file; + std::vector> relocReplacements; + ArrayRef originalRelocs = + file->getCOFFObj()->getRelocations(sc->header); + for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) { + const coff_relocation &rel = originalRelocs[j]; + Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex); + + // The estimate of the source address P should be pretty accurate, + // but we don't know whether the target Symbol address should be + // offset by thunksSize or not (or by some of thunksSize but not all of + // it), giving us some uncertainty once we have added one thunk. + uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize; + + Defined *sym = dyn_cast_or_null(relocTarget); + if (!sym) + continue; + + uint64_t s = sym->getRVA(); + + if (isInRange(rel.Type, s, p, margin)) + continue; + + // If the target isn't in range, hook it up to an existing or new + // thunk. + Defined *thunk; + bool wasNew; + std::tie(thunk, wasNew) = getThunk(lastThunks, sym, p, rel.Type, margin); + if (wasNew) { + Chunk *thunkChunk = thunk->getChunk(); + thunkChunk->setRVA( + thunkInsertionRVA); // Estimate of where it will be located. + os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk); + thunkInsertionSpot++; + thunksSize += thunkChunk->getSize(); + thunkInsertionRVA += thunkChunk->getSize(); + addressesChanged = true; + } + + // To redirect the relocation, add a symbol to the parent object file's + // symbol table, and replace the relocation symbol table index with the + // new index. + auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U}); + uint32_t &thunkSymbolIndex = insertion.first->second; + if (insertion.second) + thunkSymbolIndex = file->addRangeThunkSymbol(thunk); + relocReplacements.push_back({j, thunkSymbolIndex}); + } + + // Get a writable copy of this section's relocations so they can be + // modified. If the relocations point into the object file, allocate new + // memory. Otherwise, this must be previously allocated memory that can be + // modified in place. + ArrayRef curRelocs = sc->getRelocs(); + MutableArrayRef newRelocs; + if (originalRelocs.data() == curRelocs.data()) { + newRelocs = makeMutableArrayRef( + bAlloc.Allocate(originalRelocs.size()), + originalRelocs.size()); + } else { + newRelocs = makeMutableArrayRef( + const_cast(curRelocs.data()), curRelocs.size()); + } + + // Copy each relocation, but replace the symbol table indices which need + // thunks. + auto nextReplacement = relocReplacements.begin(); + auto endReplacement = relocReplacements.end(); + for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) { + newRelocs[i] = originalRelocs[i]; + if (nextReplacement != endReplacement && nextReplacement->first == i) { + newRelocs[i].SymbolTableIndex = nextReplacement->second; + ++nextReplacement; + } + } + + sc->setRelocs(newRelocs); + } + return addressesChanged; +} + +// Verify that all relocations are in range, with no extra margin requirements. +static bool verifyRanges(const std::vector chunks) { + for (Chunk *c : chunks) { + SectionChunk *sc = dyn_cast_or_null(c); + if (!sc) + continue; + + ArrayRef relocs = sc->getRelocs(); + for (size_t j = 0, e = relocs.size(); j < e; ++j) { + const coff_relocation &rel = relocs[j]; + Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex); + + Defined *sym = dyn_cast_or_null(relocTarget); + if (!sym) + continue; + + uint64_t p = sc->getRVA() + rel.VirtualAddress; + uint64_t s = sym->getRVA(); + + if (!isInRange(rel.Type, s, p, 0)) + return false; + } + } + return true; +} + +// Assign addresses and add thunks if necessary. +void Writer::finalizeAddresses() { + assignAddresses(); + if (config->machine != ARMNT && config->machine != ARM64) + return; + + size_t origNumChunks = 0; + for (OutputSection *sec : outputSections) { + sec->origChunks = sec->chunks; + origNumChunks += sec->chunks.size(); + } + + int pass = 0; + int margin = 1024 * 100; + while (true) { + // First check whether we need thunks at all, or if the previous pass of + // adding them turned out ok. + bool rangesOk = true; + size_t numChunks = 0; + for (OutputSection *sec : outputSections) { + if (!verifyRanges(sec->chunks)) { + rangesOk = false; + break; + } + numChunks += sec->chunks.size(); + } + if (rangesOk) { + if (pass > 0) + log("Added " + Twine(numChunks - origNumChunks) + " thunks with " + + "margin " + Twine(margin) + " in " + Twine(pass) + " passes"); + return; + } + + if (pass >= 10) + fatal("adding thunks hasn't converged after " + Twine(pass) + " passes"); + + if (pass > 0) { + // If the previous pass didn't work out, reset everything back to the + // original conditions before retrying with a wider margin. This should + // ideally never happen under real circumstances. + for (OutputSection *sec : outputSections) + sec->chunks = sec->origChunks; + margin *= 2; + } + + // Try adding thunks everywhere where it is needed, with a margin + // to avoid things going out of range due to the added thunks. + bool addressesChanged = false; + for (OutputSection *sec : outputSections) + addressesChanged |= createThunks(sec, margin); + // If the verification above thought we needed thunks, we should have + // added some. + assert(addressesChanged); + + // Recalculate the layout for the whole image (and verify the ranges at + // the start of the next round). + assignAddresses(); + + pass++; + } +} + +// The main function of the writer. +void Writer::run() { + ScopedTimer t1(codeLayoutTimer); + + createImportTables(); + createSections(); + createMiscChunks(); + appendImportThunks(); + createExportTable(); + mergeSections(); + removeUnusedSections(); + finalizeAddresses(); + removeEmptySections(); + assignOutputSectionIndices(); + setSectionPermissions(); + createSymbolAndStringTable(); + + if (fileSize > UINT32_MAX) + fatal("image size (" + Twine(fileSize) + ") " + + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); + + openFile(config->outputFile); + if (config->is64()) { + writeHeader(); + } else { + writeHeader(); + } + writeSections(); + sortExceptionTable(); + + t1.stop(); + + if (!config->pdbPath.empty() && config->debug) { + assert(buildId); + createPDB(symtab, outputSections, sectionTable, buildId->buildId); + } + writeBuildId(); + + writeMapFile(outputSections); + + if (errorCount()) + return; + + ScopedTimer t2(diskCommitTimer); + if (auto e = buffer->commit()) + fatal("failed to write the output file: " + toString(std::move(e))); +} + +static StringRef getOutputSectionName(StringRef name) { + StringRef s = name.split('$').first; + + // Treat a later period as a separator for MinGW, for sections like + // ".ctors.01234". + return s.substr(0, s.find('.', 1)); +} + +// For /order. +static void sortBySectionOrder(std::vector &chunks) { + auto getPriority = [](const Chunk *c) { + if (auto *sec = dyn_cast(c)) + if (sec->sym) + return config->order.lookup(sec->sym->getName()); + return 0; + }; + + llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) { + return getPriority(a) < getPriority(b); + }); +} + +// Change the characteristics of existing PartialSections that belong to the +// section Name to Chars. +void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) { + for (auto it : partialSections) { + PartialSection *pSec = it.second; + StringRef curName = pSec->name; + if (!curName.consume_front(name) || + (!curName.empty() && !curName.startswith("$"))) + continue; + if (pSec->characteristics == chars) + continue; + PartialSection *destSec = createPartialSection(pSec->name, chars); + destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(), + pSec->chunks.end()); + pSec->chunks.clear(); + } +} + +// Sort concrete section chunks from GNU import libraries. +// +// GNU binutils doesn't use short import files, but instead produces import +// libraries that consist of object files, with section chunks for the .idata$* +// sections. These are linked just as regular static libraries. Each import +// library consists of one header object, one object file for every imported +// symbol, and one trailer object. In order for the .idata tables/lists to +// be formed correctly, the section chunks within each .idata$* section need +// to be grouped by library, and sorted alphabetically within each library +// (which makes sure the header comes first and the trailer last). +bool Writer::fixGnuImportChunks() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + + // Make sure all .idata$* section chunks are mapped as RDATA in order to + // be sorted into the same sections as our own synthesized .idata chunks. + fixPartialSectionChars(".idata", rdata); + + bool hasIdata = false; + // Sort all .idata$* chunks, grouping chunks from the same library, + // with alphabetical ordering of the object fils within a library. + for (auto it : partialSections) { + PartialSection *pSec = it.second; + if (!pSec->name.startswith(".idata")) + continue; + + if (!pSec->chunks.empty()) + hasIdata = true; + llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) { + SectionChunk *sc1 = dyn_cast_or_null(s); + SectionChunk *sc2 = dyn_cast_or_null(t); + if (!sc1 || !sc2) { + // if SC1, order them ascending. If SC2 or both null, + // S is not less than T. + return sc1 != nullptr; + } + // Make a string with "libraryname/objectfile" for sorting, achieving + // both grouping by library and sorting of objects within a library, + // at once. + std::string key1 = + (sc1->file->parentName + "/" + sc1->file->getName()).str(); + std::string key2 = + (sc2->file->parentName + "/" + sc2->file->getName()).str(); + return key1 < key2; + }); + } + return hasIdata; +} + +// Add generated idata chunks, for imported symbols and DLLs, and a +// terminator in .idata$2. +void Writer::addSyntheticIdata() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + idata.create(); + + // Add the .idata content in the right section groups, to allow + // chunks from other linked in object files to be grouped together. + // See Microsoft PE/COFF spec 5.4 for details. + auto add = [&](StringRef n, std::vector &v) { + PartialSection *pSec = createPartialSection(n, rdata); + pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end()); + }; + + // The loader assumes a specific order of data. + // Add each type in the correct order. + add(".idata$2", idata.dirs); + add(".idata$4", idata.lookups); + add(".idata$5", idata.addresses); + add(".idata$6", idata.hints); + add(".idata$7", idata.dllNames); +} + +// Locate the first Chunk and size of the import directory list and the +// IAT. +void Writer::locateImportTables() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + + if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) { + if (!importDirs->chunks.empty()) + importTableStart = importDirs->chunks.front(); + for (Chunk *c : importDirs->chunks) + importTableSize += c->getSize(); + } + + if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) { + if (!importAddresses->chunks.empty()) + iatStart = importAddresses->chunks.front(); + for (Chunk *c : importAddresses->chunks) + iatSize += c->getSize(); + } +} + +// Return whether a SectionChunk's suffix (the dollar and any trailing +// suffix) should be removed and sorted into the main suffixless +// PartialSection. +static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { + // On MinGW, comdat groups are formed by putting the comdat group name + // after the '$' in the section name. For .eh_frame$, that must + // still be sorted before the .eh_frame trailer from crtend.o, thus just + // strip the section name trailer. For other sections, such as + // .tls$$ (where non-comdat .tls symbols are otherwise stored in + // ".tls$"), they must be strictly sorted after .tls. And for the + // hypothetical case of comdat .CRT$XCU, we definitely need to keep the + // suffix for sorting. Thus, to play it safe, only strip the suffix for + // the standard sections. + if (!config->mingw) + return false; + if (!sc || !sc->isCOMDAT()) + return false; + return name.startswith(".text$") || name.startswith(".data$") || + name.startswith(".rdata$") || name.startswith(".pdata$") || + name.startswith(".xdata$") || name.startswith(".eh_frame$"); +} + +// Create output section objects and add them to OutputSections. +void Writer::createSections() { + // First, create the builtin sections. + const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA; + const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA; + const uint32_t code = IMAGE_SCN_CNT_CODE; + const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE; + const uint32_t r = IMAGE_SCN_MEM_READ; + const uint32_t w = IMAGE_SCN_MEM_WRITE; + const uint32_t x = IMAGE_SCN_MEM_EXECUTE; + + SmallDenseMap, OutputSection *> sections; + auto createSection = [&](StringRef name, uint32_t outChars) { + OutputSection *&sec = sections[{name, outChars}]; + if (!sec) { + sec = make(name, outChars); + outputSections.push_back(sec); + } + return sec; + }; + + // Try to match the section order used by link.exe. + textSec = createSection(".text", code | r | x); + createSection(".bss", bss | r | w); + rdataSec = createSection(".rdata", data | r); + buildidSec = createSection(".buildid", data | r); + dataSec = createSection(".data", data | r | w); + pdataSec = createSection(".pdata", data | r); + idataSec = createSection(".idata", data | r); + edataSec = createSection(".edata", data | r); + didatSec = createSection(".didat", data | r); + rsrcSec = createSection(".rsrc", data | r); + relocSec = createSection(".reloc", data | discardable | r); + ctorsSec = createSection(".ctors", data | r | w); + dtorsSec = createSection(".dtors", data | r | w); + + // Then bin chunks by name and output characteristics. + for (Chunk *c : symtab->getChunks()) { + auto *sc = dyn_cast(c); + if (sc && !sc->live) { + if (config->verbose) + sc->printDiscardedMessage(); + continue; + } + StringRef name = c->getSectionName(); + if (shouldStripSectionSuffix(sc, name)) + name = name.split('$').first; + PartialSection *pSec = createPartialSection(name, + c->getOutputCharacteristics()); + pSec->chunks.push_back(c); + } + + fixPartialSectionChars(".rsrc", data | r); + // Even in non MinGW cases, we might need to link against GNU import + // libraries. + bool hasIdata = fixGnuImportChunks(); + if (!idata.empty()) + hasIdata = true; + + if (hasIdata) + addSyntheticIdata(); + + // Process an /order option. + if (!config->order.empty()) + for (auto it : partialSections) + sortBySectionOrder(it.second->chunks); + + if (hasIdata) + locateImportTables(); + + // Then create an OutputSection for each section. + // '$' and all following characters in input section names are + // discarded when determining output section. So, .text$foo + // contributes to .text, for example. See PE/COFF spec 3.2. + for (auto it : partialSections) { + PartialSection *pSec = it.second; + StringRef name = getOutputSectionName(pSec->name); + uint32_t outChars = pSec->characteristics; + + if (name == ".CRT") { + // In link.exe, there is a special case for the I386 target where .CRT + // sections are treated as if they have output characteristics DATA | R if + // their characteristics are DATA | R | W. This implements the same + // special case for all architectures. + outChars = data | r; + + log("Processing section " + pSec->name + " -> " + name); + + sortCRTSectionChunks(pSec->chunks); + } + + OutputSection *sec = createSection(name, outChars); + for (Chunk *c : pSec->chunks) + sec->addChunk(c); + + sec->addContributingPartialSection(pSec); + } + + // Finally, move some output sections to the end. + auto sectionOrder = [&](const OutputSection *s) { + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file + // because the loader cannot handle holes. Stripping can remove other + // discardable ones than .reloc, which is first of them (created early). + if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + return 2; + // .rsrc should come at the end of the non-discardable sections because its + // size may change by the Win32 UpdateResources() function, causing + // subsequent sections to move (see https://crbug.com/827082). + if (s == rsrcSec) + return 1; + return 0; + }; + llvm::stable_sort(outputSections, + [&](const OutputSection *s, const OutputSection *t) { + return sectionOrder(s) < sectionOrder(t); + }); +} + +void Writer::createMiscChunks() { + for (MergeChunk *p : MergeChunk::instances) { + if (p) { + p->finalizeContents(); + rdataSec->addChunk(p); + } + } + + // Create thunks for locally-dllimported symbols. + if (!symtab->localImportChunks.empty()) { + for (Chunk *c : symtab->localImportChunks) + rdataSec->addChunk(c); + } + + // Create Debug Information Chunks + OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; + if (config->debug || config->repro) { + debugDirectory = make(debugRecords, config->repro); + debugInfoSec->addChunk(debugDirectory); + } + + if (config->debug) { + // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We + // output a PDB no matter what, and this chunk provides the only means of + // allowing a debugger to match a PDB and an executable. So we need it even + // if we're ultimately not going to write CodeView data to the PDB. + buildId = make(); + debugRecords.push_back(buildId); + + for (Chunk *c : debugRecords) + debugInfoSec->addChunk(c); + } + + // Create SEH table. x86-only. + if (config->safeSEH) + createSEHTable(); + + // Create /guard:cf tables if requested. + if (config->guardCF != GuardCFLevel::Off) + createGuardCFTables(); + + if (config->mingw) { + createRuntimePseudoRelocs(); + + insertCtorDtorSymbols(); + } +} + +// Create .idata section for the DLL-imported symbol table. +// The format of this section is inherently Windows-specific. +// IdataContents class abstracted away the details for us, +// so we just let it create chunks and add them to the section. +void Writer::createImportTables() { + // Initialize DLLOrder so that import entries are ordered in + // the same order as in the command line. (That affects DLL + // initialization order, and this ordering is MSVC-compatible.) + for (ImportFile *file : ImportFile::instances) { + if (!file->live) + continue; + + std::string dll = StringRef(file->dllName).lower(); + if (config->dllOrder.count(dll) == 0) + config->dllOrder[dll] = config->dllOrder.size(); + + if (file->impSym && !isa(file->impSym)) + fatal(toString(*file->impSym) + " was replaced"); + DefinedImportData *impSym = cast_or_null(file->impSym); + if (config->delayLoads.count(StringRef(file->dllName).lower())) { + if (!file->thunkSym) + fatal("cannot delay-load " + toString(file) + + " due to import of data: " + toString(*impSym)); + delayIdata.add(impSym); + } else { + idata.add(impSym); + } + } +} + +void Writer::appendImportThunks() { + if (ImportFile::instances.empty()) + return; + + for (ImportFile *file : ImportFile::instances) { + if (!file->live) + continue; + + if (!file->thunkSym) + continue; + + if (!isa(file->thunkSym)) + fatal(toString(*file->thunkSym) + " was replaced"); + DefinedImportThunk *thunk = cast(file->thunkSym); + if (file->thunkLive) + textSec->addChunk(thunk->getChunk()); + } + + if (!delayIdata.empty()) { + Defined *helper = cast(config->delayLoadHelper); + delayIdata.create(helper); + for (Chunk *c : delayIdata.getChunks()) + didatSec->addChunk(c); + for (Chunk *c : delayIdata.getDataChunks()) + dataSec->addChunk(c); + for (Chunk *c : delayIdata.getCodeChunks()) + textSec->addChunk(c); + } +} + +void Writer::createExportTable() { + if (config->exports.empty()) + return; + for (Chunk *c : edata.chunks) + edataSec->addChunk(c); +} + +void Writer::removeUnusedSections() { + // Remove sections that we can be sure won't get content, to avoid + // allocating space for their section headers. + auto isUnused = [this](OutputSection *s) { + if (s == relocSec) + return false; // This section is populated later. + // MergeChunks have zero size at this point, as their size is finalized + // later. Only remove sections that have no Chunks at all. + return s->chunks.empty(); + }; + outputSections.erase( + std::remove_if(outputSections.begin(), outputSections.end(), isUnused), + outputSections.end()); +} + +// The Windows loader doesn't seem to like empty sections, +// so we remove them if any. +void Writer::removeEmptySections() { + auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; + outputSections.erase( + std::remove_if(outputSections.begin(), outputSections.end(), isEmpty), + outputSections.end()); +} + +void Writer::assignOutputSectionIndices() { + // Assign final output section indices, and assign each chunk to its output + // section. + uint32_t idx = 1; + for (OutputSection *os : outputSections) { + os->sectionIndex = idx; + for (Chunk *c : os->chunks) + c->setOutputSectionIdx(idx); + ++idx; + } + + // Merge chunks are containers of chunks, so assign those an output section + // too. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + for (SectionChunk *sc : mc->sections) + if (sc && sc->live) + sc->setOutputSectionIdx(mc->getOutputSectionIdx()); +} + +size_t Writer::addEntryToStringTable(StringRef str) { + assert(str.size() > COFF::NameSize); + size_t offsetOfEntry = strtab.size() + 4; // +4 for the size field + strtab.insert(strtab.end(), str.begin(), str.end()); + strtab.push_back('\0'); + return offsetOfEntry; +} + +Optional Writer::createSymbol(Defined *def) { + coff_symbol16 sym; + switch (def->kind()) { + case Symbol::DefinedAbsoluteKind: + sym.Value = def->getRVA(); + sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + break; + case Symbol::DefinedSyntheticKind: + // Relative symbols are unrepresentable in a COFF symbol table. + return None; + default: { + // Don't write symbols that won't be written to the output to the symbol + // table. + Chunk *c = def->getChunk(); + if (!c) + return None; + OutputSection *os = c->getOutputSection(); + if (!os) + return None; + + sym.Value = def->getRVA() - os->getRVA(); + sym.SectionNumber = os->sectionIndex; + break; + } + } + + // Symbols that are runtime pseudo relocations don't point to the actual + // symbol data itself (as they are imported), but points to the IAT entry + // instead. Avoid emitting them to the symbol table, as they can confuse + // debuggers. + if (def->isRuntimePseudoReloc) + return None; + + StringRef name = def->getName(); + if (name.size() > COFF::NameSize) { + sym.Name.Offset.Zeroes = 0; + sym.Name.Offset.Offset = addEntryToStringTable(name); + } else { + memset(sym.Name.ShortName, 0, COFF::NameSize); + memcpy(sym.Name.ShortName, name.data(), name.size()); + } + + if (auto *d = dyn_cast(def)) { + COFFSymbolRef ref = d->getCOFFSymbol(); + sym.Type = ref.getType(); + sym.StorageClass = ref.getStorageClass(); + } else { + sym.Type = IMAGE_SYM_TYPE_NULL; + sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; + } + sym.NumberOfAuxSymbols = 0; + return sym; +} + +void Writer::createSymbolAndStringTable() { + // PE/COFF images are limited to 8 byte section names. Longer names can be + // supported by writing a non-standard string table, but this string table is + // not mapped at runtime and the long names will therefore be inaccessible. + // link.exe always truncates section names to 8 bytes, whereas binutils always + // preserves long section names via the string table. LLD adopts a hybrid + // solution where discardable sections have long names preserved and + // non-discardable sections have their names truncated, to ensure that any + // section which is mapped at runtime also has its name mapped at runtime. + for (OutputSection *sec : outputSections) { + if (sec->name.size() <= COFF::NameSize) + continue; + if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) + continue; + sec->setStringTableOff(addEntryToStringTable(sec->name)); + } + + if (config->debugDwarf || config->debugSymtab) { + for (ObjFile *file : ObjFile::instances) { + for (Symbol *b : file->getSymbols()) { + auto *d = dyn_cast_or_null(b); + if (!d || d->writtenToSymtab) + continue; + d->writtenToSymtab = true; + + if (Optional sym = createSymbol(d)) + outputSymtab.push_back(*sym); + } + } + } + + if (outputSymtab.empty() && strtab.empty()) + return; + + // We position the symbol table to be adjacent to the end of the last section. + uint64_t fileOff = fileSize; + pointerToSymbolTable = fileOff; + fileOff += outputSymtab.size() * sizeof(coff_symbol16); + fileOff += 4 + strtab.size(); + fileSize = alignTo(fileOff, config->fileAlign); +} + +void Writer::mergeSections() { + if (!pdataSec->chunks.empty()) { + firstPdata = pdataSec->chunks.front(); + lastPdata = pdataSec->chunks.back(); + } + + for (auto &p : config->merge) { + StringRef toName = p.second; + if (p.first == toName) + continue; + StringSet<> names; + while (1) { + if (!names.insert(toName).second) + fatal("/merge: cycle found for section '" + p.first + "'"); + auto i = config->merge.find(toName); + if (i == config->merge.end()) + break; + toName = i->second; + } + OutputSection *from = findSection(p.first); + OutputSection *to = findSection(toName); + if (!from) + continue; + if (!to) { + from->name = toName; + continue; + } + to->merge(from); + } +} + +// Visits all sections to assign incremental, non-overlapping RVAs and +// file offsets. +void Writer::assignAddresses() { + sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + + sizeof(data_directory) * numberOfDataDirectory + + sizeof(coff_section) * outputSections.size(); + sizeOfHeaders += + config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); + sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); + fileSize = sizeOfHeaders; + + // The first page is kept unmapped. + uint64_t rva = alignTo(sizeOfHeaders, config->align); + + for (OutputSection *sec : outputSections) { + if (sec == relocSec) + addBaserels(); + uint64_t rawSize = 0, virtualSize = 0; + sec->header.VirtualAddress = rva; + + // If /FUNCTIONPADMIN is used, functions are padded in order to create a + // hotpatchable image. + const bool isCodeSection = + (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && + (sec->header.Characteristics & IMAGE_SCN_MEM_READ) && + (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE); + uint32_t padding = isCodeSection ? config->functionPadMin : 0; + + for (Chunk *c : sec->chunks) { + if (padding && c->isHotPatchable()) + virtualSize += padding; + virtualSize = alignTo(virtualSize, c->getAlignment()); + c->setRVA(rva + virtualSize); + virtualSize += c->getSize(); + if (c->hasData) + rawSize = alignTo(virtualSize, config->fileAlign); + } + if (virtualSize > UINT32_MAX) + error("section larger than 4 GiB: " + sec->name); + sec->header.VirtualSize = virtualSize; + sec->header.SizeOfRawData = rawSize; + if (rawSize != 0) + sec->header.PointerToRawData = fileSize; + rva += alignTo(virtualSize, config->align); + fileSize += alignTo(rawSize, config->fileAlign); + } + sizeOfImage = alignTo(rva, config->align); + + // Assign addresses to sections in MergeChunks. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + mc->assignSubsectionRVAs(); +} + +template void Writer::writeHeader() { + // Write DOS header. For backwards compatibility, the first part of a PE/COFF + // executable consists of an MS-DOS MZ executable. If the executable is run + // under DOS, that program gets run (usually to just print an error message). + // When run under Windows, the loader looks at AddressOfNewExeHeader and uses + // the PE header instead. + uint8_t *buf = buffer->getBufferStart(); + auto *dos = reinterpret_cast(buf); + buf += sizeof(dos_header); + dos->Magic[0] = 'M'; + dos->Magic[1] = 'Z'; + dos->UsedBytesInTheLastPage = dosStubSize % 512; + dos->FileSizeInPages = divideCeil(dosStubSize, 512); + dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16; + + dos->AddressOfRelocationTable = sizeof(dos_header); + dos->AddressOfNewExeHeader = dosStubSize; + + // Write DOS program. + memcpy(buf, dosProgram, sizeof(dosProgram)); + buf += sizeof(dosProgram); + + // Write PE magic + memcpy(buf, PEMagic, sizeof(PEMagic)); + buf += sizeof(PEMagic); + + // Write COFF header + auto *coff = reinterpret_cast(buf); + buf += sizeof(*coff); + coff->Machine = config->machine; + coff->NumberOfSections = outputSections.size(); + coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; + if (config->largeAddressAware) + coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + if (!config->is64()) + coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE; + if (config->dll) + coff->Characteristics |= IMAGE_FILE_DLL; + if (!config->relocatable) + coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + if (config->swaprunCD) + coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; + if (config->swaprunNet) + coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; + coff->SizeOfOptionalHeader = + sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; + + // Write PE header + auto *pe = reinterpret_cast(buf); + buf += sizeof(*pe); + pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; + + // If {Major,Minor}LinkerVersion is left at 0.0, then for some + // reason signing the resulting PE file with Authenticode produces a + // signature that fails to validate on Windows 7 (but is OK on 10). + // Set it to 14.0, which is what VS2015 outputs, and which avoids + // that problem. + pe->MajorLinkerVersion = 14; + pe->MinorLinkerVersion = 0; + + pe->ImageBase = config->imageBase; + pe->SectionAlignment = config->align; + pe->FileAlignment = config->fileAlign; + pe->MajorImageVersion = config->majorImageVersion; + pe->MinorImageVersion = config->minorImageVersion; + pe->MajorOperatingSystemVersion = config->majorOSVersion; + pe->MinorOperatingSystemVersion = config->minorOSVersion; + pe->MajorSubsystemVersion = config->majorOSVersion; + pe->MinorSubsystemVersion = config->minorOSVersion; + pe->Subsystem = config->subsystem; + pe->SizeOfImage = sizeOfImage; + pe->SizeOfHeaders = sizeOfHeaders; + if (!config->noEntry) { + Defined *entry = cast(config->entry); + pe->AddressOfEntryPoint = entry->getRVA(); + // Pointer to thumb code must have the LSB set, so adjust it. + if (config->machine == ARMNT) + pe->AddressOfEntryPoint |= 1; + } + pe->SizeOfStackReserve = config->stackReserve; + pe->SizeOfStackCommit = config->stackCommit; + pe->SizeOfHeapReserve = config->heapReserve; + pe->SizeOfHeapCommit = config->heapCommit; + if (config->appContainer) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; + if (config->dynamicBase) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; + if (config->highEntropyVA) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; + if (!config->allowBind) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; + if (config->nxCompat) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; + if (!config->allowIsolation) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; + if (config->guardCF != GuardCFLevel::Off) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; + if (config->integrityCheck) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; + if (setNoSEHCharacteristic) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; + if (config->terminalServerAware) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; + pe->NumberOfRvaAndSize = numberOfDataDirectory; + if (textSec->getVirtualSize()) { + pe->BaseOfCode = textSec->getRVA(); + pe->SizeOfCode = textSec->getRawSize(); + } + pe->SizeOfInitializedData = getSizeOfInitializedData(); + + // Write data directory + auto *dir = reinterpret_cast(buf); + buf += sizeof(*dir) * numberOfDataDirectory; + if (!config->exports.empty()) { + dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); + dir[EXPORT_TABLE].Size = edata.getSize(); + } + if (importTableStart) { + dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); + dir[IMPORT_TABLE].Size = importTableSize; + } + if (iatStart) { + dir[IAT].RelativeVirtualAddress = iatStart->getRVA(); + dir[IAT].Size = iatSize; + } + if (rsrcSec->getVirtualSize()) { + dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA(); + dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize(); + } + if (firstPdata) { + dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA(); + dir[EXCEPTION_TABLE].Size = + lastPdata->getRVA() + lastPdata->getSize() - firstPdata->getRVA(); + } + if (relocSec->getVirtualSize()) { + dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); + dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); + } + if (Symbol *sym = symtab->findUnderscore("_tls_used")) { + if (Defined *b = dyn_cast(sym)) { + dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); + dir[TLS_TABLE].Size = config->is64() + ? sizeof(object::coff_tls_directory64) + : sizeof(object::coff_tls_directory32); + } + } + if (debugDirectory) { + dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); + dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); + } + if (Symbol *sym = symtab->findUnderscore("_load_config_used")) { + if (auto *b = dyn_cast(sym)) { + SectionChunk *sc = b->getChunk(); + assert(b->getRVA() >= sc->getRVA()); + uint64_t offsetInChunk = b->getRVA() - sc->getRVA(); + if (!sc->hasData || offsetInChunk + 4 > sc->getSize()) + fatal("_load_config_used is malformed"); + + ArrayRef secContents = sc->getContents(); + uint32_t loadConfigSize = + *reinterpret_cast(&secContents[offsetInChunk]); + if (offsetInChunk + loadConfigSize > sc->getSize()) + fatal("_load_config_used is too large"); + dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA(); + dir[LOAD_CONFIG_TABLE].Size = loadConfigSize; + } + } + if (!delayIdata.empty()) { + dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = + delayIdata.getDirRVA(); + dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize(); + } + + // Write section table + for (OutputSection *sec : outputSections) { + sec->writeHeaderTo(buf); + buf += sizeof(coff_section); + } + sectionTable = ArrayRef( + buf - outputSections.size() * sizeof(coff_section), buf); + + if (outputSymtab.empty() && strtab.empty()) + return; + + coff->PointerToSymbolTable = pointerToSymbolTable; + uint32_t numberOfSymbols = outputSymtab.size(); + coff->NumberOfSymbols = numberOfSymbols; + auto *symbolTable = reinterpret_cast( + buffer->getBufferStart() + coff->PointerToSymbolTable); + for (size_t i = 0; i != numberOfSymbols; ++i) + symbolTable[i] = outputSymtab[i]; + // Create the string table, it follows immediately after the symbol table. + // The first 4 bytes is length including itself. + buf = reinterpret_cast(&symbolTable[numberOfSymbols]); + write32le(buf, strtab.size() + 4); + if (!strtab.empty()) + memcpy(buf + 4, strtab.data(), strtab.size()); +} + +void Writer::openFile(StringRef path) { + buffer = CHECK( + FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable), + "failed to open " + path); +} + +void Writer::createSEHTable() { + SymbolRVASet handlers; + for (ObjFile *file : ObjFile::instances) { + if (!file->hasSafeSEH()) + error("/safeseh: " + file->getName() + " is not compatible with SEH"); + markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); + } + + // Set the "no SEH" characteristic if there really were no handlers, or if + // there is no load config object to point to the table of handlers. + setNoSEHCharacteristic = + handlers.empty() || !symtab->findUnderscore("_load_config_used"); + + maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", + "__safe_se_handler_count"); +} + +// Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set +// cannot contain duplicates. Therefore, the set is uniqued by Chunk and the +// symbol's offset into that Chunk. +static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) { + Chunk *c = s->getChunk(); + if (auto *sc = dyn_cast(c)) + c = sc->repl; // Look through ICF replacement. + uint32_t off = s->getRVA() - (c ? c->getRVA() : 0); + rvaSet.insert({c, off}); +} + +// Given a symbol, add it to the GFIDs table if it is a live, defined, function +// symbol in an executable section. +static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, + Symbol *s) { + if (!s) + return; + + switch (s->kind()) { + case Symbol::DefinedLocalImportKind: + case Symbol::DefinedImportDataKind: + // Defines an __imp_ pointer, so it is data, so it is ignored. + break; + case Symbol::DefinedCommonKind: + // Common is always data, so it is ignored. + break; + case Symbol::DefinedAbsoluteKind: + case Symbol::DefinedSyntheticKind: + // Absolute is never code, synthetic generally isn't and usually isn't + // determinable. + break; + case Symbol::LazyKind: + case Symbol::UndefinedKind: + // Undefined symbols resolve to zero, so they don't have an RVA. Lazy + // symbols shouldn't have relocations. + break; + + case Symbol::DefinedImportThunkKind: + // Thunks are always code, include them. + addSymbolToRVASet(addressTakenSyms, cast(s)); + break; + + case Symbol::DefinedRegularKind: { + // This is a regular, defined, symbol from a COFF file. Mark the symbol as + // address taken if the symbol type is function and it's in an executable + // section. + auto *d = cast(s); + if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) { + SectionChunk *sc = dyn_cast(d->getChunk()); + if (sc && sc->live && + sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) + addSymbolToRVASet(addressTakenSyms, d); + } + break; + } + } +} + +// Visit all relocations from all section contributions of this object file and +// mark the relocation target as address-taken. +static void markSymbolsWithRelocations(ObjFile *file, + SymbolRVASet &usedSymbols) { + for (Chunk *c : file->getChunks()) { + // We only care about live section chunks. Common chunks and other chunks + // don't generally contain relocations. + SectionChunk *sc = dyn_cast(c); + if (!sc || !sc->live) + continue; + + for (const coff_relocation &reloc : sc->getRelocs()) { + if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32) + // Ignore relative relocations on x86. On x86_64 they can't be ignored + // since they're also used to compute absolute addresses. + continue; + + Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex); + maybeAddAddressTakenFunction(usedSymbols, ref); + } + } +} + +// Create the guard function id table. This is a table of RVAs of all +// address-taken functions. It is sorted and uniqued, just like the safe SEH +// table. +void Writer::createGuardCFTables() { + SymbolRVASet addressTakenSyms; + SymbolRVASet longJmpTargets; + for (ObjFile *file : ObjFile::instances) { + // If the object was compiled with /guard:cf, the address taken symbols + // are in .gfids$y sections, and the longjmp targets are in .gljmp$y + // sections. If the object was not compiled with /guard:cf, we assume there + // were no setjmp targets, and that all code symbols with relocations are + // possibly address-taken. + if (file->hasGuardCF()) { + markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); + markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); + } else { + markSymbolsWithRelocations(file, addressTakenSyms); + } + } + + // Mark the image entry as address-taken. + if (config->entry) + maybeAddAddressTakenFunction(addressTakenSyms, config->entry); + + // Mark exported symbols in executable sections as address-taken. + for (Export &e : config->exports) + maybeAddAddressTakenFunction(addressTakenSyms, e.sym); + + // Ensure sections referenced in the gfid table are 16-byte aligned. + for (const ChunkAndOffset &c : addressTakenSyms) + if (c.inputChunk->getAlignment() < 16) + c.inputChunk->setAlignment(16); + + maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table", + "__guard_fids_count"); + + // Add the longjmp target table unless the user told us not to. + if (config->guardCF == GuardCFLevel::Full) + maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", + "__guard_longjmp_count"); + + // Set __guard_flags, which will be used in the load config to indicate that + // /guard:cf was enabled. + uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | + uint32_t(coff_guard_flags::HasFidTable); + if (config->guardCF == GuardCFLevel::Full) + guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + Symbol *flagSym = symtab->findUnderscore("__guard_flags"); + cast(flagSym)->setVA(guardFlags); +} + +// Take a list of input sections containing symbol table indices and add those +// symbols to an RVA table. The challenge is that symbol RVAs are not known and +// depend on the table size, so we can't directly build a set of integers. +void Writer::markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols) { + for (SectionChunk *c : symIdxChunks) { + // Skip sections discarded by linker GC. This comes up when a .gfids section + // is associated with something like a vtable and the vtable is discarded. + // In this case, the associated gfids section is discarded, and we don't + // mark the virtual member functions as address-taken by the vtable. + if (!c->live) + continue; + + // Validate that the contents look like symbol table indices. + ArrayRef data = c->getContents(); + if (data.size() % 4 != 0) { + warn("ignoring " + c->getSectionName() + + " symbol table index section in object " + toString(file)); + continue; + } + + // Read each symbol table index and check if that symbol was included in the + // final link. If so, add it to the table symbol set. + ArrayRef symIndices( + reinterpret_cast(data.data()), data.size() / 4); + ArrayRef objSymbols = file->getSymbols(); + for (uint32_t symIndex : symIndices) { + if (symIndex >= objSymbols.size()) { + warn("ignoring invalid symbol table index in section " + + c->getSectionName() + " in object " + toString(file)); + continue; + } + if (Symbol *s = objSymbols[symIndex]) { + if (s->isLive()) + addSymbolToRVASet(tableSymbols, cast(s)); + } + } + } +} + +// Replace the absolute table symbol with a synthetic symbol pointing to +// tableChunk so that we can emit base relocations for it and resolve section +// relative relocations. +void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym) { + if (tableSymbols.empty()) + return; + + RVATableChunk *tableChunk = make(std::move(tableSymbols)); + rdataSec->addChunk(tableChunk); + + Symbol *t = symtab->findUnderscore(tableSym); + Symbol *c = symtab->findUnderscore(countSym); + replaceSymbol(t, t->getName(), tableChunk); + cast(c)->setVA(tableChunk->getSize() / 4); +} + +// MinGW specific. Gather all relocations that are imported from a DLL even +// though the code didn't expect it to, produce the table that the runtime +// uses for fixing them up, and provide the synthetic symbols that the +// runtime uses for finding the table. +void Writer::createRuntimePseudoRelocs() { + std::vector rels; + + for (Chunk *c : symtab->getChunks()) { + auto *sc = dyn_cast(c); + if (!sc || !sc->live) + continue; + sc->getRuntimePseudoRelocs(rels); + } + + if (!rels.empty()) + log("Writing " + Twine(rels.size()) + " runtime pseudo relocations"); + PseudoRelocTableChunk *table = make(rels); + rdataSec->addChunk(table); + EmptyChunk *endOfList = make(); + rdataSec->addChunk(endOfList); + + Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); + Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); + replaceSymbol(headSym, headSym->getName(), table); + replaceSymbol(endSym, endSym->getName(), endOfList); +} + +// MinGW specific. +// The MinGW .ctors and .dtors lists have sentinels at each end; +// a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. +// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ +// and __DTOR_LIST__ respectively. +void Writer::insertCtorDtorSymbols() { + AbsolutePointerChunk *ctorListHead = make(-1); + AbsolutePointerChunk *ctorListEnd = make(0); + AbsolutePointerChunk *dtorListHead = make(-1); + AbsolutePointerChunk *dtorListEnd = make(0); + ctorsSec->insertChunkAtStart(ctorListHead); + ctorsSec->addChunk(ctorListEnd); + dtorsSec->insertChunkAtStart(dtorListHead); + dtorsSec->addChunk(dtorListEnd); + + Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__"); + Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__"); + replaceSymbol(ctorListSym, ctorListSym->getName(), + ctorListHead); + replaceSymbol(dtorListSym, dtorListSym->getName(), + dtorListHead); +} + +// Handles /section options to allow users to overwrite +// section attributes. +void Writer::setSectionPermissions() { + for (auto &p : config->section) { + StringRef name = p.first; + uint32_t perm = p.second; + for (OutputSection *sec : outputSections) + if (sec->name == name) + sec->setPermissions(perm); + } +} + +// Write section contents to a mmap'ed file. +void Writer::writeSections() { + // Record the number of sections to apply section index relocations + // against absolute symbols. See applySecIdx in Chunks.cpp.. + DefinedAbsolute::numOutputSections = outputSections.size(); + + uint8_t *buf = buffer->getBufferStart(); + for (OutputSection *sec : outputSections) { + uint8_t *secBuf = buf + sec->getFileOff(); + // Fill gaps between functions in .text with INT3 instructions + // instead of leaving as NUL bytes (which can be interpreted as + // ADD instructions). + if (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) + memset(secBuf, 0xCC, sec->getRawSize()); + parallelForEach(sec->chunks, [&](Chunk *c) { + c->writeTo(secBuf + c->getRVA() - sec->getRVA()); + }); + } +} + +void Writer::writeBuildId() { + // There are two important parts to the build ID. + // 1) If building with debug info, the COFF debug directory contains a + // timestamp as well as a Guid and Age of the PDB. + // 2) In all cases, the PE COFF file header also contains a timestamp. + // For reproducibility, instead of a timestamp we want to use a hash of the + // PE contents. + if (config->debug) { + assert(buildId && "BuildId is not set!"); + // BuildId->BuildId was filled in when the PDB was written. + } + + // At this point the only fields in the COFF file which remain unset are the + // "timestamp" in the COFF file header, and the ones in the coff debug + // directory. Now we can hash the file and write that hash to the various + // timestamp fields in the file. + StringRef outputFileData( + reinterpret_cast(buffer->getBufferStart()), + buffer->getBufferSize()); + + uint32_t timestamp = config->timestamp; + uint64_t hash = 0; + bool generateSyntheticBuildId = + config->mingw && config->debug && config->pdbPath.empty(); + + if (config->repro || generateSyntheticBuildId) + hash = xxHash64(outputFileData); + + if (config->repro) + timestamp = static_cast(hash); + + if (generateSyntheticBuildId) { + // For MinGW builds without a PDB file, we still generate a build id + // to allow associating a crash dump to the executable. + buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70; + buildId->buildId->PDB70.Age = 1; + memcpy(buildId->buildId->PDB70.Signature, &hash, 8); + // xxhash only gives us 8 bytes, so put some fixed data in the other half. + memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8); + } + + if (debugDirectory) + debugDirectory->setTimeDateStamp(timestamp); + + uint8_t *buf = buffer->getBufferStart(); + buf += dosStubSize + sizeof(PEMagic); + object::coff_file_header *coffHeader = + reinterpret_cast(buf); + coffHeader->TimeDateStamp = timestamp; +} + +// Sort .pdata section contents according to PE/COFF spec 5.5. +void Writer::sortExceptionTable() { + if (!firstPdata) + return; + // We assume .pdata contains function table entries only. + auto bufAddr = [&](Chunk *c) { + OutputSection *os = c->getOutputSection(); + return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - + os->getRVA(); + }; + uint8_t *begin = bufAddr(firstPdata); + uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); + if (config->machine == AMD64) { + struct Entry { ulittle32_t begin, end, unwind; }; + parallelSort( + MutableArrayRef((Entry *)begin, (Entry *)end), + [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); + return; + } + if (config->machine == ARMNT || config->machine == ARM64) { + struct Entry { ulittle32_t begin, unwind; }; + parallelSort( + MutableArrayRef((Entry *)begin, (Entry *)end), + [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); + return; + } + errs() << "warning: don't know how to handle .pdata.\n"; +} + +// The CRT section contains, among other things, the array of function +// pointers that initialize every global variable that is not trivially +// constructed. The CRT calls them one after the other prior to invoking +// main(). +// +// As per C++ spec, 3.6.2/2.3, +// "Variables with ordered initialization defined within a single +// translation unit shall be initialized in the order of their definitions +// in the translation unit" +// +// It is therefore critical to sort the chunks containing the function +// pointers in the order that they are listed in the object file (top to +// bottom), otherwise global objects might not be initialized in the +// correct order. +void Writer::sortCRTSectionChunks(std::vector &chunks) { + auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) { + auto sa = dyn_cast(a); + auto sb = dyn_cast(b); + assert(sa && sb && "Non-section chunks in CRT section!"); + + StringRef sAObj = sa->file->mb.getBufferIdentifier(); + StringRef sBObj = sb->file->mb.getBufferIdentifier(); + + return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber(); + }; + llvm::stable_sort(chunks, sectionChunkOrder); + + if (config->verbose) { + for (auto &c : chunks) { + auto sc = dyn_cast(c); + log(" " + sc->file->mb.getBufferIdentifier().str() + + ", SectionID: " + Twine(sc->getSectionNumber())); + } + } +} + +OutputSection *Writer::findSection(StringRef name) { + for (OutputSection *sec : outputSections) + if (sec->name == name) + return sec; + return nullptr; +} + +uint32_t Writer::getSizeOfInitializedData() { + uint32_t res = 0; + for (OutputSection *s : outputSections) + if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + res += s->getRawSize(); + return res; +} + +// Add base relocations to .reloc section. +void Writer::addBaserels() { + if (!config->relocatable) + return; + relocSec->chunks.clear(); + std::vector v; + for (OutputSection *sec : outputSections) { + if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + continue; + // Collect all locations for base relocations. + for (Chunk *c : sec->chunks) + c->getBaserels(&v); + // Add the addresses to .reloc section. + if (!v.empty()) + addBaserelBlocks(v); + v.clear(); + } +} + +// Add addresses to .reloc section. Note that addresses are grouped by page. +void Writer::addBaserelBlocks(std::vector &v) { + const uint32_t mask = ~uint32_t(pageSize - 1); + uint32_t page = v[0].rva & mask; + size_t i = 0, j = 1; + for (size_t e = v.size(); j < e; ++j) { + uint32_t p = v[j].rva & mask; + if (p == page) + continue; + relocSec->addChunk(make(page, &v[i], &v[0] + j)); + i = j; + page = p; + } + if (i == j) + return; + relocSec->addChunk(make(page, &v[i], &v[0] + j)); +} + +PartialSection *Writer::createPartialSection(StringRef name, + uint32_t outChars) { + PartialSection *&pSec = partialSections[{name, outChars}]; + if (pSec) + return pSec; + pSec = make(name, outChars); + return pSec; +} + +PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { + auto it = partialSections.find({name, outChars}); + if (it != partialSections.end()) + return it->second; + return nullptr; +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/Writer.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/CMakeLists.txt (revision 352529) @@ -0,0 +1,48 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(COFFOptionsTableGen) + +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lld_library(lldCOFF + Chunks.cpp + DebugTypes.cpp + DLL.cpp + Driver.cpp + DriverUtils.cpp + ICF.cpp + InputFiles.cpp + LTO.cpp + MapFile.cpp + MarkLive.cpp + MinGW.cpp + PDB.cpp + SymbolTable.cpp + Symbols.cpp + Writer.cpp + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + BinaryFormat + Core + DebugInfoCodeView + DebugInfoMSF + DebugInfoPDB + LibDriver + LTO + MC + Object + Option + Support + WindowsManifest + + LINK_LIBS + lldCommon + ${LLVM_PTHREAD_LIB} + + DEPENDS + COFFOptionsTableGen + ${tablegen_deps} + ) Property changes on: vendor/lld/lld-release_900-r372316/COFF/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Chunks.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Chunks.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Chunks.cpp (revision 352529) @@ -0,0 +1,922 @@ +//===- Chunks.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Chunks.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "Writer.h" +#include "SymbolTable.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::COFF; +using llvm::support::ulittle32_t; + +namespace lld { +namespace coff { + +SectionChunk::SectionChunk(ObjFile *f, const coff_section *h) + : Chunk(SectionKind), file(f), header(h), repl(this) { + // Initialize relocs. + setRelocs(file->getCOFFObj()->getRelocations(header)); + + // Initialize sectionName. + StringRef sectionName; + if (Expected e = file->getCOFFObj()->getSectionName(header)) + sectionName = *e; + sectionNameData = sectionName.data(); + sectionNameSize = sectionName.size(); + + setAlignment(header->getAlignment()); + + hasData = !(header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); + + // If linker GC is disabled, every chunk starts out alive. If linker GC is + // enabled, treat non-comdat sections as roots. Generally optimized object + // files will be built with -ffunction-sections or /Gy, so most things worth + // stripping will be in a comdat. + live = !config->doGC || !isCOMDAT(); +} + +// SectionChunk is one of the most frequently allocated classes, so it is +// important to keep it as compact as possible. As of this writing, the number +// below is the size of this class on x64 platforms. +static_assert(sizeof(SectionChunk) <= 88, "SectionChunk grew unexpectedly"); + +static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); } +static void add32(uint8_t *p, int32_t v) { write32le(p, read32le(p) + v); } +static void add64(uint8_t *p, int64_t v) { write64le(p, read64le(p) + v); } +static void or16(uint8_t *p, uint16_t v) { write16le(p, read16le(p) | v); } +static void or32(uint8_t *p, uint32_t v) { write32le(p, read32le(p) | v); } + +// Verify that given sections are appropriate targets for SECREL +// relocations. This check is relaxed because unfortunately debug +// sections have section-relative relocations against absolute symbols. +static bool checkSecRel(const SectionChunk *sec, OutputSection *os) { + if (os) + return true; + if (sec->isCodeView()) + return false; + error("SECREL relocation cannot be applied to absolute symbols"); + return false; +} + +static void applySecRel(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (!checkSecRel(sec, os)) + return; + uint64_t secRel = s - os->getRVA(); + if (secRel > UINT32_MAX) { + error("overflow in SECREL relocation in section: " + sec->getSectionName()); + return; + } + add32(off, secRel); +} + +static void applySecIdx(uint8_t *off, OutputSection *os) { + // Absolute symbol doesn't have section index, but section index relocation + // against absolute symbol should be resolved to one plus the last output + // section index. This is required for compatibility with MSVC. + if (os) + add16(off, os->sectionIndex); + else + add16(off, DefinedAbsolute::numOutputSections + 1); +} + +void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + switch (type) { + case IMAGE_REL_AMD64_ADDR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_AMD64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break; + case IMAGE_REL_AMD64_REL32: add32(off, s - p - 4); break; + case IMAGE_REL_AMD64_REL32_1: add32(off, s - p - 5); break; + case IMAGE_REL_AMD64_REL32_2: add32(off, s - p - 6); break; + case IMAGE_REL_AMD64_REL32_3: add32(off, s - p - 7); break; + case IMAGE_REL_AMD64_REL32_4: add32(off, s - p - 8); break; + case IMAGE_REL_AMD64_REL32_5: add32(off, s - p - 9); break; + case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break; + default: + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); + } +} + +void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + switch (type) { + case IMAGE_REL_I386_ABSOLUTE: break; + case IMAGE_REL_I386_DIR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_I386_DIR32NB: add32(off, s); break; + case IMAGE_REL_I386_REL32: add32(off, s - p - 4); break; + case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break; + default: + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); + } +} + +static void applyMOV(uint8_t *off, uint16_t v) { + write16le(off, (read16le(off) & 0xfbf0) | ((v & 0x800) >> 1) | ((v >> 12) & 0xf)); + write16le(off + 2, (read16le(off + 2) & 0x8f00) | ((v & 0x700) << 4) | (v & 0xff)); +} + +static uint16_t readMOV(uint8_t *off, bool movt) { + uint16_t op1 = read16le(off); + if ((op1 & 0xfbf0) != (movt ? 0xf2c0 : 0xf240)) + error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") + + " instruction in MOV32T relocation"); + uint16_t op2 = read16le(off + 2); + if ((op2 & 0x8000) != 0) + error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") + + " instruction in MOV32T relocation"); + return (op2 & 0x00ff) | ((op2 >> 4) & 0x0700) | ((op1 << 1) & 0x0800) | + ((op1 & 0x000f) << 12); +} + +void applyMOV32T(uint8_t *off, uint32_t v) { + uint16_t immW = readMOV(off, false); // read MOVW operand + uint16_t immT = readMOV(off + 4, true); // read MOVT operand + uint32_t imm = immW | (immT << 16); + v += imm; // add the immediate offset + applyMOV(off, v); // set MOVW operand + applyMOV(off + 4, v >> 16); // set MOVT operand +} + +static void applyBranch20T(uint8_t *off, int32_t v) { + if (!isInt<21>(v)) + error("relocation out of range"); + uint32_t s = v < 0 ? 1 : 0; + uint32_t j1 = (v >> 19) & 1; + uint32_t j2 = (v >> 18) & 1; + or16(off, (s << 10) | ((v >> 12) & 0x3f)); + or16(off + 2, (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff)); +} + +void applyBranch24T(uint8_t *off, int32_t v) { + if (!isInt<25>(v)) + error("relocation out of range"); + uint32_t s = v < 0 ? 1 : 0; + uint32_t j1 = ((~v >> 23) & 1) ^ s; + uint32_t j2 = ((~v >> 22) & 1) ^ s; + or16(off, (s << 10) | ((v >> 12) & 0x3ff)); + // Clear out the J1 and J2 bits which may be set. + write16le(off + 2, (read16le(off + 2) & 0xd000) | (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff)); +} + +void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + // Pointer to thumb code must have the LSB set. + uint64_t sx = s; + if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE)) + sx |= 1; + switch (type) { + case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break; + case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break; + case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break; + case IMAGE_REL_ARM_BLX23T: applyBranch24T(off, sx - p - 4); break; + case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break; + case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break; + default: + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); + } +} + +// Interpret the existing immediate value as a byte offset to the +// target symbol, then update the instruction with the immediate as +// the page offset from the current instruction to the target. +void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift) { + uint32_t orig = read32le(off); + uint64_t imm = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC); + s += imm; + imm = (s >> shift) - (p >> shift); + uint32_t immLo = (imm & 0x3) << 29; + uint32_t immHi = (imm & 0x1FFFFC) << 3; + uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); + write32le(off, (orig & ~mask) | immLo | immHi); +} + +// Update the immediate field in a AARCH64 ldr, str, and add instruction. +// Optionally limit the range of the written immediate by one or more bits +// (rangeLimit). +void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit) { + uint32_t orig = read32le(off); + imm += (orig >> 10) & 0xFFF; + orig &= ~(0xFFF << 10); + write32le(off, orig | ((imm & (0xFFF >> rangeLimit)) << 10)); +} + +// Add the 12 bit page offset to the existing immediate. +// Ldr/str instructions store the opcode immediate scaled +// by the load/store size (giving a larger range for larger +// loads/stores). The immediate is always (both before and after +// fixing up the relocation) stored scaled similarly. +// Even if larger loads/stores have a larger range, limit the +// effective offset to 12 bit, since it is intended to be a +// page offset. +static void applyArm64Ldr(uint8_t *off, uint64_t imm) { + uint32_t orig = read32le(off); + uint32_t size = orig >> 30; + // 0x04000000 indicates SIMD/FP registers + // 0x00800000 indicates 128 bit + if ((orig & 0x4800000) == 0x4800000) + size += 4; + if ((imm & ((1 << size) - 1)) != 0) + error("misaligned ldr/str offset"); + applyArm64Imm(off, imm >> size, size); +} + +static void applySecRelLow12A(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (checkSecRel(sec, os)) + applyArm64Imm(off, (s - os->getRVA()) & 0xfff, 0); +} + +static void applySecRelHigh12A(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (!checkSecRel(sec, os)) + return; + uint64_t secRel = (s - os->getRVA()) >> 12; + if (0xfff < secRel) { + error("overflow in SECREL_HIGH12A relocation in section: " + + sec->getSectionName()); + return; + } + applyArm64Imm(off, secRel & 0xfff, 0); +} + +static void applySecRelLdr(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (checkSecRel(sec, os)) + applyArm64Ldr(off, (s - os->getRVA()) & 0xfff); +} + +void applyArm64Branch26(uint8_t *off, int64_t v) { + if (!isInt<28>(v)) + error("relocation out of range"); + or32(off, (v & 0x0FFFFFFC) >> 2); +} + +static void applyArm64Branch19(uint8_t *off, int64_t v) { + if (!isInt<21>(v)) + error("relocation out of range"); + or32(off, (v & 0x001FFFFC) << 3); +} + +static void applyArm64Branch14(uint8_t *off, int64_t v) { + if (!isInt<16>(v)) + error("relocation out of range"); + or32(off, (v & 0x0000FFFC) << 3); +} + +void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + switch (type) { + case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break; + case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break; + case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(off, s & 0xfff, 0); break; + case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(off, s & 0xfff); break; + case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(off, s - p); break; + case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(off, s - p); break; + case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(off, s - p); break; + case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break; + case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break; + case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, off, os, s); break; + case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break; + case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, off, os, s); break; + case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break; + default: + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); + } +} + +static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk, + Defined *sym, + const coff_relocation &rel) { + // Don't report these errors when the relocation comes from a debug info + // section or in mingw mode. MinGW mode object files (built by GCC) can + // have leftover sections with relocations against discarded comdat + // sections. Such sections are left as is, with relocations untouched. + if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw) + return; + + // Get the name of the symbol. If it's null, it was discarded early, so we + // have to go back to the object file. + ObjFile *file = fromChunk->file; + StringRef name; + if (sym) { + name = sym->getName(); + } else { + COFFSymbolRef coffSym = + check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex)); + file->getCOFFObj()->getSymbolName(coffSym, name); + } + + std::vector symbolLocations = + getSymbolLocations(file, rel.SymbolTableIndex); + + std::string out; + llvm::raw_string_ostream os(out); + os << "relocation against symbol in discarded section: " + name; + for (const std::string &s : symbolLocations) + os << s; + error(os.str()); +} + +void SectionChunk::writeTo(uint8_t *buf) const { + if (!hasData) + return; + // Copy section contents from source object file to output file. + ArrayRef a = getContents(); + if (!a.empty()) + memcpy(buf, a.data(), a.size()); + + // Apply relocations. + size_t inputSize = getSize(); + for (size_t i = 0, e = relocsSize; i < e; i++) { + const coff_relocation &rel = relocsData[i]; + + // Check for an invalid relocation offset. This check isn't perfect, because + // we don't have the relocation size, which is only known after checking the + // machine and relocation type. As a result, a relocation may overwrite the + // beginning of the following input section. + if (rel.VirtualAddress >= inputSize) { + error("relocation points beyond the end of its parent section"); + continue; + } + + uint8_t *off = buf + rel.VirtualAddress; + + auto *sym = + dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); + + // Get the output section of the symbol for this relocation. The output + // section is needed to compute SECREL and SECTION relocations used in debug + // info. + Chunk *c = sym ? sym->getChunk() : nullptr; + OutputSection *os = c ? c->getOutputSection() : nullptr; + + // Skip the relocation if it refers to a discarded section, and diagnose it + // as an error if appropriate. If a symbol was discarded early, it may be + // null. If it was discarded late, the output section will be null, unless + // it was an absolute or synthetic symbol. + if (!sym || + (!os && !isa(sym) && !isa(sym))) { + maybeReportRelocationToDiscarded(this, sym, rel); + continue; + } + + uint64_t s = sym->getRVA(); + + // Compute the RVA of the relocation for relative relocations. + uint64_t p = rva + rel.VirtualAddress; + switch (config->machine) { + case AMD64: + applyRelX64(off, rel.Type, os, s, p); + break; + case I386: + applyRelX86(off, rel.Type, os, s, p); + break; + case ARMNT: + applyRelARM(off, rel.Type, os, s, p); + break; + case ARM64: + applyRelARM64(off, rel.Type, os, s, p); + break; + default: + llvm_unreachable("unknown machine type"); + } + } +} + +void SectionChunk::addAssociative(SectionChunk *child) { + // Insert this child at the head of the list. + assert(child->assocChildren == nullptr && + "associated sections cannot have their own associated children"); + child->assocChildren = assocChildren; + assocChildren = child; +} + +static uint8_t getBaserelType(const coff_relocation &rel) { + switch (config->machine) { + case AMD64: + if (rel.Type == IMAGE_REL_AMD64_ADDR64) + return IMAGE_REL_BASED_DIR64; + return IMAGE_REL_BASED_ABSOLUTE; + case I386: + if (rel.Type == IMAGE_REL_I386_DIR32) + return IMAGE_REL_BASED_HIGHLOW; + return IMAGE_REL_BASED_ABSOLUTE; + case ARMNT: + if (rel.Type == IMAGE_REL_ARM_ADDR32) + return IMAGE_REL_BASED_HIGHLOW; + if (rel.Type == IMAGE_REL_ARM_MOV32T) + return IMAGE_REL_BASED_ARM_MOV32T; + return IMAGE_REL_BASED_ABSOLUTE; + case ARM64: + if (rel.Type == IMAGE_REL_ARM64_ADDR64) + return IMAGE_REL_BASED_DIR64; + return IMAGE_REL_BASED_ABSOLUTE; + default: + llvm_unreachable("unknown machine type"); + } +} + +// Windows-specific. +// Collect all locations that contain absolute addresses, which need to be +// fixed by the loader if load-time relocation is needed. +// Only called when base relocation is enabled. +void SectionChunk::getBaserels(std::vector *res) { + for (size_t i = 0, e = relocsSize; i < e; i++) { + const coff_relocation &rel = relocsData[i]; + uint8_t ty = getBaserelType(rel); + if (ty == IMAGE_REL_BASED_ABSOLUTE) + continue; + Symbol *target = file->getSymbol(rel.SymbolTableIndex); + if (!target || isa(target)) + continue; + res->emplace_back(rva + rel.VirtualAddress, ty); + } +} + +// MinGW specific. +// Check whether a static relocation of type Type can be deferred and +// handled at runtime as a pseudo relocation (for references to a module +// local variable, which turned out to actually need to be imported from +// another DLL) This returns the size the relocation is supposed to update, +// in bits, or 0 if the relocation cannot be handled as a runtime pseudo +// relocation. +static int getRuntimePseudoRelocSize(uint16_t type) { + // Relocations that either contain an absolute address, or a plain + // relative offset, since the runtime pseudo reloc implementation + // adds 8/16/32/64 bit values to a memory address. + // + // Given a pseudo relocation entry, + // + // typedef struct { + // DWORD sym; + // DWORD target; + // DWORD flags; + // } runtime_pseudo_reloc_item_v2; + // + // the runtime relocation performs this adjustment: + // *(base + .target) += *(base + .sym) - (base + .sym) + // + // This works for both absolute addresses (IMAGE_REL_*_ADDR32/64, + // IMAGE_REL_I386_DIR32, where the memory location initially contains + // the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32), + // where the memory location originally contains the relative offset to the + // IAT slot. + // + // This requires the target address to be writable, either directly out of + // the image, or temporarily changed at runtime with VirtualProtect. + // Since this only operates on direct address values, it doesn't work for + // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations. + switch (config->machine) { + case AMD64: + switch (type) { + case IMAGE_REL_AMD64_ADDR64: + return 64; + case IMAGE_REL_AMD64_ADDR32: + case IMAGE_REL_AMD64_REL32: + case IMAGE_REL_AMD64_REL32_1: + case IMAGE_REL_AMD64_REL32_2: + case IMAGE_REL_AMD64_REL32_3: + case IMAGE_REL_AMD64_REL32_4: + case IMAGE_REL_AMD64_REL32_5: + return 32; + default: + return 0; + } + case I386: + switch (type) { + case IMAGE_REL_I386_DIR32: + case IMAGE_REL_I386_REL32: + return 32; + default: + return 0; + } + case ARMNT: + switch (type) { + case IMAGE_REL_ARM_ADDR32: + return 32; + default: + return 0; + } + case ARM64: + switch (type) { + case IMAGE_REL_ARM64_ADDR64: + return 64; + case IMAGE_REL_ARM64_ADDR32: + return 32; + default: + return 0; + } + default: + llvm_unreachable("unknown machine type"); + } +} + +// MinGW specific. +// Append information to the provided vector about all relocations that +// need to be handled at runtime as runtime pseudo relocations (references +// to a module local variable, which turned out to actually need to be +// imported from another DLL). +void SectionChunk::getRuntimePseudoRelocs( + std::vector &res) { + for (const coff_relocation &rel : getRelocs()) { + auto *target = + dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); + if (!target || !target->isRuntimePseudoReloc) + continue; + int sizeInBits = getRuntimePseudoRelocSize(rel.Type); + if (sizeInBits == 0) { + error("unable to automatically import from " + target->getName() + + " with relocation type " + + file->getCOFFObj()->getRelocationTypeName(rel.Type) + " in " + + toString(file)); + continue; + } + // sizeInBits is used to initialize the Flags field; currently no + // other flags are defined. + res.emplace_back( + RuntimePseudoReloc(target, this, rel.VirtualAddress, sizeInBits)); + } +} + +bool SectionChunk::isCOMDAT() const { + return header->Characteristics & IMAGE_SCN_LNK_COMDAT; +} + +void SectionChunk::printDiscardedMessage() const { + // Removed by dead-stripping. If it's removed by ICF, ICF already + // printed out the name, so don't repeat that here. + if (sym && this == repl) + message("Discarded " + sym->getName()); +} + +StringRef SectionChunk::getDebugName() const { + if (sym) + return sym->getName(); + return ""; +} + +ArrayRef SectionChunk::getContents() const { + ArrayRef a; + cantFail(file->getCOFFObj()->getSectionContents(header, a)); + return a; +} + +ArrayRef SectionChunk::consumeDebugMagic() { + assert(isCodeView()); + return consumeDebugMagic(getContents(), getSectionName()); +} + +ArrayRef SectionChunk::consumeDebugMagic(ArrayRef data, + StringRef sectionName) { + if (data.empty()) + return {}; + + // First 4 bytes are section magic. + if (data.size() < 4) + fatal("the section is too short: " + sectionName); + + if (!sectionName.startswith(".debug$")) + fatal("invalid section: " + sectionName); + + uint32_t magic = support::endian::read32le(data.data()); + uint32_t expectedMagic = sectionName == ".debug$H" + ? DEBUG_HASHES_SECTION_MAGIC + : DEBUG_SECTION_MAGIC; + if (magic != expectedMagic) { + warn("ignoring section " + sectionName + " with unrecognized magic 0x" + + utohexstr(magic)); + return {}; + } + return data.slice(4); +} + +SectionChunk *SectionChunk::findByName(ArrayRef sections, + StringRef name) { + for (SectionChunk *c : sections) + if (c->getSectionName() == name) + return c; + return nullptr; +} + +void SectionChunk::replace(SectionChunk *other) { + p2Align = std::max(p2Align, other->p2Align); + other->repl = repl; + other->live = false; +} + +uint32_t SectionChunk::getSectionNumber() const { + DataRefImpl r; + r.p = reinterpret_cast(header); + SectionRef s(r, file->getCOFFObj()); + return s.getIndex() + 1; +} + +CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) { + // The value of a common symbol is its size. Align all common symbols smaller + // than 32 bytes naturally, i.e. round the size up to the next power of two. + // This is what MSVC link.exe does. + setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(sym.getValue())))); + hasData = false; +} + +uint32_t CommonChunk::getOutputCharacteristics() const { + return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE; +} + +void StringChunk::writeTo(uint8_t *buf) const { + memcpy(buf, str.data(), str.size()); + buf[str.size()] = '\0'; +} + +ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) { + // Intel Optimization Manual says that all branch targets + // should be 16-byte aligned. MSVC linker does this too. + setAlignment(16); +} + +void ImportThunkChunkX64::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkX86, sizeof(importThunkX86)); + // The first two bytes is a JMP instruction. Fill its operand. + write32le(buf + 2, impSymbol->getRVA() - rva - getSize()); +} + +void ImportThunkChunkX86::getBaserels(std::vector *res) { + res->emplace_back(getRVA() + 2); +} + +void ImportThunkChunkX86::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkX86, sizeof(importThunkX86)); + // The first two bytes is a JMP instruction. Fill its operand. + write32le(buf + 2, + impSymbol->getRVA() + config->imageBase); +} + +void ImportThunkChunkARM::getBaserels(std::vector *res) { + res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T); +} + +void ImportThunkChunkARM::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkARM, sizeof(importThunkARM)); + // Fix mov.w and mov.t operands. + applyMOV32T(buf, impSymbol->getRVA() + config->imageBase); +} + +void ImportThunkChunkARM64::writeTo(uint8_t *buf) const { + int64_t off = impSymbol->getRVA() & 0xfff; + memcpy(buf, importThunkARM64, sizeof(importThunkARM64)); + applyArm64Addr(buf, impSymbol->getRVA(), rva, 12); + applyArm64Ldr(buf + 4, off); +} + +// A Thumb2, PIC, non-interworking range extension thunk. +const uint8_t armThunk[] = { + 0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) + 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4) + 0xe7, 0x44, // L1: add pc, ip +}; + +size_t RangeExtensionThunkARM::getSize() const { + assert(config->machine == ARMNT); + return sizeof(armThunk); +} + +void RangeExtensionThunkARM::writeTo(uint8_t *buf) const { + assert(config->machine == ARMNT); + uint64_t offset = target->getRVA() - rva - 12; + memcpy(buf, armThunk, sizeof(armThunk)); + applyMOV32T(buf, uint32_t(offset)); +} + +// A position independent ARM64 adrp+add thunk, with a maximum range of +// +/- 4 GB, which is enough for any PE-COFF. +const uint8_t arm64Thunk[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest + 0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest + 0x00, 0x02, 0x1f, 0xd6, // br x16 +}; + +size_t RangeExtensionThunkARM64::getSize() const { + assert(config->machine == ARM64); + return sizeof(arm64Thunk); +} + +void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const { + assert(config->machine == ARM64); + memcpy(buf, arm64Thunk, sizeof(arm64Thunk)); + applyArm64Addr(buf + 0, target->getRVA(), rva, 12); + applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0); +} + +void LocalImportChunk::getBaserels(std::vector *res) { + res->emplace_back(getRVA()); +} + +size_t LocalImportChunk::getSize() const { return config->wordsize; } + +void LocalImportChunk::writeTo(uint8_t *buf) const { + if (config->is64()) { + write64le(buf, sym->getRVA() + config->imageBase); + } else { + write32le(buf, sym->getRVA() + config->imageBase); + } +} + +void RVATableChunk::writeTo(uint8_t *buf) const { + ulittle32_t *begin = reinterpret_cast(buf); + size_t cnt = 0; + for (const ChunkAndOffset &co : syms) + begin[cnt++] = co.inputChunk->getRVA() + co.offset; + std::sort(begin, begin + cnt); + assert(std::unique(begin, begin + cnt) == begin + cnt && + "RVA tables should be de-duplicated"); +} + +// MinGW specific, for the "automatic import of variables from DLLs" feature. +size_t PseudoRelocTableChunk::getSize() const { + if (relocs.empty()) + return 0; + return 12 + 12 * relocs.size(); +} + +// MinGW specific. +void PseudoRelocTableChunk::writeTo(uint8_t *buf) const { + if (relocs.empty()) + return; + + ulittle32_t *table = reinterpret_cast(buf); + // This is the list header, to signal the runtime pseudo relocation v2 + // format. + table[0] = 0; + table[1] = 0; + table[2] = 1; + + size_t idx = 3; + for (const RuntimePseudoReloc &rpr : relocs) { + table[idx + 0] = rpr.sym->getRVA(); + table[idx + 1] = rpr.target->getRVA() + rpr.targetOffset; + table[idx + 2] = rpr.flags; + idx += 3; + } +} + +// Windows-specific. This class represents a block in .reloc section. +// The format is described here. +// +// On Windows, each DLL is linked against a fixed base address and +// usually loaded to that address. However, if there's already another +// DLL that overlaps, the loader has to relocate it. To do that, DLLs +// contain .reloc sections which contain offsets that need to be fixed +// up at runtime. If the loader finds that a DLL cannot be loaded to its +// desired base address, it loads it to somewhere else, and add - to each offset that is +// specified by the .reloc section. In ELF terms, .reloc sections +// contain relative relocations in REL format (as opposed to RELA.) +// +// This already significantly reduces the size of relocations compared +// to ELF .rel.dyn, but Windows does more to reduce it (probably because +// it was invented for PCs in the late '80s or early '90s.) Offsets in +// .reloc are grouped by page where the page size is 12 bits, and +// offsets sharing the same page address are stored consecutively to +// represent them with less space. This is very similar to the page +// table which is grouped by (multiple stages of) pages. +// +// For example, let's say we have 0x00030, 0x00500, 0x00700, 0x00A00, +// 0x20004, and 0x20008 in a .reloc section for x64. The uppermost 4 +// bits have a type IMAGE_REL_BASED_DIR64 or 0xA. In the section, they +// are represented like this: +// +// 0x00000 -- page address (4 bytes) +// 16 -- size of this block (4 bytes) +// 0xA030 -- entries (2 bytes each) +// 0xA500 +// 0xA700 +// 0xAA00 +// 0x20000 -- page address (4 bytes) +// 12 -- size of this block (4 bytes) +// 0xA004 -- entries (2 bytes each) +// 0xA008 +// +// Usually we have a lot of relocations for each page, so the number of +// bytes for one .reloc entry is close to 2 bytes on average. +BaserelChunk::BaserelChunk(uint32_t page, Baserel *begin, Baserel *end) { + // Block header consists of 4 byte page RVA and 4 byte block size. + // Each entry is 2 byte. Last entry may be padding. + data.resize(alignTo((end - begin) * 2 + 8, 4)); + uint8_t *p = data.data(); + write32le(p, page); + write32le(p + 4, data.size()); + p += 8; + for (Baserel *i = begin; i != end; ++i) { + write16le(p, (i->type << 12) | (i->rva - page)); + p += 2; + } +} + +void BaserelChunk::writeTo(uint8_t *buf) const { + memcpy(buf, data.data(), data.size()); +} + +uint8_t Baserel::getDefaultType() { + switch (config->machine) { + case AMD64: + case ARM64: + return IMAGE_REL_BASED_DIR64; + case I386: + case ARMNT: + return IMAGE_REL_BASED_HIGHLOW; + default: + llvm_unreachable("unknown machine type"); + } +} + +MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {}; + +MergeChunk::MergeChunk(uint32_t alignment) + : builder(StringTableBuilder::RAW, alignment) { + setAlignment(alignment); +} + +void MergeChunk::addSection(SectionChunk *c) { + assert(isPowerOf2_32(c->getAlignment())); + uint8_t p2Align = llvm::Log2_32(c->getAlignment()); + assert(p2Align < array_lengthof(instances)); + auto *&mc = instances[p2Align]; + if (!mc) + mc = make(c->getAlignment()); + mc->sections.push_back(c); +} + +void MergeChunk::finalizeContents() { + assert(!finalized && "should only finalize once"); + for (SectionChunk *c : sections) + if (c->live) + builder.add(toStringRef(c->getContents())); + builder.finalize(); + finalized = true; +} + +void MergeChunk::assignSubsectionRVAs() { + for (SectionChunk *c : sections) { + if (!c->live) + continue; + size_t off = builder.getOffset(toStringRef(c->getContents())); + c->setRVA(rva + off); + } +} + +uint32_t MergeChunk::getOutputCharacteristics() const { + return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA; +} + +size_t MergeChunk::getSize() const { + return builder.getSize(); +} + +void MergeChunk::writeTo(uint8_t *buf) const { + builder.write(buf); +} + +// MinGW specific. +size_t AbsolutePointerChunk::getSize() const { return config->wordsize; } + +void AbsolutePointerChunk::writeTo(uint8_t *buf) const { + if (config->is64()) { + write64le(buf, value); + } else { + write32le(buf, value); + } +} + +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/Chunks.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Chunks.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Chunks.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Chunks.h (revision 352529) @@ -0,0 +1,686 @@ +//===- Chunks.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_COFF_CHUNKS_H +#define LLD_COFF_CHUNKS_H + +#include "Config.h" +#include "InputFiles.h" +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/iterator.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/COFF.h" +#include +#include + +namespace lld { +namespace coff { + +using llvm::COFF::ImportDirectoryTableEntry; +using llvm::object::COFFSymbolRef; +using llvm::object::SectionRef; +using llvm::object::coff_relocation; +using llvm::object::coff_section; + +class Baserel; +class Defined; +class DefinedImportData; +class DefinedRegular; +class ObjFile; +class OutputSection; +class RuntimePseudoReloc; +class Symbol; + +// Mask for permissions (discardable, writable, readable, executable, etc). +const uint32_t permMask = 0xFE000000; + +// Mask for section types (code, data, bss). +const uint32_t typeMask = 0x000000E0; + +// The log base 2 of the largest section alignment, which is log2(8192), or 13. +enum : unsigned { Log2MaxSectionAlignment = 13 }; + +// A Chunk represents a chunk of data that will occupy space in the +// output (if the resolver chose that). It may or may not be backed by +// a section of an input file. It could be linker-created data, or +// doesn't even have actual data (if common or bss). +class Chunk { +public: + enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind }; + Kind kind() const { return chunkKind; } + + // Returns the size of this chunk (even if this is a common or BSS.) + size_t getSize() const; + + // Returns chunk alignment in power of two form. Value values are powers of + // two from 1 to 8192. + uint32_t getAlignment() const { return 1U << p2Align; } + + // Update the chunk section alignment measured in bytes. Internally alignment + // is stored in log2. + void setAlignment(uint32_t align) { + // Treat zero byte alignment as 1 byte alignment. + align = align ? align : 1; + assert(llvm::isPowerOf2_32(align) && "alignment is not a power of 2"); + p2Align = llvm::Log2_32(align); + assert(p2Align <= Log2MaxSectionAlignment && + "impossible requested alignment"); + } + + // Write this chunk to a mmap'ed file, assuming Buf is pointing to + // beginning of the file. Because this function may use RVA values + // of other chunks for relocations, you need to set them properly + // before calling this function. + void writeTo(uint8_t *buf) const; + + // The writer sets and uses the addresses. In practice, PE images cannot be + // larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs + // can be stored with 32 bits. + uint32_t getRVA() const { return rva; } + void setRVA(uint64_t v) { + rva = (uint32_t)v; + assert(rva == v && "RVA truncated"); + } + + // Returns readable/writable/executable bits. + uint32_t getOutputCharacteristics() const; + + // Returns the section name if this is a section chunk. + // It is illegal to call this function on non-section chunks. + StringRef getSectionName() const; + + // An output section has pointers to chunks in the section, and each + // chunk has a back pointer to an output section. + void setOutputSectionIdx(uint16_t o) { osidx = o; } + uint16_t getOutputSectionIdx() const { return osidx; } + OutputSection *getOutputSection() const; + + // Windows-specific. + // Collect all locations that contain absolute addresses for base relocations. + void getBaserels(std::vector *res); + + // Returns a human-readable name of this chunk. Chunks are unnamed chunks of + // bytes, so this is used only for logging or debugging. + StringRef getDebugName() const; + + // Return true if this file has the hotpatch flag set to true in the + // S_COMPILE3 record in codeview debug info. Also returns true for some thunks + // synthesized by the linker. + bool isHotPatchable() const; + +protected: + Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {} + + const Kind chunkKind; + +public: + // Returns true if this has non-zero data. BSS chunks return + // false. If false is returned, the space occupied by this chunk + // will be filled with zeros. Corresponds to the + // IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit. + uint8_t hasData : 1; + +public: + // The alignment of this chunk, stored in log2 form. The writer uses the + // value. + uint8_t p2Align : 7; + + // The output section index for this chunk. The first valid section number is + // one. + uint16_t osidx = 0; + + // The RVA of this chunk in the output. The writer sets a value. + uint32_t rva = 0; +}; + +class NonSectionChunk : public Chunk { +public: + virtual ~NonSectionChunk() = default; + + // Returns the size of this chunk (even if this is a common or BSS.) + virtual size_t getSize() const = 0; + + virtual uint32_t getOutputCharacteristics() const { return 0; } + + // Write this chunk to a mmap'ed file, assuming Buf is pointing to + // beginning of the file. Because this function may use RVA values + // of other chunks for relocations, you need to set them properly + // before calling this function. + virtual void writeTo(uint8_t *buf) const {} + + // Returns the section name if this is a section chunk. + // It is illegal to call this function on non-section chunks. + virtual StringRef getSectionName() const { + llvm_unreachable("unimplemented getSectionName"); + } + + // Windows-specific. + // Collect all locations that contain absolute addresses for base relocations. + virtual void getBaserels(std::vector *res) {} + + // Returns a human-readable name of this chunk. Chunks are unnamed chunks of + // bytes, so this is used only for logging or debugging. + virtual StringRef getDebugName() const { return ""; } + + static bool classof(const Chunk *c) { return c->kind() != SectionKind; } + +protected: + NonSectionChunk(Kind k = OtherKind) : Chunk(k) {} +}; + +// A chunk corresponding a section of an input file. +class SectionChunk final : public Chunk { + // Identical COMDAT Folding feature accesses section internal data. + friend class ICF; + +public: + class symbol_iterator : public llvm::iterator_adaptor_base< + symbol_iterator, const coff_relocation *, + std::random_access_iterator_tag, Symbol *> { + friend SectionChunk; + + ObjFile *file; + + symbol_iterator(ObjFile *file, const coff_relocation *i) + : symbol_iterator::iterator_adaptor_base(i), file(file) {} + + public: + symbol_iterator() = default; + + Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); } + }; + + SectionChunk(ObjFile *file, const coff_section *header); + static bool classof(const Chunk *c) { return c->kind() == SectionKind; } + size_t getSize() const { return header->SizeOfRawData; } + ArrayRef getContents() const; + void writeTo(uint8_t *buf) const; + + uint32_t getOutputCharacteristics() const { + return header->Characteristics & (permMask | typeMask); + } + StringRef getSectionName() const { + return StringRef(sectionNameData, sectionNameSize); + } + void getBaserels(std::vector *res); + bool isCOMDAT() const; + void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + + void getRuntimePseudoRelocs(std::vector &res); + + // Called if the garbage collector decides to not include this chunk + // in a final output. It's supposed to print out a log message to stdout. + void printDiscardedMessage() const; + + // Adds COMDAT associative sections to this COMDAT section. A chunk + // and its children are treated as a group by the garbage collector. + void addAssociative(SectionChunk *child); + + StringRef getDebugName() const; + + // True if this is a codeview debug info chunk. These will not be laid out in + // the image. Instead they will end up in the PDB, if one is requested. + bool isCodeView() const { + return getSectionName() == ".debug" || getSectionName().startswith(".debug$"); + } + + // True if this is a DWARF debug info or exception handling chunk. + bool isDWARF() const { + return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame"; + } + + // Allow iteration over the bodies of this chunk's relocated symbols. + llvm::iterator_range symbols() const { + return llvm::make_range(symbol_iterator(file, relocsData), + symbol_iterator(file, relocsData + relocsSize)); + } + + ArrayRef getRelocs() const { + return llvm::makeArrayRef(relocsData, relocsSize); + } + + // Reloc setter used by ARM range extension thunk insertion. + void setRelocs(ArrayRef newRelocs) { + relocsData = newRelocs.data(); + relocsSize = newRelocs.size(); + assert(relocsSize == newRelocs.size() && "reloc size truncation"); + } + + // Single linked list iterator for associated comdat children. + class AssociatedIterator + : public llvm::iterator_facade_base< + AssociatedIterator, std::forward_iterator_tag, SectionChunk> { + public: + AssociatedIterator() = default; + AssociatedIterator(SectionChunk *head) : cur(head) {} + AssociatedIterator &operator=(const AssociatedIterator &r) { + cur = r.cur; + return *this; + } + bool operator==(const AssociatedIterator &r) const { return cur == r.cur; } + const SectionChunk &operator*() const { return *cur; } + SectionChunk &operator*() { return *cur; } + AssociatedIterator &operator++() { + cur = cur->assocChildren; + return *this; + } + + private: + SectionChunk *cur = nullptr; + }; + + // Allow iteration over the associated child chunks for this section. + llvm::iterator_range children() const { + return llvm::make_range(AssociatedIterator(assocChildren), + AssociatedIterator(nullptr)); + } + + // The section ID this chunk belongs to in its Obj. + uint32_t getSectionNumber() const; + + ArrayRef consumeDebugMagic(); + + static ArrayRef consumeDebugMagic(ArrayRef data, + StringRef sectionName); + + static SectionChunk *findByName(ArrayRef sections, + StringRef name); + + // The file that this chunk was created from. + ObjFile *file; + + // Pointer to the COFF section header in the input file. + const coff_section *header; + + // The COMDAT leader symbol if this is a COMDAT chunk. + DefinedRegular *sym = nullptr; + + // The CRC of the contents as described in the COFF spec 4.5.5. + // Auxiliary Format 5: Section Definitions. Used for ICF. + uint32_t checksum = 0; + + // Used by the garbage collector. + bool live; + + // Whether this section needs to be kept distinct from other sections during + // ICF. This is set by the driver using address-significance tables. + bool keepUnique = false; + + // The COMDAT selection if this is a COMDAT chunk. + llvm::COFF::COMDATType selection = (llvm::COFF::COMDATType)0; + + // A pointer pointing to a replacement for this chunk. + // Initially it points to "this" object. If this chunk is merged + // with other chunk by ICF, it points to another chunk, + // and this chunk is considered as dead. + SectionChunk *repl; + +private: + SectionChunk *assocChildren = nullptr; + + // Used for ICF (Identical COMDAT Folding) + void replace(SectionChunk *other); + uint32_t eqClass[2] = {0, 0}; + + // Relocations for this section. Size is stored below. + const coff_relocation *relocsData; + + // Section name string. Size is stored below. + const char *sectionNameData; + + uint32_t relocsSize = 0; + uint32_t sectionNameSize = 0; +}; + +// Inline methods to implement faux-virtual dispatch for SectionChunk. + +inline size_t Chunk::getSize() const { + if (isa(this)) + return static_cast(this)->getSize(); + else + return static_cast(this)->getSize(); +} + +inline uint32_t Chunk::getOutputCharacteristics() const { + if (isa(this)) + return static_cast(this)->getOutputCharacteristics(); + else + return static_cast(this) + ->getOutputCharacteristics(); +} + +inline void Chunk::writeTo(uint8_t *buf) const { + if (isa(this)) + static_cast(this)->writeTo(buf); + else + static_cast(this)->writeTo(buf); +} + +inline StringRef Chunk::getSectionName() const { + if (isa(this)) + return static_cast(this)->getSectionName(); + else + return static_cast(this)->getSectionName(); +} + +inline void Chunk::getBaserels(std::vector *res) { + if (isa(this)) + static_cast(this)->getBaserels(res); + else + static_cast(this)->getBaserels(res); +} + +inline StringRef Chunk::getDebugName() const { + if (isa(this)) + return static_cast(this)->getDebugName(); + else + return static_cast(this)->getDebugName(); +} + +// This class is used to implement an lld-specific feature (not implemented in +// MSVC) that minimizes the output size by finding string literals sharing tail +// parts and merging them. +// +// If string tail merging is enabled and a section is identified as containing a +// string literal, it is added to a MergeChunk with an appropriate alignment. +// The MergeChunk then tail merges the strings using the StringTableBuilder +// class and assigns RVAs and section offsets to each of the member chunks based +// on the offsets assigned by the StringTableBuilder. +class MergeChunk : public NonSectionChunk { +public: + MergeChunk(uint32_t alignment); + static void addSection(SectionChunk *c); + void finalizeContents(); + void assignSubsectionRVAs(); + + uint32_t getOutputCharacteristics() const override; + StringRef getSectionName() const override { return ".rdata"; } + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; + + static MergeChunk *instances[Log2MaxSectionAlignment + 1]; + std::vector sections; + +private: + llvm::StringTableBuilder builder; + bool finalized = false; +}; + +// A chunk for common symbols. Common chunks don't have actual data. +class CommonChunk : public NonSectionChunk { +public: + CommonChunk(const COFFSymbolRef sym); + size_t getSize() const override { return sym.getValue(); } + uint32_t getOutputCharacteristics() const override; + StringRef getSectionName() const override { return ".bss"; } + +private: + const COFFSymbolRef sym; +}; + +// A chunk for linker-created strings. +class StringChunk : public NonSectionChunk { +public: + explicit StringChunk(StringRef s) : str(s) {} + size_t getSize() const override { return str.size() + 1; } + void writeTo(uint8_t *buf) const override; + +private: + StringRef str; +}; + +static const uint8_t importThunkX86[] = { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0 +}; + +static const uint8_t importThunkARM[] = { + 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 + 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 + 0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip] +}; + +static const uint8_t importThunkARM64[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, #0 + 0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16] + 0x00, 0x02, 0x1f, 0xd6, // br x16 +}; + +// Windows-specific. +// A chunk for DLL import jump table entry. In a final output, its +// contents will be a JMP instruction to some __imp_ symbol. +class ImportThunkChunk : public NonSectionChunk { +public: + ImportThunkChunk(Defined *s) + : NonSectionChunk(ImportThunkKind), impSymbol(s) {} + static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; } + +protected: + Defined *impSymbol; +}; + +class ImportThunkChunkX64 : public ImportThunkChunk { +public: + explicit ImportThunkChunkX64(Defined *s); + size_t getSize() const override { return sizeof(importThunkX86); } + void writeTo(uint8_t *buf) const override; +}; + +class ImportThunkChunkX86 : public ImportThunkChunk { +public: + explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkX86); } + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; +}; + +class ImportThunkChunkARM : public ImportThunkChunk { +public: + explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkARM); } + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; +}; + +class ImportThunkChunkARM64 : public ImportThunkChunk { +public: + explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkARM64); } + void writeTo(uint8_t *buf) const override; +}; + +class RangeExtensionThunkARM : public NonSectionChunk { +public: + explicit RangeExtensionThunkARM(Defined *t) : target(t) {} + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; + + Defined *target; +}; + +class RangeExtensionThunkARM64 : public NonSectionChunk { +public: + explicit RangeExtensionThunkARM64(Defined *t) : target(t) {} + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; + + Defined *target; +}; + +// Windows-specific. +// See comments for DefinedLocalImport class. +class LocalImportChunk : public NonSectionChunk { +public: + explicit LocalImportChunk(Defined *s) : sym(s) { + setAlignment(config->wordsize); + } + size_t getSize() const override; + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; + +private: + Defined *sym; +}; + +// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and +// offset into the chunk. Order does not matter as the RVA table will be sorted +// later. +struct ChunkAndOffset { + Chunk *inputChunk; + uint32_t offset; + + struct DenseMapInfo { + static ChunkAndOffset getEmptyKey() { + return {llvm::DenseMapInfo::getEmptyKey(), 0}; + } + static ChunkAndOffset getTombstoneKey() { + return {llvm::DenseMapInfo::getTombstoneKey(), 0}; + } + static unsigned getHashValue(const ChunkAndOffset &co) { + return llvm::DenseMapInfo>::getHashValue( + {co.inputChunk, co.offset}); + } + static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) { + return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset; + } + }; +}; + +using SymbolRVASet = llvm::DenseSet; + +// Table which contains symbol RVAs. Used for /safeseh and /guard:cf. +class RVATableChunk : public NonSectionChunk { +public: + explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {} + size_t getSize() const override { return syms.size() * 4; } + void writeTo(uint8_t *buf) const override; + +private: + SymbolRVASet syms; +}; + +// Windows-specific. +// This class represents a block in .reloc section. +// See the PE/COFF spec 5.6 for details. +class BaserelChunk : public NonSectionChunk { +public: + BaserelChunk(uint32_t page, Baserel *begin, Baserel *end); + size_t getSize() const override { return data.size(); } + void writeTo(uint8_t *buf) const override; + +private: + std::vector data; +}; + +class Baserel { +public: + Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {} + explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {} + uint8_t getDefaultType(); + + uint32_t rva; + uint8_t type; +}; + +// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a +// specific place in a section, without any data. This is used for the MinGW +// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept +// of an empty chunk isn't MinGW specific. +class EmptyChunk : public NonSectionChunk { +public: + EmptyChunk() {} + size_t getSize() const override { return 0; } + void writeTo(uint8_t *buf) const override {} +}; + +// MinGW specific, for the "automatic import of variables from DLLs" feature. +// This provides the table of runtime pseudo relocations, for variable +// references that turned out to need to be imported from a DLL even though +// the reference didn't use the dllimport attribute. The MinGW runtime will +// process this table after loading, before handling control over to user +// code. +class PseudoRelocTableChunk : public NonSectionChunk { +public: + PseudoRelocTableChunk(std::vector &relocs) + : relocs(std::move(relocs)) { + setAlignment(4); + } + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; + +private: + std::vector relocs; +}; + +// MinGW specific; information about one individual location in the image +// that needs to be fixed up at runtime after loading. This represents +// one individual element in the PseudoRelocTableChunk table. +class RuntimePseudoReloc { +public: + RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset, + int flags) + : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {} + + Defined *sym; + SectionChunk *target; + uint32_t targetOffset; + // The Flags field contains the size of the relocation, in bits. No other + // flags are currently defined. + int flags; +}; + +// MinGW specific. A Chunk that contains one pointer-sized absolute value. +class AbsolutePointerChunk : public NonSectionChunk { +public: + AbsolutePointerChunk(uint64_t value) : value(value) { + setAlignment(getSize()); + } + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; + +private: + uint64_t value; +}; + +// Return true if this file has the hotpatch flag set to true in the S_COMPILE3 +// record in codeview debug info. Also returns true for some thunks synthesized +// by the linker. +inline bool Chunk::isHotPatchable() const { + if (auto *sc = dyn_cast(this)) + return sc->file->hotPatchable; + else if (isa(this)) + return true; + return false; +} + +void applyMOV32T(uint8_t *off, uint32_t v); +void applyBranch24T(uint8_t *off, int32_t v); + +void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift); +void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit); +void applyArm64Branch26(uint8_t *off, int64_t v); + +} // namespace coff +} // namespace lld + +namespace llvm { +template <> +struct DenseMapInfo + : lld::coff::ChunkAndOffset::DenseMapInfo {}; +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/Chunks.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/DLL.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/DLL.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/DLL.cpp (revision 352529) @@ -0,0 +1,736 @@ +//===- DLL.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines various types of chunks for the DLL import or export +// descriptor tables. They are inherently Windows-specific. +// You need to read Microsoft PE/COFF spec to understand details +// about the data structures. +// +// If you are not particularly interested in linking against Windows +// DLL, you can skip this file, and you should still be able to +// understand the rest of the linker. +// +//===----------------------------------------------------------------------===// + +#include "DLL.h" +#include "Chunks.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::COFF; + +namespace lld { +namespace coff { +namespace { + +// Import table + +// A chunk for the import descriptor table. +class HintNameChunk : public NonSectionChunk { +public: + HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {} + + size_t getSize() const override { + // Starts with 2 byte Hint field, followed by a null-terminated string, + // ends with 0 or 1 byte padding. + return alignTo(name.size() + 3, 2); + } + + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + write16le(buf, hint); + memcpy(buf + 2, name.data(), name.size()); + } + +private: + StringRef name; + uint16_t hint; +}; + +// A chunk for the import descriptor table. +class LookupChunk : public NonSectionChunk { +public: + explicit LookupChunk(Chunk *c) : hintName(c) { + setAlignment(config->wordsize); + } + size_t getSize() const override { return config->wordsize; } + + void writeTo(uint8_t *buf) const override { + if (config->is64()) + write64le(buf, hintName->getRVA()); + else + write32le(buf, hintName->getRVA()); + } + + Chunk *hintName; +}; + +// A chunk for the import descriptor table. +// This chunk represent import-by-ordinal symbols. +// See Microsoft PE/COFF spec 7.1. Import Header for details. +class OrdinalOnlyChunk : public NonSectionChunk { +public: + explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) { + setAlignment(config->wordsize); + } + size_t getSize() const override { return config->wordsize; } + + void writeTo(uint8_t *buf) const override { + // An import-by-ordinal slot has MSB 1 to indicate that + // this is import-by-ordinal (and not import-by-name). + if (config->is64()) { + write64le(buf, (1ULL << 63) | ordinal); + } else { + write32le(buf, (1ULL << 31) | ordinal); + } + } + + uint16_t ordinal; +}; + +// A chunk for the import descriptor table. +class ImportDirectoryChunk : public NonSectionChunk { +public: + explicit ImportDirectoryChunk(Chunk *n) : dllName(n) {} + size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); } + + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (coff_import_directory_table_entry *)(buf); + e->ImportLookupTableRVA = lookupTab->getRVA(); + e->NameRVA = dllName->getRVA(); + e->ImportAddressTableRVA = addressTab->getRVA(); + } + + Chunk *dllName; + Chunk *lookupTab; + Chunk *addressTab; +}; + +// A chunk representing null terminator in the import table. +// Contents of this chunk is always null bytes. +class NullChunk : public NonSectionChunk { +public: + explicit NullChunk(size_t n) : size(n) { hasData = false; } + size_t getSize() const override { return size; } + + void writeTo(uint8_t *buf) const override { + memset(buf, 0, size); + } + +private: + size_t size; +}; + +static std::vector> +binImports(const std::vector &imports) { + // Group DLL-imported symbols by DLL name because that's how + // symbols are layed out in the import descriptor table. + auto less = [](const std::string &a, const std::string &b) { + return config->dllOrder[a] < config->dllOrder[b]; + }; + std::map, + bool(*)(const std::string &, const std::string &)> m(less); + for (DefinedImportData *sym : imports) + m[sym->getDLLName().lower()].push_back(sym); + + std::vector> v; + for (auto &kv : m) { + // Sort symbols by name for each group. + std::vector &syms = kv.second; + std::sort(syms.begin(), syms.end(), + [](DefinedImportData *a, DefinedImportData *b) { + return a->getName() < b->getName(); + }); + v.push_back(std::move(syms)); + } + return v; +} + +// Export table +// See Microsoft PE/COFF spec 4.3 for details. + +// A chunk for the delay import descriptor table etnry. +class DelayDirectoryChunk : public NonSectionChunk { +public: + explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {} + + size_t getSize() const override { + return sizeof(delay_import_directory_table_entry); + } + + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (delay_import_directory_table_entry *)(buf); + e->Attributes = 1; + e->Name = dllName->getRVA(); + e->ModuleHandle = moduleHandle->getRVA(); + e->DelayImportAddressTable = addressTab->getRVA(); + e->DelayImportNameTable = nameTab->getRVA(); + } + + Chunk *dllName; + Chunk *moduleHandle; + Chunk *addressTab; + Chunk *nameTab; +}; + +// Initial contents for delay-loaded functions. +// This code calls __delayLoadHelper2 function to resolve a symbol +// and then overwrites its jump table slot with the result +// for subsequent function calls. +static const uint8_t thunkX64[] = { + 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_] + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ +}; + +static const uint8_t tailMergeX64[] = { + 0x51, // push rcx + 0x52, // push rdx + 0x41, 0x50, // push r8 + 0x41, 0x51, // push r9 + 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h + 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 + 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 + 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 + 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 + 0x48, 0x8B, 0xD0, // mov rdx, rax + 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] + 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 + 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] + 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] + 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] + 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] + 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h + 0x41, 0x59, // pop r9 + 0x41, 0x58, // pop r8 + 0x5A, // pop rdx + 0x59, // pop rcx + 0xFF, 0xE0, // jmp rax +}; + +static const uint8_t thunkX86[] = { + 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__ + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ +}; + +static const uint8_t tailMergeX86[] = { + 0x51, // push ecx + 0x52, // push edx + 0x50, // push eax + 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR__dll + 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 + 0x5A, // pop edx + 0x59, // pop ecx + 0xFF, 0xE0, // jmp eax +}; + +static const uint8_t thunkARM[] = { + 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_ + 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_ + 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_ +}; + +static const uint8_t tailMergeARM[] = { + 0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr} + 0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16 + 0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7} + 0x61, 0x46, // mov r1, ip + 0x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR + 0xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR + 0x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper2 + 0x84, 0x46, // mov ip, r0 + 0xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7} + 0xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr} + 0x60, 0x47, // bx ip +}; + +static const uint8_t thunkARM64[] = { + 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_ + 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_ + 0x00, 0x00, 0x00, 0x14, // b __tailMerge_ +}; + +static const uint8_t tailMergeARM64[] = { + 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! + 0xfd, 0x03, 0x00, 0x91, // mov x29, sp + 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] + 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32] + 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48] + 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64] + 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80] + 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112] + 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144] + 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176] + 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 + 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR + 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR + 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2 + 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 + 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] + 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] + 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112] + 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80] + 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64] + 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48] + 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32] + 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16] + 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208 + 0x00, 0x02, 0x1f, 0xd6, // br x16 +}; + +// A chunk for the delay import thunk. +class ThunkChunkX64 : public NonSectionChunk { +public: + ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkX64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkX64, sizeof(thunkX64)); + write32le(buf + 3, imp->getRVA() - rva - 7); + write32le(buf + 8, tailMerge->getRVA() - rva - 12); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; +}; + +class TailMergeChunkX64 : public NonSectionChunk { +public: + TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeX64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeX64, sizeof(tailMergeX64)); + write32le(buf + 39, desc->getRVA() - rva - 43); + write32le(buf + 44, helper->getRVA() - rva - 48); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + +class ThunkChunkX86 : public NonSectionChunk { +public: + ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkX86); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkX86, sizeof(thunkX86)); + write32le(buf + 1, imp->getRVA() + config->imageBase); + write32le(buf + 6, tailMerge->getRVA() - rva - 10); + } + + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 1); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; +}; + +class TailMergeChunkX86 : public NonSectionChunk { +public: + TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeX86); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeX86, sizeof(tailMergeX86)); + write32le(buf + 4, desc->getRVA() + config->imageBase); + write32le(buf + 9, helper->getRVA() - rva - 13); + } + + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 4); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + +class ThunkChunkARM : public NonSectionChunk { +public: + ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkARM); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkARM, sizeof(thunkARM)); + applyMOV32T(buf + 0, imp->getRVA() + config->imageBase); + applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12); + } + + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; +}; + +class TailMergeChunkARM : public NonSectionChunk { +public: + TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeARM); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeARM, sizeof(tailMergeARM)); + applyMOV32T(buf + 14, desc->getRVA() + config->imageBase); + applyBranch24T(buf + 22, helper->getRVA() - rva - 26); + } + + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + +class ThunkChunkARM64 : public NonSectionChunk { +public: + ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkARM64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkARM64, sizeof(thunkARM64)); + applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12); + applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0); + applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; +}; + +class TailMergeChunkARM64 : public NonSectionChunk { +public: + TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeARM64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64)); + applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12); + applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0); + applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + +// A chunk for the import descriptor table. +class DelayAddressChunk : public NonSectionChunk { +public: + explicit DelayAddressChunk(Chunk *c) : thunk(c) { + setAlignment(config->wordsize); + } + size_t getSize() const override { return config->wordsize; } + + void writeTo(uint8_t *buf) const override { + if (config->is64()) { + write64le(buf, thunk->getRVA() + config->imageBase); + } else { + uint32_t bit = 0; + // Pointer to thumb code must have the LSB set, so adjust it. + if (config->machine == ARMNT) + bit = 1; + write32le(buf, (thunk->getRVA() + config->imageBase) | bit); + } + } + + void getBaserels(std::vector *res) override { + res->emplace_back(rva); + } + + Chunk *thunk; +}; + +// Export table +// Read Microsoft PE/COFF spec 5.3 for details. + +// A chunk for the export descriptor table. +class ExportDirectoryChunk : public NonSectionChunk { +public: + ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o) + : maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n), + ordinalTab(o) {} + + size_t getSize() const override { + return sizeof(export_directory_table_entry); + } + + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (export_directory_table_entry *)(buf); + e->NameRVA = dllName->getRVA(); + e->OrdinalBase = 0; + e->AddressTableEntries = maxOrdinal + 1; + e->NumberOfNamePointers = nameTabSize; + e->ExportAddressTableRVA = addressTab->getRVA(); + e->NamePointerRVA = nameTab->getRVA(); + e->OrdinalTableRVA = ordinalTab->getRVA(); + } + + uint16_t maxOrdinal; + uint16_t nameTabSize; + Chunk *dllName; + Chunk *addressTab; + Chunk *nameTab; + Chunk *ordinalTab; +}; + +class AddressTableChunk : public NonSectionChunk { +public: + explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {} + size_t getSize() const override { return size * 4; } + + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + for (const Export &e : config->exports) { + uint8_t *p = buf + e.ordinal * 4; + uint32_t bit = 0; + // Pointer to thumb code must have the LSB set, so adjust it. + if (config->machine == ARMNT && !e.data) + bit = 1; + if (e.forwardChunk) { + write32le(p, e.forwardChunk->getRVA() | bit); + } else { + write32le(p, cast(e.sym)->getRVA() | bit); + } + } + } + +private: + size_t size; +}; + +class NamePointersChunk : public NonSectionChunk { +public: + explicit NamePointersChunk(std::vector &v) : chunks(v) {} + size_t getSize() const override { return chunks.size() * 4; } + + void writeTo(uint8_t *buf) const override { + for (Chunk *c : chunks) { + write32le(buf, c->getRVA()); + buf += 4; + } + } + +private: + std::vector chunks; +}; + +class ExportOrdinalChunk : public NonSectionChunk { +public: + explicit ExportOrdinalChunk(size_t i) : size(i) {} + size_t getSize() const override { return size * 2; } + + void writeTo(uint8_t *buf) const override { + for (Export &e : config->exports) { + if (e.noname) + continue; + write16le(buf, e.ordinal); + buf += 2; + } + } + +private: + size_t size; +}; + +} // anonymous namespace + +void IdataContents::create() { + std::vector> v = binImports(imports); + + // Create .idata contents for each DLL. + for (std::vector &syms : v) { + // Create lookup and address tables. If they have external names, + // we need to create hintName chunks to store the names. + // If they don't (if they are import-by-ordinals), we store only + // ordinal values to the table. + size_t base = lookups.size(); + for (DefinedImportData *s : syms) { + uint16_t ord = s->getOrdinal(); + if (s->getExternalName().empty()) { + lookups.push_back(make(ord)); + addresses.push_back(make(ord)); + continue; + } + auto *c = make(s->getExternalName(), ord); + lookups.push_back(make(c)); + addresses.push_back(make(c)); + hints.push_back(c); + } + // Terminate with null values. + lookups.push_back(make(config->wordsize)); + addresses.push_back(make(config->wordsize)); + + for (int i = 0, e = syms.size(); i < e; ++i) + syms[i]->setLocation(addresses[base + i]); + + // Create the import table header. + dllNames.push_back(make(syms[0]->getDLLName())); + auto *dir = make(dllNames.back()); + dir->lookupTab = lookups[base]; + dir->addressTab = addresses[base]; + dirs.push_back(dir); + } + // Add null terminator. + dirs.push_back(make(sizeof(ImportDirectoryTableEntry))); +} + +std::vector DelayLoadContents::getChunks() { + std::vector v; + v.insert(v.end(), dirs.begin(), dirs.end()); + v.insert(v.end(), names.begin(), names.end()); + v.insert(v.end(), hintNames.begin(), hintNames.end()); + v.insert(v.end(), dllNames.begin(), dllNames.end()); + return v; +} + +std::vector DelayLoadContents::getDataChunks() { + std::vector v; + v.insert(v.end(), moduleHandles.begin(), moduleHandles.end()); + v.insert(v.end(), addresses.begin(), addresses.end()); + return v; +} + +uint64_t DelayLoadContents::getDirSize() { + return dirs.size() * sizeof(delay_import_directory_table_entry); +} + +void DelayLoadContents::create(Defined *h) { + helper = h; + std::vector> v = binImports(imports); + + // Create .didat contents for each DLL. + for (std::vector &syms : v) { + // Create the delay import table header. + dllNames.push_back(make(syms[0]->getDLLName())); + auto *dir = make(dllNames.back()); + + size_t base = addresses.size(); + Chunk *tm = newTailMergeChunk(dir); + for (DefinedImportData *s : syms) { + Chunk *t = newThunkChunk(s, tm); + auto *a = make(t); + addresses.push_back(a); + thunks.push_back(t); + StringRef extName = s->getExternalName(); + if (extName.empty()) { + names.push_back(make(s->getOrdinal())); + } else { + auto *c = make(extName, 0); + names.push_back(make(c)); + hintNames.push_back(c); + } + } + thunks.push_back(tm); + // Terminate with null values. + addresses.push_back(make(8)); + names.push_back(make(8)); + + for (int i = 0, e = syms.size(); i < e; ++i) + syms[i]->setLocation(addresses[base + i]); + auto *mh = make(8); + mh->setAlignment(8); + moduleHandles.push_back(mh); + + // Fill the delay import table header fields. + dir->moduleHandle = mh; + dir->addressTab = addresses[base]; + dir->nameTab = names[base]; + dirs.push_back(dir); + } + // Add null terminator. + dirs.push_back(make(sizeof(delay_import_directory_table_entry))); +} + +Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { + switch (config->machine) { + case AMD64: + return make(dir, helper); + case I386: + return make(dir, helper); + case ARMNT: + return make(dir, helper); + case ARM64: + return make(dir, helper); + default: + llvm_unreachable("unsupported machine type"); + } +} + +Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, + Chunk *tailMerge) { + switch (config->machine) { + case AMD64: + return make(s, tailMerge); + case I386: + return make(s, tailMerge); + case ARMNT: + return make(s, tailMerge); + case ARM64: + return make(s, tailMerge); + default: + llvm_unreachable("unsupported machine type"); + } +} + +EdataContents::EdataContents() { + uint16_t maxOrdinal = 0; + for (Export &e : config->exports) + maxOrdinal = std::max(maxOrdinal, e.ordinal); + + auto *dllName = make(sys::path::filename(config->outputFile)); + auto *addressTab = make(maxOrdinal); + std::vector names; + for (Export &e : config->exports) + if (!e.noname) + names.push_back(make(e.exportName)); + + std::vector forwards; + for (Export &e : config->exports) { + if (e.forwardTo.empty()) + continue; + e.forwardChunk = make(e.forwardTo); + forwards.push_back(e.forwardChunk); + } + + auto *nameTab = make(names); + auto *ordinalTab = make(names.size()); + auto *dir = make(maxOrdinal, names.size(), dllName, + addressTab, nameTab, ordinalTab); + chunks.push_back(dir); + chunks.push_back(dllName); + chunks.push_back(addressTab); + chunks.push_back(nameTab); + chunks.push_back(ordinalTab); + chunks.insert(chunks.end(), names.begin(), names.end()); + chunks.insert(chunks.end(), forwards.begin(), forwards.end()); +} + +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/DLL.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/DLL.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/DLL.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/DLL.h (revision 352529) @@ -0,0 +1,82 @@ +//===- DLL.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_COFF_DLL_H +#define LLD_COFF_DLL_H + +#include "Chunks.h" +#include "Symbols.h" + +namespace lld { +namespace coff { + +// Windows-specific. +// IdataContents creates all chunks for the DLL import table. +// You are supposed to call add() to add symbols and then +// call create() to populate the chunk vectors. +class IdataContents { +public: + void add(DefinedImportData *sym) { imports.push_back(sym); } + bool empty() { return imports.empty(); } + + void create(); + + std::vector imports; + std::vector dirs; + std::vector lookups; + std::vector addresses; + std::vector hints; + std::vector dllNames; +}; + +// Windows-specific. +// DelayLoadContents creates all chunks for the delay-load DLL import table. +class DelayLoadContents { +public: + void add(DefinedImportData *sym) { imports.push_back(sym); } + bool empty() { return imports.empty(); } + void create(Defined *helper); + std::vector getChunks(); + std::vector getDataChunks(); + ArrayRef getCodeChunks() { return thunks; } + + uint64_t getDirRVA() { return dirs[0]->getRVA(); } + uint64_t getDirSize(); + +private: + Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge); + Chunk *newTailMergeChunk(Chunk *dir); + + Defined *helper; + std::vector imports; + std::vector dirs; + std::vector moduleHandles; + std::vector addresses; + std::vector names; + std::vector hintNames; + std::vector thunks; + std::vector dllNames; +}; + +// Windows-specific. +// EdataContents creates all chunks for the DLL export table. +class EdataContents { +public: + EdataContents(); + std::vector chunks; + + uint64_t getRVA() { return chunks[0]->getRVA(); } + uint64_t getSize() { + return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA(); + } +}; + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/DLL.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/DebugTypes.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/DebugTypes.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/DebugTypes.cpp (revision 352529) @@ -0,0 +1,268 @@ +//===- DebugTypes.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "DebugTypes.h" +#include "Driver.h" +#include "InputFiles.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/Support/Path.h" + +using namespace lld; +using namespace lld::coff; +using namespace llvm; +using namespace llvm::codeview; + +namespace { +// The TypeServerSource class represents a PDB type server, a file referenced by +// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ +// files, therefore there must be only once instance per OBJ lot. The file path +// is discovered from the dependent OBJ's debug type stream. The +// TypeServerSource object is then queued and loaded by the COFF Driver. The +// debug type stream for such PDB files will be merged first in the final PDB, +// before any dependent OBJ. +class TypeServerSource : public TpiSource { +public: + explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s) + : TpiSource(PDB, nullptr), session(s), mb(m) {} + + // Queue a PDB type server for loading in the COFF Driver + static void enqueue(const ObjFile *dependentFile, + const TypeServer2Record &ts); + + // Create an instance + static Expected getInstance(MemoryBufferRef m); + + // Fetch the PDB instance loaded for a corresponding dependent OBJ. + static Expected + findFromFile(const ObjFile *dependentFile); + + static std::map> + instances; + + // The interface to the PDB (if it was opened successfully) + std::unique_ptr session; + +private: + MemoryBufferRef mb; +}; + +// This class represents the debug type stream of an OBJ file that depends on a +// PDB type server (see TypeServerSource). +class UseTypeServerSource : public TpiSource { +public: + UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) + : TpiSource(UsingPDB, f), typeServerDependency(*ts) {} + + // Information about the PDB type server dependency, that needs to be loaded + // in before merging this OBJ. + TypeServer2Record typeServerDependency; +}; + +// This class represents the debug type stream of a Microsoft precompiled +// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output +// PDB, before any other OBJs that depend on this. Note that only MSVC generate +// such files, clang does not. +class PrecompSource : public TpiSource { +public: + PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {} +}; + +// This class represents the debug type stream of an OBJ file that depends on a +// Microsoft precompiled headers OBJ (see PrecompSource). +class UsePrecompSource : public TpiSource { +public: + UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) + : TpiSource(UsingPCH, f), precompDependency(*precomp) {} + + // Information about the Precomp OBJ dependency, that needs to be loaded in + // before merging this OBJ. + PrecompRecord precompDependency; +}; +} // namespace + +static std::vector> GC; + +TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { + GC.push_back(std::unique_ptr(this)); +} + +TpiSource *lld::coff::makeTpiSource(const ObjFile *f) { + return new TpiSource(TpiSource::Regular, f); +} + +TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, + const TypeServer2Record *ts) { + TypeServerSource::enqueue(f, *ts); + return new UseTypeServerSource(f, ts); +} + +TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { + return new PrecompSource(f); +} + +TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, + const PrecompRecord *precomp) { + return new UsePrecompSource(f, precomp); +} + +namespace lld { +namespace coff { +template <> +const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { + assert(source->kind == TpiSource::UsingPCH); + return ((const UsePrecompSource *)source)->precompDependency; +} + +template <> +const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { + assert(source->kind == TpiSource::UsingPDB); + return ((const UseTypeServerSource *)source)->typeServerDependency; +} +} // namespace coff +} // namespace lld + +std::map> + TypeServerSource::instances; + +// Make a PDB path assuming the PDB is in the same folder as the OBJ +static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) { + StringRef localPath = + !file->parentName.empty() ? file->parentName : file->getName(); + SmallString<128> path = sys::path::parent_path(localPath); + + // Currently, type server PDBs are only created by MSVC cl, which only runs + // on Windows, so we can assume type server paths are Windows style. + sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows)); + return path.str(); +} + +// The casing of the PDB path stamped in the OBJ can differ from the actual path +// on disk. With this, we ensure to always use lowercase as a key for the +// PDBInputFile::Instances map, at least on Windows. +static std::string normalizePdbPath(StringRef path) { +#if defined(_WIN32) + return path.lower(); +#else // LINUX + return path; +#endif +} + +// If existing, return the actual PDB path on disk. +static Optional findPdbPath(StringRef pdbPath, + const ObjFile *dependentFile) { + // Ensure the file exists before anything else. In some cases, if the path + // points to a removable device, Driver::enqueuePath() would fail with an + // error (EAGAIN, "resource unavailable try again") which we want to skip + // silently. + if (llvm::sys::fs::exists(pdbPath)) + return normalizePdbPath(pdbPath); + std::string ret = getPdbBaseName(dependentFile, pdbPath); + if (llvm::sys::fs::exists(ret)) + return normalizePdbPath(ret); + return None; +} + +// Fetch the PDB instance that was already loaded by the COFF Driver. +Expected +TypeServerSource::findFromFile(const ObjFile *dependentFile) { + const TypeServer2Record &ts = + retrieveDependencyInfo(dependentFile->debugTypesObj); + + Optional p = findPdbPath(ts.Name, dependentFile); + if (!p) + return createFileError(ts.Name, errorCodeToError(std::error_code( + ENOENT, std::generic_category()))); + + auto it = TypeServerSource::instances.find(*p); + // The PDB file exists on disk, at this point we expect it to have been + // inserted in the map by TypeServerSource::loadPDB() + assert(it != TypeServerSource::instances.end()); + + std::pair &pdb = it->second; + + if (!pdb.second) + return createFileError( + *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str())); + + pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile(); + pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); + + // Just because a file with a matching name was found doesn't mean it can be + // used. The GUID must match between the PDB header and the OBJ + // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. + if (info.getGuid() != ts.getGuid()) + return createFileError( + ts.Name, + make_error(pdb::pdb_error_code::signature_out_of_date)); + + return pdb.second; +} + +// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is +// moved here. +Expected +lld::coff::findTypeServerSource(const ObjFile *f) { + Expected ts = TypeServerSource::findFromFile(f); + if (!ts) + return ts.takeError(); + return ts.get()->session.get(); +} + +// Queue a PDB type server for loading in the COFF Driver +void TypeServerSource::enqueue(const ObjFile *dependentFile, + const TypeServer2Record &ts) { + // Start by finding where the PDB is located (either the record path or next + // to the OBJ file) + Optional p = findPdbPath(ts.Name, dependentFile); + if (!p) + return; + auto it = TypeServerSource::instances.emplace( + *p, std::pair{}); + if (!it.second) + return; // another OBJ already scheduled this PDB for load + + driver->enqueuePath(*p, false); +} + +// Create an instance of TypeServerSource or an error string if the PDB couldn't +// be loaded. The error message will be displayed later, when the referring OBJ +// will be merged in. NOTE - a PDB load failure is not a link error: some +// debug info will simply be missing from the final PDB - that is the default +// accepted behavior. +void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { + std::string path = normalizePdbPath(m.getBufferIdentifier()); + + Expected ts = TypeServerSource::getInstance(m); + if (!ts) + TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; + else + TypeServerSource::instances[path] = {{}, *ts}; +} + +Expected TypeServerSource::getInstance(MemoryBufferRef m) { + std::unique_ptr iSession; + Error err = pdb::NativeSession::createFromPdb( + MemoryBuffer::getMemBuffer(m, false), iSession); + if (err) + return std::move(err); + + std::unique_ptr session( + static_cast(iSession.release())); + + pdb::PDBFile &pdbFile = session->getPDBFile(); + Expected info = pdbFile.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!info) + return info.takeError(); + return new TypeServerSource(m, session.release()); +} Index: vendor/lld/lld-release_900-r372316/COFF/DebugTypes.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/DebugTypes.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/DebugTypes.h (revision 352529) @@ -0,0 +1,60 @@ +//===- DebugTypes.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_COFF_DEBUGTYPES_H +#define LLD_COFF_DEBUGTYPES_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { +namespace codeview { +class PrecompRecord; +class TypeServer2Record; +} // namespace codeview +namespace pdb { +class NativeSession; +} +} // namespace llvm + +namespace lld { +namespace coff { + +class ObjFile; + +class TpiSource { +public: + enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; + + TpiSource(TpiKind k, const ObjFile *f); + virtual ~TpiSource() {} + + const TpiKind kind; + const ObjFile *file; +}; + +TpiSource *makeTpiSource(const ObjFile *f); +TpiSource *makeUseTypeServerSource(const ObjFile *f, + const llvm::codeview::TypeServer2Record *ts); +TpiSource *makePrecompSource(const ObjFile *f); +TpiSource *makeUsePrecompSource(const ObjFile *f, + const llvm::codeview::PrecompRecord *precomp); + +void loadTypeServerSource(llvm::MemoryBufferRef m); + +// Temporary interface to get the dependency +template const T &retrieveDependencyInfo(const TpiSource *source); + +// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here +llvm::Expected +findTypeServerSource(const ObjFile *f); + +} // namespace coff +} // namespace lld + +#endif \ No newline at end of file Index: vendor/lld/lld-release_900-r372316/COFF/DriverUtils.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/DriverUtils.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/DriverUtils.cpp (revision 352529) @@ -0,0 +1,897 @@ +//===- DriverUtils.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains utility functions for the driver. Because there +// are so many small functions, we created this separate file to make +// Driver.cpp less cluttered. +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "Driver.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/WindowsManifest/WindowsManifestMerger.h" +#include + +using namespace llvm::COFF; +using namespace llvm; +using llvm::sys::Process; + +namespace lld { +namespace coff { +namespace { + +const uint16_t SUBLANG_ENGLISH_US = 0x0409; +const uint16_t RT_MANIFEST = 24; + +class Executor { +public: + explicit Executor(StringRef s) : prog(saver.save(s)) {} + void add(StringRef s) { args.push_back(saver.save(s)); } + void add(std::string &s) { args.push_back(saver.save(s)); } + void add(Twine s) { args.push_back(saver.save(s)); } + void add(const char *s) { args.push_back(saver.save(s)); } + + void run() { + ErrorOr exeOrErr = sys::findProgramByName(prog); + if (auto ec = exeOrErr.getError()) + fatal("unable to find " + prog + " in PATH: " + ec.message()); + StringRef exe = saver.save(*exeOrErr); + args.insert(args.begin(), exe); + + if (sys::ExecuteAndWait(args[0], args) != 0) + fatal("ExecuteAndWait failed: " + + llvm::join(args.begin(), args.end(), " ")); + } + +private: + StringRef prog; + std::vector args; +}; + +} // anonymous namespace + +// Parses a string in the form of "[,]". +void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { + StringRef s1, s2; + std::tie(s1, s2) = arg.split(','); + if (s1.getAsInteger(0, *addr)) + fatal("invalid number: " + s1); + if (size && !s2.empty() && s2.getAsInteger(0, *size)) + fatal("invalid number: " + s2); +} + +// Parses a string in the form of "[.]". +// If second number is not present, Minor is set to 0. +void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { + StringRef s1, s2; + std::tie(s1, s2) = arg.split('.'); + if (s1.getAsInteger(0, *major)) + fatal("invalid number: " + s1); + *minor = 0; + if (!s2.empty() && s2.getAsInteger(0, *minor)) + fatal("invalid number: " + s2); +} + +void parseGuard(StringRef fullArg) { + SmallVector splitArgs; + fullArg.split(splitArgs, ","); + for (StringRef arg : splitArgs) { + if (arg.equals_lower("no")) + config->guardCF = GuardCFLevel::Off; + else if (arg.equals_lower("nolongjmp")) + config->guardCF = GuardCFLevel::NoLongJmp; + else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) + config->guardCF = GuardCFLevel::Full; + else + fatal("invalid argument to /guard: " + arg); + } +} + +// Parses a string in the form of "[,[.]]". +void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor) { + StringRef sysStr, ver; + std::tie(sysStr, ver) = arg.split(','); + std::string sysStrLower = sysStr.lower(); + *sys = StringSwitch(sysStrLower) + .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) + .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) + .Case("default", IMAGE_SUBSYSTEM_UNKNOWN) + .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) + .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) + .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) + .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) + .Case("native", IMAGE_SUBSYSTEM_NATIVE) + .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI) + .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) + .Default(IMAGE_SUBSYSTEM_UNKNOWN); + if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default") + fatal("unknown subsystem: " + sysStr); + if (!ver.empty()) + parseVersion(ver, major, minor); +} + +// Parse a string of the form of "=". +// Results are directly written to Config. +void parseAlternateName(StringRef s) { + StringRef from, to; + std::tie(from, to) = s.split('='); + if (from.empty() || to.empty()) + fatal("/alternatename: invalid argument: " + s); + auto it = config->alternateNames.find(from); + if (it != config->alternateNames.end() && it->second != to) + fatal("/alternatename: conflicts: " + s); + config->alternateNames.insert(it, std::make_pair(from, to)); +} + +// Parse a string of the form of "=". +// Results are directly written to Config. +void parseMerge(StringRef s) { + StringRef from, to; + std::tie(from, to) = s.split('='); + if (from.empty() || to.empty()) + fatal("/merge: invalid argument: " + s); + if (from == ".rsrc" || to == ".rsrc") + fatal("/merge: cannot merge '.rsrc' with any section"); + if (from == ".reloc" || to == ".reloc") + fatal("/merge: cannot merge '.reloc' with any section"); + auto pair = config->merge.insert(std::make_pair(from, to)); + bool inserted = pair.second; + if (!inserted) { + StringRef existing = pair.first->second; + if (existing != to) + warn(s + ": already merged into " + existing); + } +} + +static uint32_t parseSectionAttributes(StringRef s) { + uint32_t ret = 0; + for (char c : s.lower()) { + switch (c) { + case 'd': + ret |= IMAGE_SCN_MEM_DISCARDABLE; + break; + case 'e': + ret |= IMAGE_SCN_MEM_EXECUTE; + break; + case 'k': + ret |= IMAGE_SCN_MEM_NOT_CACHED; + break; + case 'p': + ret |= IMAGE_SCN_MEM_NOT_PAGED; + break; + case 'r': + ret |= IMAGE_SCN_MEM_READ; + break; + case 's': + ret |= IMAGE_SCN_MEM_SHARED; + break; + case 'w': + ret |= IMAGE_SCN_MEM_WRITE; + break; + default: + fatal("/section: invalid argument: " + s); + } + } + return ret; +} + +// Parses /section option argument. +void parseSection(StringRef s) { + StringRef name, attrs; + std::tie(name, attrs) = s.split(','); + if (name.empty() || attrs.empty()) + fatal("/section: invalid argument: " + s); + config->section[name] = parseSectionAttributes(attrs); +} + +// Parses /aligncomm option argument. +void parseAligncomm(StringRef s) { + StringRef name, align; + std::tie(name, align) = s.split(','); + if (name.empty() || align.empty()) { + error("/aligncomm: invalid argument: " + s); + return; + } + int v; + if (align.getAsInteger(0, v)) { + error("/aligncomm: invalid argument: " + s); + return; + } + config->alignComm[name] = std::max(config->alignComm[name], 1 << v); +} + +// Parses /functionpadmin option argument. +void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { + StringRef arg = a->getNumValues() ? a->getValue() : ""; + if (!arg.empty()) { + // Optional padding in bytes is given. + if (arg.getAsInteger(0, config->functionPadMin)) + error("/functionpadmin: invalid argument: " + arg); + return; + } + // No optional argument given. + // Set default padding based on machine, similar to link.exe. + // There is no default padding for ARM platforms. + if (machine == I386) { + config->functionPadMin = 5; + } else if (machine == AMD64) { + config->functionPadMin = 6; + } else { + error("/functionpadmin: invalid argument for this machine: " + arg); + } +} + +// Parses a string in the form of "EMBED[,=]|NO". +// Results are directly written to Config. +void parseManifest(StringRef arg) { + if (arg.equals_lower("no")) { + config->manifest = Configuration::No; + return; + } + if (!arg.startswith_lower("embed")) + fatal("invalid option " + arg); + config->manifest = Configuration::Embed; + arg = arg.substr(strlen("embed")); + if (arg.empty()) + return; + if (!arg.startswith_lower(",id=")) + fatal("invalid option " + arg); + arg = arg.substr(strlen(",id=")); + if (arg.getAsInteger(0, config->manifestID)) + fatal("invalid option " + arg); +} + +// Parses a string in the form of "level=|uiAccess=|NO". +// Results are directly written to Config. +void parseManifestUAC(StringRef arg) { + if (arg.equals_lower("no")) { + config->manifestUAC = false; + return; + } + for (;;) { + arg = arg.ltrim(); + if (arg.empty()) + return; + if (arg.startswith_lower("level=")) { + arg = arg.substr(strlen("level=")); + std::tie(config->manifestLevel, arg) = arg.split(" "); + continue; + } + if (arg.startswith_lower("uiaccess=")) { + arg = arg.substr(strlen("uiaccess=")); + std::tie(config->manifestUIAccess, arg) = arg.split(" "); + continue; + } + fatal("invalid option " + arg); + } +} + +// Parses a string in the form of "cd|net[,(cd|net)]*" +// Results are directly written to Config. +void parseSwaprun(StringRef arg) { + do { + StringRef swaprun, newArg; + std::tie(swaprun, newArg) = arg.split(','); + if (swaprun.equals_lower("cd")) + config->swaprunCD = true; + else if (swaprun.equals_lower("net")) + config->swaprunNet = true; + else if (swaprun.empty()) + error("/swaprun: missing argument"); + else + error("/swaprun: invalid argument: " + swaprun); + // To catch trailing commas, e.g. `/spawrun:cd,` + if (newArg.empty() && arg.endswith(",")) + error("/swaprun: missing argument"); + arg = newArg; + } while (!arg.empty()); +} + +// An RAII temporary file class that automatically removes a temporary file. +namespace { +class TemporaryFile { +public: + TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") { + SmallString<128> s; + if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s)) + fatal("cannot create a temporary file: " + ec.message()); + path = s.str(); + + if (!contents.empty()) { + std::error_code ec; + raw_fd_ostream os(path, ec, sys::fs::F_None); + if (ec) + fatal("failed to open " + path + ": " + ec.message()); + os << contents; + } + } + + TemporaryFile(TemporaryFile &&obj) { + std::swap(path, obj.path); + } + + ~TemporaryFile() { + if (path.empty()) + return; + if (sys::fs::remove(path)) + fatal("failed to remove " + path); + } + + // Returns a memory buffer of this temporary file. + // Note that this function does not leave the file open, + // so it is safe to remove the file immediately after this function + // is called (you cannot remove an opened file on Windows.) + std::unique_ptr getMemoryBuffer() { + // IsVolatile=true forces MemoryBuffer to not use mmap(). + return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false, + /*IsVolatile=*/true), + "could not open " + path); + } + + std::string path; +}; +} + +static std::string createDefaultXml() { + std::string ret; + raw_string_ostream os(ret); + + // Emit the XML. Note that we do *not* verify that the XML attributes are + // syntactically correct. This is intentional for link.exe compatibility. + os << "\n" + << "\n"; + if (config->manifestUAC) { + os << " \n" + << " \n" + << " \n" + << " \n" + << " \n" + << " \n" + << " \n"; + } + if (!config->manifestDependency.empty()) { + os << " \n" + << " \n" + << " manifestDependency << " />\n" + << " \n" + << " \n"; + } + os << "\n"; + return os.str(); +} + +static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { + std::unique_ptr defaultXmlCopy = + MemoryBuffer::getMemBufferCopy(defaultXml); + + windows_manifest::WindowsManifestMerger merger; + if (auto e = merger.merge(*defaultXmlCopy.get())) + fatal("internal manifest tool failed on default xml: " + + toString(std::move(e))); + + for (StringRef filename : config->manifestInput) { + std::unique_ptr manifest = + check(MemoryBuffer::getFile(filename)); + if (auto e = merger.merge(*manifest.get())) + fatal("internal manifest tool failed on file " + filename + ": " + + toString(std::move(e))); + } + + return merger.getMergedManifest().get()->getBuffer(); +} + +static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { + // Create the default manifest file as a temporary file. + TemporaryFile Default("defaultxml", "manifest"); + std::error_code ec; + raw_fd_ostream os(Default.path, ec, sys::fs::F_Text); + if (ec) + fatal("failed to open " + Default.path + ": " + ec.message()); + os << defaultXml; + os.close(); + + // Merge user-supplied manifests if they are given. Since libxml2 is not + // enabled, we must shell out to Microsoft's mt.exe tool. + TemporaryFile user("user", "manifest"); + + Executor e("mt.exe"); + e.add("/manifest"); + e.add(Default.path); + for (StringRef filename : config->manifestInput) { + e.add("/manifest"); + e.add(filename); + } + e.add("/nologo"); + e.add("/out:" + StringRef(user.path)); + e.run(); + + return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) + .get() + ->getBuffer(); +} + +static std::string createManifestXml() { + std::string defaultXml = createDefaultXml(); + if (config->manifestInput.empty()) + return defaultXml; + + if (windows_manifest::isAvailable()) + return createManifestXmlWithInternalMt(defaultXml); + + return createManifestXmlWithExternalMt(defaultXml); +} + +static std::unique_ptr +createMemoryBufferForManifestRes(size_t manifestSize) { + size_t resSize = alignTo( + object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + + sizeof(object::WinResHeaderSuffix) + manifestSize, + object::WIN_RES_DATA_ALIGNMENT); + return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile + + ".manifest.res"); +} + +static void writeResFileHeader(char *&buf) { + memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); + buf += sizeof(COFF::WinResMagic); + memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); + buf += object::WIN_RES_NULL_ENTRY_SIZE; +} + +static void writeResEntryHeader(char *&buf, size_t manifestSize) { + // Write the prefix. + auto *prefix = reinterpret_cast(buf); + prefix->DataSize = manifestSize; + prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + + sizeof(object::WinResIDs) + + sizeof(object::WinResHeaderSuffix); + buf += sizeof(object::WinResHeaderPrefix); + + // Write the Type/Name IDs. + auto *iDs = reinterpret_cast(buf); + iDs->setType(RT_MANIFEST); + iDs->setName(config->manifestID); + buf += sizeof(object::WinResIDs); + + // Write the suffix. + auto *suffix = reinterpret_cast(buf); + suffix->DataVersion = 0; + suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; + suffix->Language = SUBLANG_ENGLISH_US; + suffix->Version = 0; + suffix->Characteristics = 0; + buf += sizeof(object::WinResHeaderSuffix); +} + +// Create a resource file containing a manifest XML. +std::unique_ptr createManifestRes() { + std::string manifest = createManifestXml(); + + std::unique_ptr res = + createMemoryBufferForManifestRes(manifest.size()); + + char *buf = res->getBufferStart(); + writeResFileHeader(buf); + writeResEntryHeader(buf, manifest.size()); + + // Copy the manifest data into the .res file. + std::copy(manifest.begin(), manifest.end(), buf); + return std::move(res); +} + +void createSideBySideManifest() { + std::string path = config->manifestFile; + if (path == "") + path = config->outputFile + ".manifest"; + std::error_code ec; + raw_fd_ostream out(path, ec, sys::fs::F_Text); + if (ec) + fatal("failed to create manifest: " + ec.message()); + out << createManifestXml(); +} + +// Parse a string in the form of +// "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" +// or "=.". +// Used for parsing /export arguments. +Export parseExport(StringRef arg) { + Export e; + StringRef rest; + std::tie(e.name, rest) = arg.split(","); + if (e.name.empty()) + goto err; + + if (e.name.contains('=')) { + StringRef x, y; + std::tie(x, y) = e.name.split("="); + + // If "=.". + if (y.contains(".")) { + e.name = x; + e.forwardTo = y; + return e; + } + + e.extName = x; + e.name = y; + if (e.name.empty()) + goto err; + } + + // If "=[,@ordinal[,NONAME]][,DATA][,PRIVATE]" + while (!rest.empty()) { + StringRef tok; + std::tie(tok, rest) = rest.split(","); + if (tok.equals_lower("noname")) { + if (e.ordinal == 0) + goto err; + e.noname = true; + continue; + } + if (tok.equals_lower("data")) { + e.data = true; + continue; + } + if (tok.equals_lower("constant")) { + e.constant = true; + continue; + } + if (tok.equals_lower("private")) { + e.isPrivate = true; + continue; + } + if (tok.startswith("@")) { + int32_t ord; + if (tok.substr(1).getAsInteger(0, ord)) + goto err; + if (ord <= 0 || 65535 < ord) + goto err; + e.ordinal = ord; + continue; + } + goto err; + } + return e; + +err: + fatal("invalid /export: " + arg); +} + +static StringRef undecorate(StringRef sym) { + if (config->machine != I386) + return sym; + // In MSVC mode, a fully decorated stdcall function is exported + // as-is with the leading underscore (with type IMPORT_NAME). + // In MinGW mode, a decorated stdcall function gets the underscore + // removed, just like normal cdecl functions. + if (sym.startswith("_") && sym.contains('@') && !config->mingw) + return sym; + return sym.startswith("_") ? sym.substr(1) : sym; +} + +// Convert stdcall/fastcall style symbols into unsuffixed symbols, +// with or without a leading underscore. (MinGW specific.) +static StringRef killAt(StringRef sym, bool prefix) { + if (sym.empty()) + return sym; + // Strip any trailing stdcall suffix + sym = sym.substr(0, sym.find('@', 1)); + if (!sym.startswith("@")) { + if (prefix && !sym.startswith("_")) + return saver.save("_" + sym); + return sym; + } + // For fastcall, remove the leading @ and replace it with an + // underscore, if prefixes are used. + sym = sym.substr(1); + if (prefix) + sym = saver.save("_" + sym); + return sym; +} + +// Performs error checking on all /export arguments. +// It also sets ordinals. +void fixupExports() { + // Symbol ordinals must be unique. + std::set ords; + for (Export &e : config->exports) { + if (e.ordinal == 0) + continue; + if (!ords.insert(e.ordinal).second) + fatal("duplicate export ordinal: " + e.name); + } + + for (Export &e : config->exports) { + if (!e.forwardTo.empty()) { + e.exportName = undecorate(e.name); + } else { + e.exportName = undecorate(e.extName.empty() ? e.name : e.extName); + } + } + + if (config->killAt && config->machine == I386) { + for (Export &e : config->exports) { + e.name = killAt(e.name, true); + e.exportName = killAt(e.exportName, false); + e.extName = killAt(e.extName, true); + e.symbolName = killAt(e.symbolName, true); + } + } + + // Uniquefy by name. + DenseMap map(config->exports.size()); + std::vector v; + for (Export &e : config->exports) { + auto pair = map.insert(std::make_pair(e.exportName, &e)); + bool inserted = pair.second; + if (inserted) { + v.push_back(e); + continue; + } + Export *existing = pair.first->second; + if (e == *existing || e.name != existing->name) + continue; + warn("duplicate /export option: " + e.name); + } + config->exports = std::move(v); + + // Sort by name. + std::sort(config->exports.begin(), config->exports.end(), + [](const Export &a, const Export &b) { + return a.exportName < b.exportName; + }); +} + +void assignExportOrdinals() { + // Assign unique ordinals if default (= 0). + uint16_t max = 0; + for (Export &e : config->exports) + max = std::max(max, e.ordinal); + for (Export &e : config->exports) + if (e.ordinal == 0) + e.ordinal = ++max; +} + +// Parses a string in the form of "key=value" and check +// if value matches previous values for the same key. +void checkFailIfMismatch(StringRef arg, InputFile *source) { + StringRef k, v; + std::tie(k, v) = arg.split('='); + if (k.empty() || v.empty()) + fatal("/failifmismatch: invalid argument: " + arg); + std::pair existing = config->mustMatch[k]; + if (!existing.first.empty() && v != existing.first) { + std::string sourceStr = source ? toString(source) : "cmd-line"; + std::string existingStr = + existing.second ? toString(existing.second) : "cmd-line"; + fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " + + existingStr + " has value " + existing.first + "\n>>> " + sourceStr + + " has value " + v); + } + config->mustMatch[k] = {v, source}; +} + +// Convert Windows resource files (.res files) to a .obj file. +// Does what cvtres.exe does, but in-process and cross-platform. +MemoryBufferRef convertResToCOFF(ArrayRef mbs) { + object::WindowsResourceParser parser; + + for (MemoryBufferRef mb : mbs) { + std::unique_ptr bin = check(object::createBinary(mb)); + object::WindowsResource *rf = dyn_cast(bin.get()); + if (!rf) + fatal("cannot compile non-resource file as resource"); + + std::vector duplicates; + if (auto ec = parser.parse(rf, duplicates)) + fatal(toString(std::move(ec))); + + for (const auto &dupeDiag : duplicates) + if (config->forceMultipleRes) + warn(dupeDiag); + else + error(dupeDiag); + } + + Expected> e = + llvm::object::writeWindowsResourceCOFF(config->machine, parser, + config->timestamp); + if (!e) + fatal("failed to write .res to COFF: " + toString(e.takeError())); + + MemoryBufferRef mbref = **e; + make>(std::move(*e)); // take ownership + return mbref; +} + +// Create OptTable + +// Create prefix string literals used in Options.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +// Create table mapping all options defined in Options.td +static const llvm::opt::OptTable::Info infoTable[] = { +#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ + {X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \ + X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, +#include "Options.inc" +#undef OPTION +}; + +COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} + +// Set color diagnostics according to --color-diagnostics={auto,always,never} +// or --no-color-diagnostics flags. +static void handleColorDiagnostics(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, + OPT_no_color_diagnostics); + if (!arg) + return; + if (arg->getOption().getID() == OPT_color_diagnostics) { + errorHandler().colorDiagnostics = true; + } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { + errorHandler().colorDiagnostics = false; + } else { + StringRef s = arg->getValue(); + if (s == "always") + errorHandler().colorDiagnostics = true; + else if (s == "never") + errorHandler().colorDiagnostics = false; + else if (s != "auto") + error("unknown option: --color-diagnostics=" + s); + } +} + +static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { + StringRef s = arg->getValue(); + if (s != "windows" && s != "posix") + error("invalid response file quoting: " + s); + if (s == "windows") + return cl::TokenizeWindowsCommandLine; + return cl::TokenizeGNUCommandLine; + } + // The COFF linker always defaults to Windows quoting. + return cl::TokenizeWindowsCommandLine; +} + +// Parses a given list of options. +opt::InputArgList ArgParser::parse(ArrayRef argv) { + // Make InputArgList from string vectors. + unsigned missingIndex; + unsigned missingCount; + + // We need to get the quoting style for response files before parsing all + // options so we parse here before and ignore all the options but + // --rsp-quoting. + opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); + + // Expand response files (arguments in the form of @) + // and then parse the argument again. + SmallVector expandedArgv(argv.data(), + argv.data() + argv.size()); + cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); + args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, + missingCount); + + // Print the real command line if response files are expanded. + if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { + std::string msg = "Command line:"; + for (const char *s : expandedArgv) + msg += " " + std::string(s); + message(msg); + } + + // Save the command line after response file expansion so we can write it to + // the PDB if necessary. + config->argv = {expandedArgv.begin(), expandedArgv.end()}; + + // Handle /WX early since it converts missing argument warnings to errors. + errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); + + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); + + handleColorDiagnostics(args); + + for (auto *arg : args.filtered(OPT_UNKNOWN)) { + std::string nearest; + if (table.findNearest(arg->getAsString(args), nearest) > 1) + warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); + else + warn("ignoring unknown argument '" + arg->getAsString(args) + + "', did you mean '" + nearest + "'"); + } + + if (args.hasArg(OPT_lib)) + warn("ignoring /lib since it's not the first argument"); + + return args; +} + +// Tokenizes and parses a given string as command line in .drective section. +// /EXPORT options are processed in fastpath. +std::pair> +ArgParser::parseDirectives(StringRef s) { + std::vector exports; + SmallVector rest; + + for (StringRef tok : tokenize(s)) { + if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:")) + exports.push_back(tok.substr(strlen("/export:"))); + else + rest.push_back(tok.data()); + } + + // Make InputArgList from unparsed string vectors. + unsigned missingIndex; + unsigned missingCount; + + opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount); + + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); + for (auto *arg : args.filtered(OPT_UNKNOWN)) + warn("ignoring unknown argument: " + arg->getAsString(args)); + return {std::move(args), std::move(exports)}; +} + +// link.exe has an interesting feature. If LINK or _LINK_ environment +// variables exist, their contents are handled as command line strings. +// So you can pass extra arguments using them. +opt::InputArgList ArgParser::parseLINK(std::vector argv) { + // Concatenate LINK env and command line arguments, and then parse them. + if (Optional s = Process::GetEnv("LINK")) { + std::vector v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); + } + if (Optional s = Process::GetEnv("_LINK_")) { + std::vector v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); + } + return parse(argv); +} + +std::vector ArgParser::tokenize(StringRef s) { + SmallVector tokens; + cl::TokenizeWindowsCommandLine(s, saver, tokens); + return std::vector(tokens.begin(), tokens.end()); +} + +void printHelp(const char *argv0) { + COFFOptTable().PrintHelp(outs(), + (std::string(argv0) + " [options] file...").c_str(), + "LLVM Linker", false); +} + +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/DriverUtils.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/ICF.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/ICF.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/ICF.cpp (revision 352529) @@ -0,0 +1,317 @@ +//===- ICF.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// ICF is short for Identical Code Folding. That is a size optimization to +// identify and merge two or more read-only sections (typically functions) +// that happened to have the same contents. It usually reduces output size +// by a few percent. +// +// On Windows, ICF is enabled by default. +// +// See ELF/ICF.cpp for the details about the algortihm. +// +//===----------------------------------------------------------------------===// + +#include "ICF.h" +#include "Chunks.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Threads.h" +#include "lld/Common/Timer.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" +#include +#include +#include + +using namespace llvm; + +namespace lld { +namespace coff { + +static Timer icfTimer("ICF", Timer::root()); + +class ICF { +public: + void run(ArrayRef v); + +private: + void segregate(size_t begin, size_t end, bool constant); + + bool assocEquals(const SectionChunk *a, const SectionChunk *b); + + bool equalsConstant(const SectionChunk *a, const SectionChunk *b); + bool equalsVariable(const SectionChunk *a, const SectionChunk *b); + + bool isEligible(SectionChunk *c); + + size_t findBoundary(size_t begin, size_t end); + + void forEachClassRange(size_t begin, size_t end, + std::function fn); + + void forEachClass(std::function fn); + + std::vector chunks; + int cnt = 0; + std::atomic repeat = {false}; +}; + +// Returns true if section S is subject of ICF. +// +// Microsoft's documentation +// (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April +// 2017) says that /opt:icf folds both functions and read-only data. +// Despite that, the MSVC linker folds only functions. We found +// a few instances of programs that are not safe for data merging. +// Therefore, we merge only functions just like the MSVC tool. However, we also +// merge read-only sections in a couple of cases where the address of the +// section is insignificant to the user program and the behaviour matches that +// of the Visual C++ linker. +bool ICF::isEligible(SectionChunk *c) { + // Non-comdat chunks, dead chunks, and writable chunks are not elegible. + bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; + if (!c->isCOMDAT() || !c->live || writable) + return false; + + // Code sections are eligible. + if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) + return true; + + // .pdata and .xdata unwind info sections are eligible. + StringRef outSecName = c->getSectionName().split('$').first; + if (outSecName == ".pdata" || outSecName == ".xdata") + return true; + + // So are vtables. + if (c->sym && c->sym->getName().startswith("??_7")) + return true; + + // Anything else not in an address-significance table is eligible. + return !c->keepUnique; +} + +// Split an equivalence class into smaller classes. +void ICF::segregate(size_t begin, size_t end, bool constant) { + while (begin < end) { + // Divide [Begin, End) into two. Let Mid be the start index of the + // second group. + auto bound = std::stable_partition( + chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) { + if (constant) + return equalsConstant(chunks[begin], s); + return equalsVariable(chunks[begin], s); + }); + size_t mid = bound - chunks.begin(); + + // Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an + // equivalence class ID because every group ends with a unique index. + for (size_t i = begin; i < mid; ++i) + chunks[i]->eqClass[(cnt + 1) % 2] = mid; + + // If we created a group, we need to iterate the main loop again. + if (mid != end) + repeat = true; + + begin = mid; + } +} + +// Returns true if two sections' associative children are equal. +bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { + auto childClasses = [&](const SectionChunk *sc) { + std::vector classes; + for (const SectionChunk &c : sc->children()) + if (!c.getSectionName().startswith(".debug") && + c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y") + classes.push_back(c.eqClass[cnt % 2]); + return classes; + }; + return childClasses(a) == childClasses(b); +} + +// Compare "non-moving" part of two sections, namely everything +// except relocation targets. +bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) { + if (a->relocsSize != b->relocsSize) + return false; + + // Compare relocations. + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + if (r1.Type != r2.Type || + r1.VirtualAddress != r2.VirtualAddress) { + return false; + } + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + if (b1 == b2) + return true; + if (auto *d1 = dyn_cast(b1)) + if (auto *d2 = dyn_cast(b2)) + return d1->getValue() == d2->getValue() && + d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; + return false; + }; + if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(), + b->getRelocs().begin(), eq)) + return false; + + // Compare section attributes and contents. + return a->getOutputCharacteristics() == b->getOutputCharacteristics() && + a->getSectionName() == b->getSectionName() && + a->header->SizeOfRawData == b->header->SizeOfRawData && + a->checksum == b->checksum && a->getContents() == b->getContents() && + assocEquals(a, b); +} + +// Compare "moving" part of two sections, namely relocation targets. +bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) { + // Compare relocations. + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + if (b1 == b2) + return true; + if (auto *d1 = dyn_cast(b1)) + if (auto *d2 = dyn_cast(b2)) + return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; + return false; + }; + return std::equal(a->getRelocs().begin(), a->getRelocs().end(), + b->getRelocs().begin(), eq) && + assocEquals(a, b); +} + +// Find the first Chunk after Begin that has a different class from Begin. +size_t ICF::findBoundary(size_t begin, size_t end) { + for (size_t i = begin + 1; i < end; ++i) + if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2]) + return i; + return end; +} + +void ICF::forEachClassRange(size_t begin, size_t end, + std::function fn) { + while (begin < end) { + size_t mid = findBoundary(begin, end); + fn(begin, mid); + begin = mid; + } +} + +// Call Fn on each class group. +void ICF::forEachClass(std::function fn) { + // If the number of sections are too small to use threading, + // call Fn sequentially. + if (chunks.size() < 1024) { + forEachClassRange(0, chunks.size(), fn); + ++cnt; + return; + } + + // Shard into non-overlapping intervals, and call Fn in parallel. + // The sharding must be completed before any calls to Fn are made + // so that Fn can modify the Chunks in its shard without causing data + // races. + const size_t numShards = 256; + size_t step = chunks.size() / numShards; + size_t boundaries[numShards + 1]; + boundaries[0] = 0; + boundaries[numShards] = chunks.size(); + parallelForEachN(1, numShards, [&](size_t i) { + boundaries[i] = findBoundary((i - 1) * step, chunks.size()); + }); + parallelForEachN(1, numShards + 1, [&](size_t i) { + if (boundaries[i - 1] < boundaries[i]) { + forEachClassRange(boundaries[i - 1], boundaries[i], fn); + } + }); + ++cnt; +} + +// Merge identical COMDAT sections. +// Two sections are considered the same if their section headers, +// contents and relocations are all the same. +void ICF::run(ArrayRef vec) { + ScopedTimer t(icfTimer); + + // Collect only mergeable sections and group by hash value. + uint32_t nextId = 1; + for (Chunk *c : vec) { + if (auto *sc = dyn_cast(c)) { + if (isEligible(sc)) + chunks.push_back(sc); + else + sc->eqClass[0] = nextId++; + } + } + + // Make sure that ICF doesn't merge sections that are being handled by string + // tail merging. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + for (SectionChunk *sc : mc->sections) + sc->eqClass[0] = nextId++; + + // Initially, we use hash values to partition sections. + parallelForEach(chunks, [&](SectionChunk *sc) { + sc->eqClass[0] = xxHash64(sc->getContents()); + }); + + // Combine the hashes of the sections referenced by each section into its + // hash. + for (unsigned cnt = 0; cnt != 2; ++cnt) { + parallelForEach(chunks, [&](SectionChunk *sc) { + uint32_t hash = sc->eqClass[cnt % 2]; + for (Symbol *b : sc->symbols()) + if (auto *sym = dyn_cast_or_null(b)) + hash += sym->getChunk()->eqClass[cnt % 2]; + // Set MSB to 1 to avoid collisions with non-hash classs. + sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31); + }); + } + + // From now on, sections in Chunks are ordered so that sections in + // the same group are consecutive in the vector. + llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) { + return a->eqClass[0] < b->eqClass[0]; + }); + + // Compare static contents and assign unique IDs for each static content. + forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); }); + + // Split groups by comparing relocations until convergence is obtained. + do { + repeat = false; + forEachClass( + [&](size_t begin, size_t end) { segregate(begin, end, false); }); + } while (repeat); + + log("ICF needed " + Twine(cnt) + " iterations"); + + // Merge sections in the same classs. + forEachClass([&](size_t begin, size_t end) { + if (end - begin == 1) + return; + + log("Selected " + chunks[begin]->getDebugName()); + for (size_t i = begin + 1; i < end; ++i) { + log(" Removed " + chunks[i]->getDebugName()); + chunks[begin]->replace(chunks[i]); + } + }); +} + +// Entry point to ICF. +void doICF(ArrayRef chunks) { ICF().run(chunks); } + +} // namespace coff +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/COFF/ICF.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/ICF.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/ICF.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/ICF.h (revision 352529) @@ -0,0 +1,25 @@ +//===- ICF.h --------------------------------------------------------------===// +// +// 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_COFF_ICF_H +#define LLD_COFF_ICF_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { + +class Chunk; + +void doICF(ArrayRef chunks); + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/ICF.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/LTO.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/LTO.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/LTO.cpp (revision 352529) @@ -0,0 +1,206 @@ +//===- LTO.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "LTO.h" +#include "Config.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "lld/Common/Args.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Strings.h" +#include "lld/Common/TargetOptionsCommandFlags.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/LTO/Caching.h" +#include "llvm/LTO/Config.h" +#include "llvm/LTO/LTO.h" +#include "llvm/Object/SymbolicFile.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +// Creates an empty file to and returns a raw_fd_ostream to write to it. +static std::unique_ptr openFile(StringRef file) { + std::error_code ec; + auto ret = + llvm::make_unique(file, ec, sys::fs::OpenFlags::F_None); + if (ec) { + error("cannot open " + file + ": " + ec.message()); + return nullptr; + } + return ret; +} + +static std::string getThinLTOOutputFile(StringRef path) { + return lto::getThinLTOOutputFile(path, + config->thinLTOPrefixReplace.first, + config->thinLTOPrefixReplace.second); +} + +static lto::Config createConfig() { + lto::Config c; + c.Options = initTargetOptionsFromCodeGenFlags(); + + // Always emit a section per function/datum with LTO. LLVM LTO should get most + // of the benefit of linker GC, but there are still opportunities for ICF. + c.Options.FunctionSections = true; + c.Options.DataSections = true; + + // Use static reloc model on 32-bit x86 because it usually results in more + // compact code, and because there are also known code generation bugs when + // using the PIC model (see PR34306). + if (config->machine == COFF::IMAGE_FILE_MACHINE_I386) + c.RelocModel = Reloc::Static; + else + c.RelocModel = Reloc::PIC_; + c.DisableVerify = true; + c.DiagHandler = diagnosticHandler; + c.OptLevel = config->ltoo; + c.CPU = getCPUStr(); + c.MAttrs = getMAttrs(); + c.CGOptLevel = args::getCGOptLevel(config->ltoo); + + if (config->saveTemps) + checkError(c.addSaveTemps(std::string(config->outputFile) + ".", + /*UseInputModulePath*/ true)); + return c; +} + +BitcodeCompiler::BitcodeCompiler() { + // Initialize indexFile. + if (!config->thinLTOIndexOnlyArg.empty()) + indexFile = openFile(config->thinLTOIndexOnlyArg); + + // Initialize ltoObj. + lto::ThinBackend backend; + if (config->thinLTOIndexOnly) { + auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; + backend = lto::createWriteIndexesThinBackend( + config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second, + config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); + } else if (config->thinLTOJobs != 0) { + backend = lto::createInProcessThinBackend(config->thinLTOJobs); + } + + ltoObj = llvm::make_unique(createConfig(), backend, + config->ltoPartitions); +} + +BitcodeCompiler::~BitcodeCompiler() = default; + +static void undefine(Symbol *s) { replaceSymbol(s, s->getName()); } + +void BitcodeCompiler::add(BitcodeFile &f) { + lto::InputFile &obj = *f.obj; + unsigned symNum = 0; + std::vector symBodies = f.getSymbols(); + std::vector resols(symBodies.size()); + + if (config->thinLTOIndexOnly) + thinIndices.insert(obj.getName()); + + // Provide a resolution to the LTO API for each symbol. + for (const lto::InputFile::Symbol &objSym : obj.symbols()) { + Symbol *sym = symBodies[symNum]; + lto::SymbolResolution &r = resols[symNum]; + ++symNum; + + // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile + // reports two symbols for module ASM defined. Without this check, lld + // flags an undefined in IR with a definition in ASM as prevailing. + // Once IRObjectFile is fixed to report only one symbol this hack can + // be removed. + r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; + r.VisibleToRegularObj = sym->isUsedInRegularObj; + if (r.Prevailing) + undefine(sym); + } + checkError(ltoObj->add(std::move(f.obj), resols)); +} + +// Merge all the bitcode files we have seen, codegen the result +// and return the resulting objects. +std::vector BitcodeCompiler::compile() { + unsigned maxTasks = ltoObj->getMaxTasks(); + buf.resize(maxTasks); + files.resize(maxTasks); + + // The /lldltocache option specifies the path to a directory in which to cache + // native object files for ThinLTO incremental builds. If a path was + // specified, configure LTO to use it as the cache directory. + lto::NativeObjectCache cache; + if (!config->ltoCache.empty()) + cache = check(lto::localCache( + config->ltoCache, [&](size_t task, std::unique_ptr mb) { + files[task] = std::move(mb); + })); + + checkError(ltoObj->run( + [&](size_t task) { + return llvm::make_unique( + llvm::make_unique(buf[task])); + }, + cache)); + + // Emit empty index files for non-indexed files + for (StringRef s : thinIndices) { + std::string path = getThinLTOOutputFile(s); + openFile(path + ".thinlto.bc"); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } + + // ThinLTO with index only option is required to generate only the index + // files. After that, we exit from linker and ThinLTO backend runs in a + // distributed environment. + if (config->thinLTOIndexOnly) { + if (indexFile) + indexFile->close(); + return {}; + } + + if (!config->ltoCache.empty()) + pruneCache(config->ltoCache, config->ltoCachePolicy); + + std::vector ret; + for (unsigned i = 0; i != maxTasks; ++i) { + if (buf[i].empty()) + continue; + if (config->saveTemps) { + if (i == 0) + saveBuffer(buf[i], config->outputFile + ".lto.obj"); + else + saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj"); + } + ret.emplace_back(buf[i].data(), buf[i].size()); + } + + for (std::unique_ptr &file : files) + if (file) + ret.push_back(file->getBuffer()); + + return ret; +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/LTO.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/LTO.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/LTO.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/LTO.h (revision 352529) @@ -0,0 +1,60 @@ +//===- LTO.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 +// +//===----------------------------------------------------------------------===// +// +// This file provides a way to combine bitcode files into one COFF +// file by compiling them using LLVM. +// +// If LTO is in use, your input files are not in regular COFF files +// but instead LLVM bitcode files. In that case, the linker has to +// convert bitcode files into the native format so that we can create +// a COFF file that contains native code. This file provides that +// functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_LTO_H +#define LLD_COFF_LTO_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace llvm { +namespace lto { +class LTO; +} +} + +namespace lld { +namespace coff { + +class BitcodeFile; +class InputFile; + +class BitcodeCompiler { +public: + BitcodeCompiler(); + ~BitcodeCompiler(); + + void add(BitcodeFile &f); + std::vector compile(); + +private: + std::unique_ptr ltoObj; + std::vector> buf; + std::vector> files; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; +}; +} +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/LTO.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/MapFile.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/MapFile.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/MapFile.cpp (revision 352529) @@ -0,0 +1,124 @@ +//===- MapFile.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the /lldmap option. It shows lists in order and +// hierarchically the output sections, input sections, input files and +// symbol: +// +// Address Size Align Out File Symbol +// 00201000 00000015 4 .text +// 00201000 0000000e 4 test.o:(.text) +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Writer.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Threads.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +using SymbolMapTy = + DenseMap>; + +static const std::string indent8 = " "; // 8 spaces +static const std::string indent16 = " "; // 16 spaces + +// Print out the first three columns of a line. +static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, + uint64_t align) { + os << format("%08llx %08llx %5lld ", addr, size, align); +} + +// Returns a list of all symbols that we want to print out. +static std::vector getSymbols() { + std::vector v; + for (ObjFile *file : ObjFile::instances) + for (Symbol *b : file->getSymbols()) + if (auto *sym = dyn_cast_or_null(b)) + if (sym && !sym->getCOFFSymbol().isSectionDefinition()) + v.push_back(sym); + return v; +} + +// Returns a map from sections to their symbols. +static SymbolMapTy getSectionSyms(ArrayRef syms) { + SymbolMapTy ret; + for (DefinedRegular *s : syms) + ret[s->getChunk()].push_back(s); + + // Sort symbols by address. + for (auto &it : ret) { + SmallVectorImpl &v = it.second; + std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) { + return a->getRVA() < b->getRVA(); + }); + } + return ret; +} + +// Construct a map from symbols to their stringified representations. +static DenseMap +getSymbolStrings(ArrayRef syms) { + std::vector str(syms.size()); + parallelForEachN((size_t)0, syms.size(), [&](size_t i) { + raw_string_ostream os(str[i]); + writeHeader(os, syms[i]->getRVA(), 0, 0); + os << indent16 << toString(*syms[i]); + }); + + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = std::move(str[i]); + return ret; +} + +void coff::writeMapFile(ArrayRef outputSections) { + if (config->mapFile.empty()) + return; + + std::error_code ec; + raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None); + if (ec) + fatal("cannot open " + config->mapFile + ": " + ec.message()); + + // Collect symbol info that we want to print out. + std::vector syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap symStr = getSymbolStrings(syms); + + // Print out the header line. + os << "Address Size Align Out In Symbol\n"; + + // Print out file contents. + for (OutputSection *sec : outputSections) { + writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); + os << sec->name << '\n'; + + for (Chunk *c : sec->chunks) { + auto *sc = dyn_cast(c); + if (!sc) + continue; + + writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment()); + os << indent8 << sc->file->getName() << ":(" << sc->getSectionName() + << ")\n"; + for (DefinedRegular *sym : sectionSyms[sc]) + os << symStr[sym] << '\n'; + } + } +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/MapFile.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/MapFile.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/MapFile.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/MapFile.h (revision 352529) @@ -0,0 +1,21 @@ +//===- MapFile.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_COFF_MAPFILE_H +#define LLD_COFF_MAPFILE_H + +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { +class OutputSection; +void writeMapFile(llvm::ArrayRef outputSections); +} +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/MapFile.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/MarkLive.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/MarkLive.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/MarkLive.cpp (revision 352529) @@ -0,0 +1,73 @@ +//===- MarkLive.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Chunks.h" +#include "Symbols.h" +#include "lld/Common/Timer.h" +#include "llvm/ADT/STLExtras.h" +#include + +namespace lld { +namespace coff { + +static Timer gctimer("GC", Timer::root()); + +// Set live bit on for each reachable chunk. Unmarked (unreachable) +// COMDAT chunks will be ignored by Writer, so they will be excluded +// from the final output. +void markLive(ArrayRef chunks) { + ScopedTimer t(gctimer); + + // We build up a worklist of sections which have been marked as live. We only + // push into the worklist when we discover an unmarked section, and we mark + // as we push, so sections never appear twice in the list. + SmallVector worklist; + + // COMDAT section chunks are dead by default. Add non-COMDAT chunks. + for (Chunk *c : chunks) + if (auto *sc = dyn_cast(c)) + if (sc->live) + worklist.push_back(sc); + + auto enqueue = [&](SectionChunk *c) { + if (c->live) + return; + c->live = true; + worklist.push_back(c); + }; + + auto addSym = [&](Symbol *b) { + if (auto *sym = dyn_cast(b)) + enqueue(sym->getChunk()); + else if (auto *sym = dyn_cast(b)) + sym->file->live = true; + else if (auto *sym = dyn_cast(b)) + sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true; + }; + + // Add GC root chunks. + for (Symbol *b : config->gcroot) + addSym(b); + + while (!worklist.empty()) { + SectionChunk *sc = worklist.pop_back_val(); + assert(sc->live && "We mark as live when pushing onto the worklist!"); + + // Mark all symbols listed in the relocation table for this section. + for (Symbol *b : sc->symbols()) + if (b) + addSym(b); + + // Mark associative sections if any. + for (SectionChunk &c : sc->children()) + enqueue(&c); + } +} + +} +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/MarkLive.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/MarkLive.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/MarkLive.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/MarkLive.h (revision 352529) @@ -0,0 +1,25 @@ +//===- MarkLive.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_COFF_MARKLIVE_H +#define LLD_COFF_MARKLIVE_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { + +class Chunk; + +void markLive(ArrayRef chunks); + +} // namespace coff +} // namespace lld + +#endif // LLD_COFF_MARKLIVE_H Property changes on: vendor/lld/lld-release_900-r372316/COFF/MarkLive.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/MinGW.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/MinGW.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/MinGW.cpp (revision 352529) @@ -0,0 +1,166 @@ +//===- MinGW.cpp ----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MinGW.h" +#include "SymbolTable.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::coff; +using namespace llvm; +using namespace llvm::COFF; + +AutoExporter::AutoExporter() { + excludeLibs = { + "libgcc", + "libgcc_s", + "libstdc++", + "libmingw32", + "libmingwex", + "libg2c", + "libsupc++", + "libobjc", + "libgcj", + "libclang_rt.builtins", + "libclang_rt.builtins-aarch64", + "libclang_rt.builtins-arm", + "libclang_rt.builtins-i386", + "libclang_rt.builtins-x86_64", + "libc++", + "libc++abi", + "libunwind", + "libmsvcrt", + "libucrtbase", + }; + + excludeObjects = { + "crt0.o", "crt1.o", "crt1u.o", "crt2.o", "crt2u.o", "dllcrt1.o", + "dllcrt2.o", "gcrt0.o", "gcrt1.o", "gcrt2.o", "crtbegin.o", "crtend.o", + }; + + excludeSymbolPrefixes = { + // Import symbols + "__imp_", + "__IMPORT_DESCRIPTOR_", + // Extra import symbols from GNU import libraries + "__nm_", + // C++ symbols + "__rtti_", + "__builtin_", + // Artifical symbols such as .refptr + ".", + }; + + excludeSymbolSuffixes = { + "_iname", + "_NULL_THUNK_DATA", + }; + + if (config->machine == I386) { + excludeSymbols = { + "__NULL_IMPORT_DESCRIPTOR", + "__pei386_runtime_relocator", + "_do_pseudo_reloc", + "_impure_ptr", + "__impure_ptr", + "__fmode", + "_environ", + "___dso_handle", + // These are the MinGW names that differ from the standard + // ones (lacking an extra underscore). + "_DllMain@12", + "_DllEntryPoint@12", + "_DllMainCRTStartup@12", + }; + excludeSymbolPrefixes.insert("__head_"); + } else { + excludeSymbols = { + "__NULL_IMPORT_DESCRIPTOR", + "_pei386_runtime_relocator", + "do_pseudo_reloc", + "impure_ptr", + "_impure_ptr", + "_fmode", + "environ", + "__dso_handle", + // These are the MinGW names that differ from the standard + // ones (lacking an extra underscore). + "DllMain", + "DllEntryPoint", + "DllMainCRTStartup", + }; + excludeSymbolPrefixes.insert("_head_"); + } +} + +void AutoExporter::addWholeArchive(StringRef path) { + StringRef libName = sys::path::filename(path); + // Drop the file extension, to match the processing below. + libName = libName.substr(0, libName.rfind('.')); + excludeLibs.erase(libName); +} + +bool AutoExporter::shouldExport(Defined *sym) const { + if (!sym || !sym->isLive() || !sym->getChunk()) + return false; + + // Only allow the symbol kinds that make sense to export; in particular, + // disallow import symbols. + if (!isa(sym) && !isa(sym)) + return false; + if (excludeSymbols.count(sym->getName())) + return false; + + for (StringRef prefix : excludeSymbolPrefixes.keys()) + if (sym->getName().startswith(prefix)) + return false; + for (StringRef suffix : excludeSymbolSuffixes.keys()) + if (sym->getName().endswith(suffix)) + return false; + + // If a corresponding __imp_ symbol exists and is defined, don't export it. + if (symtab->find(("__imp_" + sym->getName()).str())) + return false; + + // Check that file is non-null before dereferencing it, symbols not + // originating in regular object files probably shouldn't be exported. + if (!sym->getFile()) + return false; + + StringRef libName = sys::path::filename(sym->getFile()->parentName); + + // Drop the file extension. + libName = libName.substr(0, libName.rfind('.')); + if (!libName.empty()) + return !excludeLibs.count(libName); + + StringRef fileName = sys::path::filename(sym->getFile()->getName()); + return !excludeObjects.count(fileName); +} + +void coff::writeDefFile(StringRef name) { + std::error_code ec; + raw_fd_ostream os(name, ec, sys::fs::F_None); + if (ec) + fatal("cannot open " + name + ": " + ec.message()); + + os << "EXPORTS\n"; + for (Export &e : config->exports) { + os << " " << e.exportName << " " + << "@" << e.ordinal; + if (auto *def = dyn_cast_or_null(e.sym)) { + if (def && def->getChunk() && + !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + os << " DATA"; + } + os << "\n"; + } +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/MinGW.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/MinGW.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/MinGW.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/MinGW.h (revision 352529) @@ -0,0 +1,41 @@ +//===- MinGW.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_COFF_MINGW_H +#define LLD_COFF_MINGW_H + +#include "Config.h" +#include "Symbols.h" +#include "lld/Common/LLVM.h" + +namespace lld { +namespace coff { + +// Logic for deciding what symbols to export, when exporting all +// symbols for MinGW. +class AutoExporter { +public: + AutoExporter(); + + void addWholeArchive(StringRef path); + + llvm::StringSet<> excludeSymbols; + llvm::StringSet<> excludeSymbolPrefixes; + llvm::StringSet<> excludeSymbolSuffixes; + llvm::StringSet<> excludeLibs; + llvm::StringSet<> excludeObjects; + + bool shouldExport(Defined *sym) const; +}; + +void writeDefFile(StringRef name); + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/MinGW.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/Options.td =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Options.td (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Options.td (revision 352529) @@ -0,0 +1,225 @@ +include "llvm/Option/OptParser.td" + +// link.exe accepts options starting with either a dash or a slash. + +// Flag that takes no arguments. +class F : Flag<["/", "-", "/?", "-?"], name>; + +// Flag that takes one argument after ":". +class P : + Joined<["/", "-", "/?", "-?"], name#":">, HelpText; + +// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the +// flag on and using it suffixed by ":no" turns it off. +multiclass B { + def "" : F, HelpText; + def _no : F, HelpText; +} + +def align : P<"align", "Section alignment">; +def aligncomm : P<"aligncomm", "Set common symbol alignment">; +def alternatename : P<"alternatename", "Define weak alias">; +def base : P<"base", "Base address of the program">; +def color_diagnostics: Flag<["--"], "color-diagnostics">, + HelpText<"Use colors in diagnostics">; +def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, + HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; +def defaultlib : P<"defaultlib", "Add the library to the list of input files">; +def delayload : P<"delayload", "Delay loaded DLL name">; +def entry : P<"entry", "Name of entry point symbol">; +def errorlimit : P<"errorlimit", + "Maximum number of errors to emit before stopping (0 = no limit)">; +def export : P<"export", "Export a function">; +// No help text because /failifmismatch is not intended to be used by the user. +def failifmismatch : P<"failifmismatch", "">; +def filealign : P<"filealign", "Section alignment in the output file">; +def functionpadmin : F<"functionpadmin">; +def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">; +def guard : P<"guard", "Control flow guard">; +def heap : P<"heap", "Size of the heap">; +def ignore : P<"ignore", "Specify warning codes to ignore">; +def implib : P<"implib", "Import library name">; +def lib : F<"lib">, + HelpText<"Act like lib.exe; must be first argument if present">; +def libpath : P<"libpath", "Additional library search path">; +def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; +def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">; +def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">; +def lldsavetemps : F<"lldsavetemps">, + HelpText<"Save temporary files instead of deleting them">; +def machine : P<"machine", "Specify target platform">; +def merge : P<"merge", "Combine sections">; +def mllvm : P<"mllvm", "Options to pass to LLVM">; +def nodefaultlib : P<"nodefaultlib", "Remove a default library">; +def opt : P<"opt", "Control optimizations">; +def order : P<"order", "Put functions in order">; +def out : P<"out", "Path to file to write output">; +def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; +def no_color_diagnostics: F<"no-color-diagnostics">, + HelpText<"Do not use colors in diagnostics">; +def pdb : P<"pdb", "PDB file path">; +def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; +def section : P<"section", "Specify section attributes">; +def stack : P<"stack", "Size of the stack">; +def stub : P<"stub", "Specify DOS stub file">; +def subsystem : P<"subsystem", "Specify subsystem">; +def timestamp : P<"timestamp", "Specify the PE header timestamp">; +def version : P<"version", "Specify a version number in the PE header">; +def wholearchive_file : P<"wholearchive", "Include all object files from this archive">; + +def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, + Alias; + +def manifest : F<"manifest">, HelpText<"Create .manifest file">; +def manifest_colon : P< + "manifest", + "NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">; +def manifestuac : P<"manifestuac", "User access control">; +def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">; +def manifestdependency : P< + "manifestdependency", + "Attributes for element in manifest file; implies /manifest">; +def manifestinput : P< + "manifestinput", + "Additional manifest inputs; only valid with /manifest:embed">; + +// We cannot use multiclass P because class name "incl" is different +// from its command line option name. We do this because "include" is +// a reserved keyword in tablegen. +def incl : Joined<["/", "-", "/?", "-?"], "include:">, + HelpText<"Force symbol to be added to symbol table as undefined one">; + +// "def" is also a keyword. +def deffile : Joined<["/", "-", "/?", "-?"], "def:">, + HelpText<"Use module-definition file">; + +def debug : F<"debug">, HelpText<"Embed a symbol table in the image">; +def debug_opt : P<"debug", "Embed a symbol table in the image with option">; +def debugtype : P<"debugtype", "Debug Info Options">; +def dll : F<"dll">, HelpText<"Create a DLL">; +def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">; +def nodefaultlib_all : F<"nodefaultlib">, + HelpText<"Remove all default libraries">; +def noentry : F<"noentry">, + HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">; +def profile : F<"profile">; +def repro : F<"Brepro">, + HelpText<"Use a hash of the executable as the PE header timestamp">; +def swaprun : P<"swaprun", + "Comma-separated list of 'cd' or 'net'">; +def swaprun_cd : F<"swaprun:cd">, Alias, AliasArgs<["cd"]>, + HelpText<"Make loader run output binary from swap instead of from CD">; +def swaprun_net : F<"swaprun:net">, Alias, AliasArgs<["net"]>, + HelpText<"Make loader run output binary from swap instead of from network">; +def verbose : F<"verbose">; +def wholearchive_flag : F<"wholearchive">; + +def force : F<"force">, + HelpText<"Allow undefined and multiply defined symbols when creating executables">; +def force_unresolved : F<"force:unresolved">, + HelpText<"Allow undefined symbols when creating executables">; +def force_multiple : F<"force:multiple">, + HelpText<"Allow multiply defined symbols when creating executables">; +def force_multipleres : F<"force:multipleres">, + HelpText<"Allow multiply defined resources when creating executables">; +defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">; + +defm allowbind : B<"allowbind", "Enable DLL binding (default)", + "Disable DLL binding">; +defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)", + "Disable DLL isolation">; +defm appcontainer : B<"appcontainer", + "Image can only be run in an app container", + "Image can run outside an app container (default)">; +defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)", + "Disable ASLR (default when /fixed)">; +defm fixed : B<"fixed", "Disable base relocations", + "Enable base relocations (default)">; +defm highentropyva : B<"highentropyva", + "Enable 64-bit ASLR (default on 64-bit)", + "Disable 64-bit ASLR">; +defm incremental : B<"incremental", + "Keep original import library if contents are unchanged", + "Overwrite import library even if contents are unchanged">; +defm integritycheck : B<"integritycheck", + "Set FORCE_INTEGRITY bit in PE header", + "No effect (default)">; +defm largeaddressaware : B<"largeaddressaware", + "Enable large addresses (default on 64-bit)", + "Disable large addresses (default on 32-bit)">; +defm nxcompat : B<"nxcompat", "Enable data execution prevention (default)", + "Disable data execution provention">; +defm safeseh : B<"safeseh", + "Produce an image with Safe Exception Handler (only for x86)", + "Don't produce an image with Safe Exception Handler">; +defm tsaware : B<"tsaware", + "Create Terminal Server aware executable (default)", + "Create non-Terminal Server aware executable">; + +def help : F<"help">; + +// /?? and -?? must be before /? and -? to not confuse lib/Options. +def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; + +// LLD extensions +def exclude_all_symbols : F<"exclude-all-symbols">; +def export_all_symbols : F<"export-all-symbols">; +defm demangle : B<"demangle", + "Demangle symbols in output (default)", + "Do not demangle symbols in output">; +def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">, + HelpText<"Add symbol as undefined, but allow it to remain undefined">; +def kill_at : F<"kill-at">; +def lldmingw : F<"lldmingw">; +def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; +def pdb_source_path : P<"pdbsourcepath", + "Base path used to make relative source file path absolute in PDB">; +def rsp_quoting : Joined<["--"], "rsp-quoting=">, + HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def thinlto_emit_imports_files : + F<"thinlto-emit-imports-files">, + HelpText<"Emit .imports files with -thinlto-index-only">; +def thinlto_index_only : + F<"thinlto-index-only">, + HelpText<"Instead of linking, emit ThinLTO index files">; +def thinlto_index_only_arg : P< + "thinlto-index-only", + "-thinlto-index-only and also write native module names to file">; +def thinlto_object_suffix_replace : P< + "thinlto-object-suffix-replace", + "'old;new' replace old suffix with new suffix in ThinLTO index">; +def thinlto_prefix_replace: P< + "thinlto-prefix-replace", + "'old;new' replace old prefix with new prefix in ThinLTO outputs">; +def dash_dash_version : Flag<["--"], "version">, + HelpText<"Print version information">; +defm threads: B<"threads", + "Run the linker multi-threaded (default)", + "Do not run the linker multi-threaded">; + +// Flags for debugging +def lldmap : F<"lldmap">; +def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">; +def show_timing : F<"time">; +def summary : F<"summary">; + +//============================================================================== +// The flags below do nothing. They are defined only for link.exe compatibility. +//============================================================================== + +class QF : Joined<["/", "-", "/?", "-?"], name#":">; + +def ignoreidl : F<"ignoreidl">; +def nologo : F<"nologo">; +def throwingnew : F<"throwingnew">; +def editandcontinue : F<"editandcontinue">; +def fastfail : F<"fastfail">; + +def delay : QF<"delay">; +def errorreport : QF<"errorreport">; +def idlout : QF<"idlout">; +def maxilksize : QF<"maxilksize">; +def tlbid : QF<"tlbid">; +def tlbout : QF<"tlbout">; +def verbose_all : QF<"verbose">; +def guardsym : QF<"guardsym">; Index: vendor/lld/lld-release_900-r372316/COFF/PDB.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/PDB.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/PDB.cpp (revision 352529) @@ -0,0 +1,1836 @@ +//===- PDB.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "PDB.h" +#include "Chunks.h" +#include "Config.h" +#include "DebugTypes.h" +#include "Driver.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "TypeMerger.h" +#include "Writer.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Timer.h" +#include "lld/Common/Threads.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/CVDebugRecord.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/JamCRC.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ScopedPrinter.h" +#include + +using namespace lld; +using namespace lld::coff; +using namespace llvm; +using namespace llvm::codeview; + +using llvm::object::coff_section; + +static ExitOnError exitOnErr; + +static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); + +static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer); +static Timer typeMergingTimer("Type Merging", addObjectsTimer); +static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer); +static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer); +static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer); +static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer); + +namespace { +class DebugSHandler; + +class PDBLinker { + friend DebugSHandler; + +public: + PDBLinker(SymbolTable *symtab) + : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) { + // This isn't strictly necessary, but link.exe usually puts an empty string + // as the first "valid" string in the string table, so we do the same in + // order to maintain as much byte-for-byte compatibility as possible. + pdbStrTab.insert(""); + } + + /// Emit the basic PDB structure: initial streams, headers, etc. + void initialize(llvm::codeview::DebugInfo *buildId); + + /// Add natvis files specified on the command line. + void addNatvisFiles(); + + /// Link CodeView from each object file in the symbol table into the PDB. + void addObjectsToPDB(); + + /// Link info for each import file in the symbol table into the PDB. + void addImportFilesToPDB(ArrayRef outputSections); + + /// Link CodeView from a single object file into the target (output) PDB. + /// When a precompiled headers object is linked, its TPI map might be provided + /// externally. + void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr); + + /// Produce a mapping from the type and item indices used in the object + /// file to those in the destination PDB. + /// + /// If the object file uses a type server PDB (compiled with /Zi), merge TPI + /// and IPI from the type server PDB and return a map for it. Each unique type + /// server PDB is merged at most once, so this may return an existing index + /// mapping. + /// + /// If the object does not use a type server PDB (compiled with /Z7), we merge + /// all the type and item records from the .debug$S stream and fill in the + /// caller-provided objectIndexMap. + Expected mergeDebugT(ObjFile *file, + CVIndexMap *objectIndexMap); + + /// Reads and makes available a PDB. + Expected maybeMergeTypeServerPDB(ObjFile *file); + + /// Merges a precompiled headers TPI map into the current TPI map. The + /// precompiled headers object will also be loaded and remapped in the + /// process. + Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap); + + /// Reads and makes available a precompiled headers object. + /// + /// This is a requirement for objects compiled with cl.exe /Yu. In that + /// case, the referenced object (which was compiled with /Yc) has to be loaded + /// first. This is mainly because the current object's TPI stream has external + /// references to the precompiled headers object. + /// + /// If the precompiled headers object was already loaded, this function will + /// simply return its (remapped) TPI map. + Expected aquirePrecompObj(ObjFile *file); + + /// Adds a precompiled headers object signature -> TPI mapping. + std::pair + registerPrecompiledHeaders(uint32_t signature); + + void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector &stringTableRefs, + BinaryStreamRef symData); + + /// Add the section map and section contributions to the PDB. + void addSections(ArrayRef outputSections, + ArrayRef sectionTable); + + /// Write the PDB to disk and store the Guid generated for it in *Guid. + void commit(codeview::GUID *guid); + + // Print statistics regarding the final PDB + void printStats(); + +private: + BumpPtrAllocator alloc; + + SymbolTable *symtab; + + pdb::PDBFileBuilder builder; + + TypeMerger tMerger; + + /// PDBs use a single global string table for filenames in the file checksum + /// table. + DebugStringTableSubsection pdbStrTab; + + llvm::SmallString<128> nativePath; + + std::vector sectionMap; + + /// Type index mappings of type server PDBs that we've loaded so far. + std::map typeServerIndexMappings; + + /// Type index mappings of precompiled objects type map that we've loaded so + /// far. + std::map precompTypeIndexMappings; + + // For statistics + uint64_t globalSymbols = 0; + uint64_t moduleSymbols = 0; + uint64_t publicSymbols = 0; +}; + +class DebugSHandler { + PDBLinker &linker; + + /// The object file whose .debug$S sections we're processing. + ObjFile &file; + + /// The result of merging type indices. + const CVIndexMap &indexMap; + + /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by + /// index from other records in the .debug$S section. All of these strings + /// need to be added to the global PDB string table, and all references to + /// these strings need to have their indices re-written to refer to the + /// global PDB string table. + DebugStringTableSubsectionRef cVStrTab; + + /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to + /// by other records in the .debug$S section and need to be merged into the + /// PDB. + DebugChecksumsSubsectionRef checksums; + + /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per + /// object file. + DebugInlineeLinesSubsectionRef inlineeLines; + + /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of + /// these and they need not appear in any specific order. However, they + /// contain string table references which need to be re-written, so we + /// collect them all here and re-write them after all subsections have been + /// discovered and processed. + std::vector newFpoFrames; + + /// Pointers to raw memory that we determine have string table references + /// that need to be re-written. We first process all .debug$S subsections + /// to ensure that we can handle subsections written in any order, building + /// up this list as we go. At the end, we use the string table (which must + /// have been discovered by now else it is an error) to re-write these + /// references. + std::vector stringTableReferences; + +public: + DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap) + : linker(linker), file(file), indexMap(indexMap) {} + + void handleDebugS(lld::coff::SectionChunk &debugS); + + std::shared_ptr + mergeInlineeLines(DebugChecksumsSubsection *newChecksums); + + void finish(); +}; +} + +// Visual Studio's debugger requires absolute paths in various places in the +// PDB to work without additional configuration: +// https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box +static void pdbMakeAbsolute(SmallVectorImpl &fileName) { + // The default behavior is to produce paths that are valid within the context + // of the machine that you perform the link on. If the linker is running on + // a POSIX system, we will output absolute POSIX paths. If the linker is + // running on a Windows system, we will output absolute Windows paths. If the + // user desires any other kind of behavior, they should explicitly pass + // /pdbsourcepath, in which case we will treat the exact string the user + // passed in as the gospel and not normalize, canonicalize it. + if (sys::path::is_absolute(fileName, sys::path::Style::windows) || + sys::path::is_absolute(fileName, sys::path::Style::posix)) + return; + + // It's not absolute in any path syntax. Relative paths necessarily refer to + // the local file system, so we can make it native without ending up with a + // nonsensical path. + if (config->pdbSourcePath.empty()) { + sys::path::native(fileName); + sys::fs::make_absolute(fileName); + return; + } + + // Try to guess whether /PDBSOURCEPATH is a unix path or a windows path. + // Since PDB's are more of a Windows thing, we make this conservative and only + // decide that it's a unix path if we're fairly certain. Specifically, if + // it starts with a forward slash. + SmallString<128> absoluteFileName = config->pdbSourcePath; + sys::path::Style guessedStyle = absoluteFileName.startswith("/") + ? sys::path::Style::posix + : sys::path::Style::windows; + sys::path::append(absoluteFileName, guessedStyle, fileName); + sys::path::native(absoluteFileName, guessedStyle); + sys::path::remove_dots(absoluteFileName, true, guessedStyle); + + fileName = std::move(absoluteFileName); +} + +// A COFF .debug$H section is currently a clang extension. This function checks +// if a .debug$H section is in a format that we expect / understand, so that we +// can ignore any sections which are coincidentally also named .debug$H but do +// not contain a format we recognize. +static bool canUseDebugH(ArrayRef debugH) { + if (debugH.size() < sizeof(object::debug_h_header)) + return false; + auto *header = + reinterpret_cast(debugH.data()); + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && + header->Version == 0 && + header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && + (debugH.size() % 8 == 0); +} + +static Optional> getDebugH(ObjFile *file) { + SectionChunk *sec = + SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); + if (!sec) + return llvm::None; + ArrayRef contents = sec->getContents(); + if (!canUseDebugH(contents)) + return None; + return contents; +} + +static ArrayRef +getHashesFromDebugH(ArrayRef debugH) { + assert(canUseDebugH(debugH)); + + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + uint32_t count = debugH.size() / sizeof(GloballyHashedType); + return {reinterpret_cast(debugH.data()), count}; +} + +static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, + TypeCollection &typeTable) { + // Start the TPI or IPI stream header. + tpiBuilder.setVersionHeader(pdb::PdbTpiV80); + + // Flatten the in memory type table and hash each type. + typeTable.ForEachRecord([&](TypeIndex ti, const CVType &type) { + auto hash = pdb::hashTypeRecord(type); + if (auto e = hash.takeError()) + fatal("type hashing error"); + tpiBuilder.addTypeRecord(type.RecordData, *hash); + }); +} + +Expected +PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { + ScopedTimer t(typeMergingTimer); + + if (!file->debugTypesObj) + return *objectIndexMap; // no Types stream + + // Precompiled headers objects need to save the index map for further + // reference by other objects which use the precompiled headers. + if (file->debugTypesObj->kind == TpiSource::PCH) { + uint32_t pchSignature = file->pchSignature.getValueOr(0); + if (pchSignature == 0) + fatal("No signature found for the precompiled headers OBJ (" + + file->getName() + ")"); + + // When a precompiled headers object comes first on the command-line, we + // update the mapping here. Otherwise, if an object referencing the + // precompiled headers object comes first, the mapping is created in + // aquirePrecompObj(), thus we would skip this block. + if (!objectIndexMap->isPrecompiledTypeMap) { + auto r = registerPrecompiledHeaders(pchSignature); + if (r.second) + fatal( + "A precompiled headers OBJ with the same signature was already " + "provided! (" + + file->getName() + ")"); + + objectIndexMap = &r.first; + } + } + + if (file->debugTypesObj->kind == TpiSource::UsingPDB) { + // Look through type servers. If we've already seen this type server, + // don't merge any type information. + return maybeMergeTypeServerPDB(file); + } + + CVTypeArray &types = *file->debugTypes; + + if (file->debugTypesObj->kind == TpiSource::UsingPCH) { + // This object was compiled with /Yu, so process the corresponding + // precompiled headers object (/Yc) first. Some type indices in the current + // object are referencing data in the precompiled headers object, so we need + // both to be loaded. + Error e = mergeInPrecompHeaderObj(file, objectIndexMap); + if (e) + return std::move(e); + + // Drop LF_PRECOMP record from the input stream, as it has been replaced + // with the precompiled headers Type stream in the mergeInPrecompHeaderObj() + // call above. Note that we can't just call Types.drop_front(), as we + // explicitly want to rebase the stream. + CVTypeArray::Iterator firstType = types.begin(); + types.setUnderlyingStream( + types.getUnderlyingStream().drop_front(firstType->RecordData.size())); + } + + // Fill in the temporary, caller-provided ObjectIndexMap. + if (config->debugGHashes) { + ArrayRef hashes; + std::vector ownedHashes; + if (Optional> debugH = getDebugH(file)) + hashes = getHashesFromDebugH(*debugH); + else { + ownedHashes = GloballyHashedType::hashTypes(types); + hashes = ownedHashes; + } + + if (auto err = mergeTypeAndIdRecords( + tMerger.globalIDTable, tMerger.globalTypeTable, + objectIndexMap->tpiMap, types, hashes, file->pchSignature)) + fatal("codeview::mergeTypeAndIdRecords failed: " + + toString(std::move(err))); + } else { + if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable, + objectIndexMap->tpiMap, types, + file->pchSignature)) + fatal("codeview::mergeTypeAndIdRecords failed: " + + toString(std::move(err))); + } + return *objectIndexMap; +} + +Expected PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) { + Expected pdbSession = findTypeServerSource(file); + if (!pdbSession) + return pdbSession.takeError(); + + pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile(); + pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); + + auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap()); + CVIndexMap &indexMap = it.first->second; + if (!it.second) + return indexMap; // already merged + + // Mark this map as a type server map. + indexMap.isTypeServerMap = true; + + Expected expectedTpi = pdbFile.getPDBTpiStream(); + if (auto e = expectedTpi.takeError()) + fatal("Type server does not have TPI stream: " + toString(std::move(e))); + pdb::TpiStream *maybeIpi = nullptr; + if (pdbFile.hasPDBIpiStream()) { + Expected expectedIpi = pdbFile.getPDBIpiStream(); + if (auto e = expectedIpi.takeError()) + fatal("Error getting type server IPI stream: " + toString(std::move(e))); + maybeIpi = &*expectedIpi; + } + + if (config->debugGHashes) { + // PDBs do not actually store global hashes, so when merging a type server + // PDB we have to synthesize global hashes. To do this, we first synthesize + // global hashes for the TPI stream, since it is independent, then we + // synthesize hashes for the IPI stream, using the hashes for the TPI stream + // as inputs. + auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); + Optional endPrecomp; + // Merge TPI first, because the IPI stream will reference type indices. + if (auto err = + mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap, + expectedTpi->typeArray(), tpiHashes, endPrecomp)) + fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + + // Merge IPI. + if (maybeIpi) { + auto ipiHashes = + GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); + if (auto err = + mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap, + indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes)) + fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + } + } else { + // Merge TPI first, because the IPI stream will reference type indices. + if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap, + expectedTpi->typeArray())) + fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + + // Merge IPI. + if (maybeIpi) { + if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap, + indexMap.ipiMap, maybeIpi->typeArray())) + fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + } + } + + return indexMap; +} + +Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file, + CVIndexMap *objectIndexMap) { + const PrecompRecord &precomp = + retrieveDependencyInfo(file->debugTypesObj); + + Expected e = aquirePrecompObj(file); + if (!e) + return e.takeError(); + + const CVIndexMap &precompIndexMap = *e; + assert(precompIndexMap.isPrecompiledTypeMap); + + if (precompIndexMap.tpiMap.empty()) + return Error::success(); + + assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); + assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size()); + // Use the previously remapped index map from the precompiled headers. + objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(), + precompIndexMap.tpiMap.begin() + + precomp.getTypesCount()); + return Error::success(); +} + +static bool equals_path(StringRef path1, StringRef path2) { +#if defined(_WIN32) + return path1.equals_lower(path2); +#else + return path1.equals(path2); +#endif +} + +// Find by name an OBJ provided on the command line +static ObjFile *findObjByName(StringRef fileNameOnly) { + SmallString<128> currentPath; + + for (ObjFile *f : ObjFile::instances) { + StringRef currentFileName = sys::path::filename(f->getName()); + + // Compare based solely on the file name (link.exe behavior) + if (equals_path(currentFileName, fileNameOnly)) + return f; + } + return nullptr; +} + +std::pair +PDBLinker::registerPrecompiledHeaders(uint32_t signature) { + auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()}); + CVIndexMap &indexMap = insertion.first->second; + if (!insertion.second) + return {indexMap, true}; + // Mark this map as a precompiled types map. + indexMap.isPrecompiledTypeMap = true; + return {indexMap, false}; +} + +Expected PDBLinker::aquirePrecompObj(ObjFile *file) { + const PrecompRecord &precomp = + retrieveDependencyInfo(file->debugTypesObj); + + // First, check if we already loaded the precompiled headers object with this + // signature. Return the type index mapping if we've already seen it. + auto r = registerPrecompiledHeaders(precomp.getSignature()); + if (r.second) + return r.first; + + CVIndexMap &indexMap = r.first; + + // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP + // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, + // the paths embedded in the OBJs are in the Windows format. + SmallString<128> precompFileName = sys::path::filename( + precomp.getPrecompFilePath(), sys::path::Style::windows); + + // link.exe requires that a precompiled headers object must always be provided + // on the command-line, even if that's not necessary. + auto precompFile = findObjByName(precompFileName); + if (!precompFile) + return createFileError( + precompFileName.str(), + make_error(pdb::pdb_error_code::external_cmdline_ref)); + + addObjFile(precompFile, &indexMap); + + if (!precompFile->pchSignature) + fatal(precompFile->getName() + " is not a precompiled headers object"); + + if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0)) + return createFileError( + precomp.getPrecompFilePath().str(), + make_error(pdb::pdb_error_code::signature_out_of_date)); + + return indexMap; +} + +static bool remapTypeIndex(TypeIndex &ti, ArrayRef typeIndexMap) { + if (ti.isSimple()) + return true; + if (ti.toArrayIndex() >= typeIndexMap.size()) + return false; + ti = typeIndexMap[ti.toArrayIndex()]; + return true; +} + +static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind, + MutableArrayRef recordBytes, + const CVIndexMap &indexMap, + ArrayRef typeRefs) { + MutableArrayRef contents = + recordBytes.drop_front(sizeof(RecordPrefix)); + for (const TiReference &ref : typeRefs) { + unsigned byteSize = ref.Count * sizeof(TypeIndex); + if (contents.size() < ref.Offset + byteSize) + fatal("symbol record too short"); + + // This can be an item index or a type index. Choose the appropriate map. + ArrayRef typeOrItemMap = indexMap.tpiMap; + bool isItemIndex = ref.Kind == TiRefKind::IndexRef; + if (isItemIndex && indexMap.isTypeServerMap) + typeOrItemMap = indexMap.ipiMap; + + MutableArrayRef tIs( + reinterpret_cast(contents.data() + ref.Offset), ref.Count); + for (TypeIndex &ti : tIs) { + if (!remapTypeIndex(ti, typeOrItemMap)) { + log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " + + file->getName() + " with bad " + (isItemIndex ? "item" : "type") + + " index 0x" + utohexstr(ti.getIndex())); + ti = TypeIndex(SimpleTypeKind::NotTranslated); + continue; + } + } + } +} + +static void +recordStringTableReferenceAtOffset(MutableArrayRef contents, + uint32_t offset, + std::vector &strTableRefs) { + contents = + contents.drop_front(offset).take_front(sizeof(support::ulittle32_t)); + ulittle32_t *index = reinterpret_cast(contents.data()); + strTableRefs.push_back(index); +} + +static void +recordStringTableReferences(SymbolKind kind, MutableArrayRef contents, + std::vector &strTableRefs) { + // For now we only handle S_FILESTATIC, but we may need the same logic for + // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any + // PDBs that contain these types of records, so because of the uncertainty + // they are omitted here until we can prove that it's necessary. + switch (kind) { + case SymbolKind::S_FILESTATIC: + // FileStaticSym::ModFileOffset + recordStringTableReferenceAtOffset(contents, 8, strTableRefs); + break; + case SymbolKind::S_DEFRANGE: + case SymbolKind::S_DEFRANGE_SUBFIELD: + log("Not fixing up string table reference in S_DEFRANGE / " + "S_DEFRANGE_SUBFIELD record"); + break; + default: + break; + } +} + +static SymbolKind symbolKind(ArrayRef recordData) { + const RecordPrefix *prefix = + reinterpret_cast(recordData.data()); + return static_cast(uint16_t(prefix->RecordKind)); +} + +/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 +static void translateIdSymbols(MutableArrayRef &recordData, + TypeCollection &iDTable) { + RecordPrefix *prefix = reinterpret_cast(recordData.data()); + + SymbolKind kind = symbolKind(recordData); + + if (kind == SymbolKind::S_PROC_ID_END) { + prefix->RecordKind = SymbolKind::S_END; + return; + } + + // In an object file, GPROC32_ID has an embedded reference which refers to the + // single object file type index namespace. This has already been translated + // to the PDB file's ID stream index space, but we need to convert this to a + // symbol that refers to the type stream index space. So we remap again from + // ID index space to type index space. + if (kind == SymbolKind::S_GPROC32_ID || kind == SymbolKind::S_LPROC32_ID) { + SmallVector refs; + auto content = recordData.drop_front(sizeof(RecordPrefix)); + CVSymbol sym(recordData); + discoverTypeIndicesInSymbol(sym, refs); + assert(refs.size() == 1); + assert(refs.front().Count == 1); + + TypeIndex *ti = + reinterpret_cast(content.data() + refs[0].Offset); + // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in + // the IPI stream, whose `FunctionType` member refers to the TPI stream. + // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and + // in both cases we just need the second type index. + if (!ti->isSimple() && !ti->isNoneType()) { + CVType funcIdData = iDTable.getType(*ti); + SmallVector indices; + discoverTypeIndices(funcIdData, indices); + assert(indices.size() == 2); + *ti = indices[1]; + } + + kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 + : SymbolKind::S_LPROC32; + prefix->RecordKind = uint16_t(kind); + } +} + +/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. +/// The object file may not be aligned. +static MutableArrayRef +copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef &alignedMem) { + size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + assert(size >= 4 && "record too short"); + assert(size <= MaxRecordLength && "record too long"); + assert(alignedMem.size() >= size && "didn't preallocate enough"); + + // Copy the symbol record and zero out any padding bytes. + MutableArrayRef newData = alignedMem.take_front(size); + alignedMem = alignedMem.drop_front(size); + memcpy(newData.data(), sym.data().data(), sym.length()); + memset(newData.data() + sym.length(), 0, size - sym.length()); + + // Update the record prefix length. It should point to the beginning of the + // next record. + auto *prefix = reinterpret_cast(newData.data()); + prefix->RecordLen = size - 2; + return newData; +} + +struct ScopeRecord { + ulittle32_t ptrParent; + ulittle32_t ptrEnd; +}; + +struct SymbolScope { + ScopeRecord *openingRecord; + uint32_t scopeOffset; +}; + +static void scopeStackOpen(SmallVectorImpl &stack, + uint32_t curOffset, CVSymbol &sym) { + assert(symbolOpensScope(sym.kind())); + SymbolScope s; + s.scopeOffset = curOffset; + s.openingRecord = const_cast( + reinterpret_cast(sym.content().data())); + s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset; + stack.push_back(s); +} + +static void scopeStackClose(SmallVectorImpl &stack, + uint32_t curOffset, InputFile *file) { + if (stack.empty()) { + warn("symbol scopes are not balanced in " + file->getName()); + return; + } + SymbolScope s = stack.pop_back_val(); + s.openingRecord->ptrEnd = curOffset; +} + +static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { + switch (sym.kind()) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_CONSTANT: + // We really should not be seeing S_PROCREF and S_LPROCREF in the first place + // since they are synthesized by the linker in response to S_GPROC32 and + // S_LPROC32, but if we do see them, don't put them in the module stream I + // guess. + case SymbolKind::S_PROCREF: + case SymbolKind::S_LPROCREF: + return false; + // S_UDT records go in the module stream if it is not a global S_UDT. + case SymbolKind::S_UDT: + return !isGlobalScope; + // S_GDATA32 does not go in the module stream, but S_LDATA32 does. + case SymbolKind::S_LDATA32: + default: + return true; + } +} + +static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) { + switch (sym.kind()) { + case SymbolKind::S_CONSTANT: + case SymbolKind::S_GDATA32: + // S_LDATA32 goes in both the module stream and the globals stream. + case SymbolKind::S_LDATA32: + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: + // We really should not be seeing S_PROCREF and S_LPROCREF in the first place + // since they are synthesized by the linker in response to S_GPROC32 and + // S_LPROC32, but if we do see them, copy them straight through. + case SymbolKind::S_PROCREF: + case SymbolKind::S_LPROCREF: + return true; + // S_UDT records go in the globals stream if it is a global S_UDT. + case SymbolKind::S_UDT: + return isGlobalScope; + default: + return false; + } +} + +static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, + unsigned symOffset, const CVSymbol &sym) { + switch (sym.kind()) { + case SymbolKind::S_CONSTANT: + case SymbolKind::S_UDT: + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_PROCREF: + case SymbolKind::S_LPROCREF: + builder.addGlobalSymbol(sym); + break; + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: { + SymbolRecordKind k = SymbolRecordKind::ProcRefSym; + if (sym.kind() == SymbolKind::S_LPROC32) + k = SymbolRecordKind::LocalProcRef; + ProcRefSym ps(k); + ps.Module = modIndex; + // For some reason, MSVC seems to add one to this value. + ++ps.Module; + ps.Name = getSymbolName(sym); + ps.SumName = 0; + ps.SymOffset = symOffset; + builder.addGlobalSymbol(ps); + break; + } + default: + llvm_unreachable("Invalid symbol kind!"); + } +} + +void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector &stringTableRefs, + BinaryStreamRef symData) { + ArrayRef symsBuffer; + cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); + SmallVector scopes; + + // Iterate every symbol to check if any need to be realigned, and if so, how + // much space we need to allocate for them. + bool needsRealignment = false; + unsigned totalRealignedSize = 0; + auto ec = forEachCodeViewRecord( + symsBuffer, [&](CVSymbol sym) -> llvm::Error { + unsigned realignedSize = + alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + needsRealignment |= realignedSize != sym.length(); + totalRealignedSize += realignedSize; + return Error::success(); + }); + + // If any of the symbol record lengths was corrupt, ignore them all, warn + // about it, and move on. + if (ec) { + warn("corrupt symbol records in " + file->getName()); + consumeError(std::move(ec)); + return; + } + + // If any symbol needed realignment, allocate enough contiguous memory for + // them all. Typically symbol subsections are small enough that this will not + // cause fragmentation. + MutableArrayRef alignedSymbolMem; + if (needsRealignment) { + void *alignedData = + alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); + alignedSymbolMem = makeMutableArrayRef( + reinterpret_cast(alignedData), totalRealignedSize); + } + + // Iterate again, this time doing the real work. + unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset(); + ArrayRef bulkSymbols; + cantFail(forEachCodeViewRecord( + symsBuffer, [&](CVSymbol sym) -> llvm::Error { + // Align the record if required. + MutableArrayRef recordBytes; + if (needsRealignment) { + recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem); + sym = CVSymbol(recordBytes); + } else { + // Otherwise, we can actually mutate the symbol directly, since we + // copied it to apply relocations. + recordBytes = makeMutableArrayRef( + const_cast(sym.data().data()), sym.length()); + } + + // Discover type index references in the record. Skip it if we don't + // know where they are. + SmallVector typeRefs; + if (!discoverTypeIndicesInSymbol(sym, typeRefs)) { + log("ignoring unknown symbol record with kind 0x" + + utohexstr(sym.kind())); + return Error::success(); + } + + // Re-map all the type index references. + remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap, + typeRefs); + + // An object file may have S_xxx_ID symbols, but these get converted to + // "real" symbols in a PDB. + translateIdSymbols(recordBytes, tMerger.getIDTable()); + sym = CVSymbol(recordBytes); + + // If this record refers to an offset in the object file's string table, + // add that item to the global PDB string table and re-write the index. + recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs); + + // Fill in "Parent" and "End" fields by maintaining a stack of scopes. + if (symbolOpensScope(sym.kind())) + scopeStackOpen(scopes, curSymOffset, sym); + else if (symbolEndsScope(sym.kind())) + scopeStackClose(scopes, curSymOffset, file); + + // Add the symbol to the globals stream if necessary. Do this before + // adding the symbol to the module since we may need to get the next + // symbol offset, and writing to the module's symbol stream will update + // that offset. + if (symbolGoesInGlobalsStream(sym, scopes.empty())) { + addGlobalSymbol(builder.getGsiBuilder(), + file->moduleDBI->getModuleIndex(), curSymOffset, sym); + ++globalSymbols; + } + + if (symbolGoesInModuleStream(sym, scopes.empty())) { + // Add symbols to the module in bulk. If this symbol is contiguous + // with the previous run of symbols to add, combine the ranges. If + // not, close the previous range of symbols and start a new one. + if (sym.data().data() == bulkSymbols.end()) { + bulkSymbols = makeArrayRef(bulkSymbols.data(), + bulkSymbols.size() + sym.length()); + } else { + file->moduleDBI->addSymbolsInBulk(bulkSymbols); + bulkSymbols = recordBytes; + } + curSymOffset += sym.length(); + ++moduleSymbols; + } + return Error::success(); + })); + + // Add any remaining symbols we've accumulated. + file->moduleDBI->addSymbolsInBulk(bulkSymbols); +} + +// Allocate memory for a .debug$S / .debug$F section and relocate it. +static ArrayRef relocateDebugChunk(BumpPtrAllocator &alloc, + SectionChunk &debugChunk) { + uint8_t *buffer = alloc.Allocate(debugChunk.getSize()); + assert(debugChunk.getOutputSectionIdx() == 0 && + "debug sections should not be in output sections"); + debugChunk.writeTo(buffer); + return makeArrayRef(buffer, debugChunk.getSize()); +} + +static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { + OutputSection *os = c ? c->getOutputSection() : nullptr; + pdb::SectionContrib sc; + memset(&sc, 0, sizeof(sc)); + sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex; + sc.Off = c && os ? c->getRVA() - os->getRVA() : 0; + sc.Size = c ? c->getSize() : -1; + if (auto *secChunk = dyn_cast_or_null(c)) { + sc.Characteristics = secChunk->header->Characteristics; + sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); + ArrayRef contents = secChunk->getContents(); + JamCRC crc(0); + ArrayRef charContents = makeArrayRef( + reinterpret_cast(contents.data()), contents.size()); + crc.update(charContents); + sc.DataCrc = crc.getCRC(); + } else { + sc.Characteristics = os ? os->header.Characteristics : 0; + sc.Imod = modi; + } + sc.RelocCrc = 0; // FIXME + + return sc; +} + +static uint32_t +translateStringTableIndex(uint32_t objIndex, + const DebugStringTableSubsectionRef &objStrTable, + DebugStringTableSubsection &pdbStrTable) { + auto expectedString = objStrTable.getString(objIndex); + if (!expectedString) { + warn("Invalid string table reference"); + consumeError(expectedString.takeError()); + return 0; + } + + return pdbStrTable.insert(*expectedString); +} + +void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { + DebugSubsectionArray subsections; + + ArrayRef relocatedDebugContents = SectionChunk::consumeDebugMagic( + relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName()); + + BinaryStreamReader reader(relocatedDebugContents, support::little); + exitOnErr(reader.readArray(subsections, relocatedDebugContents.size())); + + for (const DebugSubsectionRecord &ss : subsections) { + // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++ + // runtime have subsections with this bit set. + if (uint32_t(ss.kind()) & codeview::SubsectionIgnoreFlag) + continue; + + switch (ss.kind()) { + case DebugSubsectionKind::StringTable: { + assert(!cVStrTab.valid() && + "Encountered multiple string table subsections!"); + exitOnErr(cVStrTab.initialize(ss.getRecordData())); + break; + } + case DebugSubsectionKind::FileChecksums: + assert(!checksums.valid() && + "Encountered multiple checksum subsections!"); + exitOnErr(checksums.initialize(ss.getRecordData())); + break; + case DebugSubsectionKind::Lines: + // We can add the relocated line table directly to the PDB without + // modification because the file checksum offsets will stay the same. + file.moduleDBI->addDebugSubsection(ss); + break; + case DebugSubsectionKind::InlineeLines: + assert(!inlineeLines.valid() && + "Encountered multiple inlinee lines subsections!"); + exitOnErr(inlineeLines.initialize(ss.getRecordData())); + break; + case DebugSubsectionKind::FrameData: { + // We need to re-write string table indices here, so save off all + // frame data subsections until we've processed the entire list of + // subsections so that we can be sure we have the string table. + DebugFrameDataSubsectionRef fds; + exitOnErr(fds.initialize(ss.getRecordData())); + newFpoFrames.push_back(std::move(fds)); + break; + } + case DebugSubsectionKind::Symbols: { + linker.mergeSymbolRecords(&file, indexMap, stringTableReferences, + ss.getRecordData()); + break; + } + + case DebugSubsectionKind::CrossScopeImports: + case DebugSubsectionKind::CrossScopeExports: + // These appear to relate to cross-module optimization, so we might use + // these for ThinLTO. + break; + + case DebugSubsectionKind::ILLines: + case DebugSubsectionKind::FuncMDTokenMap: + case DebugSubsectionKind::TypeMDTokenMap: + case DebugSubsectionKind::MergedAssemblyInput: + // These appear to relate to .Net assembly info. + break; + + case DebugSubsectionKind::CoffSymbolRVA: + // Unclear what this is for. + break; + + default: + warn("ignoring unknown debug$S subsection kind 0x" + + utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file)); + break; + } + } +} + +static Expected +getFileName(const DebugStringTableSubsectionRef &strings, + const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) { + auto iter = checksums.getArray().at(fileID); + if (iter == checksums.getArray().end()) + return make_error(cv_error_code::no_records); + uint32_t offset = iter->FileNameOffset; + return strings.getString(offset); +} + +std::shared_ptr +DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) { + auto newInlineeLines = std::make_shared( + *newChecksums, inlineeLines.hasExtraFiles()); + + for (const InlineeSourceLine &line : inlineeLines) { + TypeIndex inlinee = line.Header->Inlinee; + uint32_t fileID = line.Header->FileID; + uint32_t sourceLine = line.Header->SourceLineNum; + + ArrayRef typeOrItemMap = + indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap; + if (!remapTypeIndex(inlinee, typeOrItemMap)) { + log("ignoring inlinee line record in " + file.getName() + + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); + continue; + } + + SmallString<128> filename = + exitOnErr(getFileName(cVStrTab, checksums, fileID)); + pdbMakeAbsolute(filename); + newInlineeLines->addInlineSite(inlinee, filename, sourceLine); + + if (inlineeLines.hasExtraFiles()) { + for (uint32_t extraFileId : line.ExtraFiles) { + filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId)); + pdbMakeAbsolute(filename); + newInlineeLines->addExtraFile(filename); + } + } + } + + return newInlineeLines; +} + +void DebugSHandler::finish() { + pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder(); + + // We should have seen all debug subsections across the entire object file now + // which means that if a StringTable subsection and Checksums subsection were + // present, now is the time to handle them. + if (!cVStrTab.valid()) { + if (checksums.valid()) + fatal(".debug$S sections with a checksums subsection must also contain a " + "string table subsection"); + + if (!stringTableReferences.empty()) + warn("No StringTable subsection was encountered, but there are string " + "table references"); + return; + } + + // Rewrite string table indices in the Fpo Data and symbol records to refer to + // the global PDB string table instead of the object file string table. + for (DebugFrameDataSubsectionRef &fds : newFpoFrames) { + const ulittle32_t *reloc = fds.getRelocPtr(); + for (codeview::FrameData fd : fds) { + fd.RvaStart += *reloc; + fd.FrameFunc = + translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab); + dbiBuilder.addNewFpoData(fd); + } + } + + for (ulittle32_t *ref : stringTableReferences) + *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab); + + // Make a new file checksum table that refers to offsets in the PDB-wide + // string table. Generally the string table subsection appears after the + // checksum table, so we have to do this after looping over all the + // subsections. + auto newChecksums = make_unique(linker.pdbStrTab); + for (FileChecksumEntry &fc : checksums) { + SmallString<128> filename = + exitOnErr(cVStrTab.getString(fc.FileNameOffset)); + pdbMakeAbsolute(filename); + exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); + newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); + } + + // Rewrite inlinee item indices if present. + if (inlineeLines.valid()) + file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get())); + + file.moduleDBI->addDebugSubsection(std::move(newChecksums)); +} + +void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { + if (file->mergedIntoPDB) + return; + file->mergedIntoPDB = true; + + // Before we can process symbol substreams from .debug$S, we need to process + // type information, file checksums, and the string table. Add type info to + // the PDB first, so that we can get the map from object file type and item + // indices to PDB type and item indices. + CVIndexMap objectIndexMap; + auto indexMapResult = + mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap); + + // If the .debug$T sections fail to merge, assume there is no debug info. + if (!indexMapResult) { + if (!config->warnDebugInfoUnusable) { + consumeError(indexMapResult.takeError()); + return; + } + warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" + + ">>> failed to load reference " + + StringRef(toString(indexMapResult.takeError()))); + return; + } + + ScopedTimer t(symbolMergingTimer); + + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + DebugSHandler dsh(*this, *file, *indexMapResult); + // Now do all live .debug$S and .debug$F sections. + for (SectionChunk *debugChunk : file->getDebugChunks()) { + if (!debugChunk->live || debugChunk->getSize() == 0) + continue; + + if (debugChunk->getSectionName() == ".debug$S") { + dsh.handleDebugS(*debugChunk); + continue; + } + + if (debugChunk->getSectionName() == ".debug$F") { + ArrayRef relocatedDebugContents = + relocateDebugChunk(alloc, *debugChunk); + + FixedStreamArray fpoRecords; + BinaryStreamReader reader(relocatedDebugContents, support::little); + uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData); + exitOnErr(reader.readArray(fpoRecords, count)); + + // These are already relocated and don't refer to the string table, so we + // can just copy it. + for (const object::FpoData &fd : fpoRecords) + dbiBuilder.addOldFpoData(fd); + continue; + } + } + + // Do any post-processing now that all .debug$S sections have been processed. + dsh.finish(); +} + +// Add a module descriptor for every object file. We need to put an absolute +// path to the object into the PDB. If this is a plain object, we make its +// path absolute. If it's an object in an archive, we make the archive path +// absolute. +static void createModuleDBI(pdb::PDBFileBuilder &builder) { + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + SmallString<128> objName; + + for (ObjFile *file : ObjFile::instances) { + + bool inArchive = !file->parentName.empty(); + objName = inArchive ? file->parentName : file->getName(); + pdbMakeAbsolute(objName); + StringRef modName = inArchive ? file->getName() : StringRef(objName); + + file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); + file->moduleDBI->setObjFileName(objName); + + ArrayRef chunks = file->getChunks(); + uint32_t modi = file->moduleDBI->getModuleIndex(); + + for (Chunk *c : chunks) { + auto *secChunk = dyn_cast(c); + if (!secChunk || !secChunk->live) + continue; + pdb::SectionContrib sc = createSectionContrib(secChunk, modi); + file->moduleDBI->setFirstSectionContrib(sc); + break; + } + } +} + +static PublicSym32 createPublic(Defined *def) { + PublicSym32 pub(SymbolKind::S_PUB32); + pub.Name = def->getName(); + if (auto *d = dyn_cast(def)) { + if (d->getCOFFSymbol().isFunctionDefinition()) + pub.Flags = PublicSymFlags::Function; + } else if (isa(def)) { + pub.Flags = PublicSymFlags::Function; + } + + OutputSection *os = def->getChunk()->getOutputSection(); + assert(os && "all publics should be in final image"); + pub.Offset = def->getRVA() - os->getRVA(); + pub.Segment = os->sectionIndex; + return pub; +} + +// Add all object files to the PDB. Merge .debug$T sections into IpiData and +// TpiData. +void PDBLinker::addObjectsToPDB() { + ScopedTimer t1(addObjectsTimer); + + createModuleDBI(builder); + + for (ObjFile *file : ObjFile::instances) + addObjFile(file); + + builder.getStringTableBuilder().setStrings(pdbStrTab); + t1.stop(); + + // Construct TPI and IPI stream contents. + ScopedTimer t2(tpiStreamLayoutTimer); + addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); + addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); + t2.stop(); + + ScopedTimer t3(globalsLayoutTimer); + // Compute the public and global symbols. + auto &gsiBuilder = builder.getGsiBuilder(); + std::vector publics; + symtab->forEachSymbol([&publics](Symbol *s) { + // Only emit defined, live symbols that have a chunk. + auto *def = dyn_cast(s); + if (def && def->isLive() && def->getChunk()) + publics.push_back(createPublic(def)); + }); + + if (!publics.empty()) { + publicSymbols = publics.size(); + // Sort the public symbols and add them to the stream. + parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) { + return l.Name < r.Name; + }); + for (const PublicSym32 &pub : publics) + gsiBuilder.addPublicSymbol(pub); + } +} + +void PDBLinker::printStats() { + if (!config->showSummary) + return; + + SmallString<256> buffer; + raw_svector_ostream stream(buffer); + + stream << center_justify("Summary", 80) << '\n' + << std::string(80, '-') << '\n'; + + auto print = [&](uint64_t v, StringRef s) { + stream << format_decimal(v, 15) << " " << s << '\n'; + }; + + print(ObjFile::instances.size(), + "Input OBJ files (expanded from all cmd-line inputs)"); + print(typeServerIndexMappings.size(), "PDB type server dependencies"); + print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies"); + print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(), + "Merged TPI records"); + print(pdbStrTab.size(), "Output PDB strings"); + print(globalSymbols, "Global symbol records"); + print(moduleSymbols, "Module symbol records"); + print(publicSymbols, "Public symbol records"); + + message(buffer); +} + +void PDBLinker::addNatvisFiles() { + for (StringRef file : config->natvisFiles) { + ErrorOr> dataOrErr = + MemoryBuffer::getFile(file); + if (!dataOrErr) { + warn("Cannot open input file: " + file); + continue; + } + builder.addInjectedSource(file, std::move(*dataOrErr)); + } +} + +static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) { + switch (machine) { + case COFF::IMAGE_FILE_MACHINE_AMD64: + return codeview::CPUType::X64; + case COFF::IMAGE_FILE_MACHINE_ARM: + return codeview::CPUType::ARM7; + case COFF::IMAGE_FILE_MACHINE_ARM64: + return codeview::CPUType::ARM64; + case COFF::IMAGE_FILE_MACHINE_ARMNT: + return codeview::CPUType::ARMNT; + case COFF::IMAGE_FILE_MACHINE_I386: + return codeview::CPUType::Intel80386; + default: + llvm_unreachable("Unsupported CPU Type"); + } +} + +// Mimic MSVC which surrounds arguments containing whitespace with quotes. +// Double double-quotes are handled, so that the resulting string can be +// executed again on the cmd-line. +static std::string quote(ArrayRef args) { + std::string r; + r.reserve(256); + for (StringRef a : args) { + if (!r.empty()) + r.push_back(' '); + bool hasWS = a.find(' ') != StringRef::npos; + bool hasQ = a.find('"') != StringRef::npos; + if (hasWS || hasQ) + r.push_back('"'); + if (hasQ) { + SmallVector s; + a.split(s, '"'); + r.append(join(s, "\"\"")); + } else { + r.append(a); + } + if (hasWS || hasQ) + r.push_back('"'); + } + return r; +} + +static void fillLinkerVerRecord(Compile3Sym &cs) { + cs.Machine = toCodeViewMachine(config->machine); + // Interestingly, if we set the string to 0.0.0.0, then when trying to view + // local variables WinDbg emits an error that private symbols are not present. + // By setting this to a valid MSVC linker version string, local variables are + // displayed properly. As such, even though it is not representative of + // LLVM's version information, we need this for compatibility. + cs.Flags = CompileSym3Flags::None; + cs.VersionBackendBuild = 25019; + cs.VersionBackendMajor = 14; + cs.VersionBackendMinor = 10; + cs.VersionBackendQFE = 0; + + // MSVC also sets the frontend to 0.0.0.0 since this is specifically for the + // linker module (which is by definition a backend), so we don't need to do + // anything here. Also, it seems we can use "LLVM Linker" for the linker name + // without any problems. Only the backend version has to be hardcoded to a + // magic number. + cs.VersionFrontendBuild = 0; + cs.VersionFrontendMajor = 0; + cs.VersionFrontendMinor = 0; + cs.VersionFrontendQFE = 0; + cs.Version = "LLVM Linker"; + cs.setLanguage(SourceLanguage::Link); +} + +static void addCommonLinkerModuleSymbols(StringRef path, + pdb::DbiModuleDescriptorBuilder &mod, + BumpPtrAllocator &allocator) { + ObjNameSym ons(SymbolRecordKind::ObjNameSym); + EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym); + Compile3Sym cs(SymbolRecordKind::Compile3Sym); + fillLinkerVerRecord(cs); + + ons.Name = "* Linker *"; + ons.Signature = 0; + + ArrayRef args = makeArrayRef(config->argv).drop_front(); + std::string argStr = quote(args); + ebs.Fields.push_back("cwd"); + SmallString<64> cwd; + if (config->pdbSourcePath.empty()) + sys::fs::current_path(cwd); + else + cwd = config->pdbSourcePath; + ebs.Fields.push_back(cwd); + ebs.Fields.push_back("exe"); + SmallString<64> exe = config->argv[0]; + pdbMakeAbsolute(exe); + ebs.Fields.push_back(exe); + ebs.Fields.push_back("pdb"); + ebs.Fields.push_back(path); + ebs.Fields.push_back("cmd"); + ebs.Fields.push_back(argStr); + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + ons, allocator, CodeViewContainer::Pdb)); + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cs, allocator, CodeViewContainer::Pdb)); + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + ebs, allocator, CodeViewContainer::Pdb)); +} + +static void addLinkerModuleCoffGroup(PartialSection *sec, + pdb::DbiModuleDescriptorBuilder &mod, + OutputSection &os, + BumpPtrAllocator &allocator) { + // If there's a section, there's at least one chunk + assert(!sec->chunks.empty()); + const Chunk *firstChunk = *sec->chunks.begin(); + const Chunk *lastChunk = *sec->chunks.rbegin(); + + // Emit COFF group + CoffGroupSym cgs(SymbolRecordKind::CoffGroupSym); + cgs.Name = sec->name; + cgs.Segment = os.sectionIndex; + cgs.Offset = firstChunk->getRVA() - os.getRVA(); + cgs.Size = lastChunk->getRVA() + lastChunk->getSize() - firstChunk->getRVA(); + cgs.Characteristics = sec->characteristics; + + // Somehow .idata sections & sections groups in the debug symbol stream have + // the "write" flag set. However the section header for the corresponding + // .idata section doesn't have it. + if (cgs.Name.startswith(".idata")) + cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; + + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cgs, allocator, CodeViewContainer::Pdb)); +} + +static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, + OutputSection &os, + BumpPtrAllocator &allocator) { + SectionSym sym(SymbolRecordKind::SectionSym); + sym.Alignment = 12; // 2^12 = 4KB + sym.Characteristics = os.header.Characteristics; + sym.Length = os.getVirtualSize(); + sym.Name = os.name; + sym.Rva = os.getRVA(); + sym.SectionNumber = os.sectionIndex; + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + sym, allocator, CodeViewContainer::Pdb)); + + // Skip COFF groups in MinGW because it adds a significant footprint to the + // PDB, due to each function being in its own section + if (config->mingw) + return; + + // Output COFF groups for individual chunks of this section. + for (PartialSection *sec : os.contribSections) { + addLinkerModuleCoffGroup(sec, mod, os, allocator); + } +} + +// Add all import files as modules to the PDB. +void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { + if (ImportFile::instances.empty()) + return; + + std::map dllToModuleDbi; + + for (ImportFile *file : ImportFile::instances) { + if (!file->live) + continue; + + if (!file->thunkSym) + continue; + + if (!file->thunkLive) + continue; + + std::string dll = StringRef(file->dllName).lower(); + llvm::pdb::DbiModuleDescriptorBuilder *&mod = dllToModuleDbi[dll]; + if (!mod) { + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + SmallString<128> libPath = file->parentName; + pdbMakeAbsolute(libPath); + sys::path::native(libPath); + + // Name modules similar to MSVC's link.exe. + // The first module is the simple dll filename + llvm::pdb::DbiModuleDescriptorBuilder &firstMod = + exitOnErr(dbiBuilder.addModuleInfo(file->dllName)); + firstMod.setObjFileName(libPath); + pdb::SectionContrib sc = + createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + firstMod.setFirstSectionContrib(sc); + + // The second module is where the import stream goes. + mod = &exitOnErr(dbiBuilder.addModuleInfo("Import:" + file->dllName)); + mod->setObjFileName(libPath); + } + + DefinedImportThunk *thunk = cast(file->thunkSym); + Chunk *thunkChunk = thunk->getChunk(); + OutputSection *thunkOS = thunkChunk->getOutputSection(); + + ObjNameSym ons(SymbolRecordKind::ObjNameSym); + Compile3Sym cs(SymbolRecordKind::Compile3Sym); + Thunk32Sym ts(SymbolRecordKind::Thunk32Sym); + ScopeEndSym es(SymbolRecordKind::ScopeEndSym); + + ons.Name = file->dllName; + ons.Signature = 0; + + fillLinkerVerRecord(cs); + + ts.Name = thunk->getName(); + ts.Parent = 0; + ts.End = 0; + ts.Next = 0; + ts.Thunk = ThunkOrdinal::Standard; + ts.Length = thunkChunk->getSize(); + ts.Segment = thunkOS->sectionIndex; + ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); + + mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( + ons, alloc, CodeViewContainer::Pdb)); + mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cs, alloc, CodeViewContainer::Pdb)); + + SmallVector scopes; + CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol( + ts, alloc, CodeViewContainer::Pdb); + scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym); + + mod->addSymbol(newSym); + + newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc, + CodeViewContainer::Pdb); + scopeStackClose(scopes, mod->getNextSymbolOffset(), file); + + mod->addSymbol(newSym); + + pdb::SectionContrib sc = + createSectionContrib(thunk->getChunk(), mod->getModuleIndex()); + mod->setFirstSectionContrib(sc); + } +} + +// Creates a PDB file. +void coff::createPDB(SymbolTable *symtab, + ArrayRef outputSections, + ArrayRef sectionTable, + llvm::codeview::DebugInfo *buildId) { + ScopedTimer t1(totalPdbLinkTimer); + PDBLinker pdb(symtab); + + pdb.initialize(buildId); + pdb.addObjectsToPDB(); + pdb.addImportFilesToPDB(outputSections); + pdb.addSections(outputSections, sectionTable); + pdb.addNatvisFiles(); + + ScopedTimer t2(diskCommitTimer); + codeview::GUID guid; + pdb.commit(&guid); + memcpy(&buildId->PDB70.Signature, &guid, 16); + + t2.stop(); + t1.stop(); + pdb.printStats(); +} + +void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { + exitOnErr(builder.initialize(4096)); // 4096 is blocksize + + buildId->Signature.CVSignature = OMF::Signature::PDB70; + // Signature is set to a hash of the PDB contents when the PDB is done. + memset(buildId->PDB70.Signature, 0, 16); + buildId->PDB70.Age = 1; + + // Create streams in MSF for predefined streams, namely + // PDB, TPI, DBI and IPI. + for (int i = 0; i < (int)pdb::kSpecialStreamCount; ++i) + exitOnErr(builder.getMsfBuilder().addStream(0)); + + // Add an Info stream. + auto &infoBuilder = builder.getInfoBuilder(); + infoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); + infoBuilder.setHashPDBContentsToGUID(true); + + // Add an empty DBI stream. + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + dbiBuilder.setAge(buildId->PDB70.Age); + dbiBuilder.setVersionHeader(pdb::PdbDbiV70); + dbiBuilder.setMachineType(config->machine); + // Technically we are not link.exe 14.11, but there are known cases where + // debugging tools on Windows expect Microsoft-specific version numbers or + // they fail to work at all. Since we know we produce PDBs that are + // compatible with LINK 14.11, we set that version number here. + dbiBuilder.setBuildNumber(14, 11); +} + +void PDBLinker::addSections(ArrayRef outputSections, + ArrayRef sectionTable) { + // It's not entirely clear what this is, but the * Linker * module uses it. + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + nativePath = config->pdbPath; + pdbMakeAbsolute(nativePath); + uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); + auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); + linkerModule.setPdbFilePathNI(pdbFilePathNI); + addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc); + + // Add section contributions. They must be ordered by ascending RVA. + for (OutputSection *os : outputSections) { + addLinkerModuleSectionSymbol(linkerModule, *os, alloc); + for (Chunk *c : os->chunks) { + pdb::SectionContrib sc = + createSectionContrib(c, linkerModule.getModuleIndex()); + builder.getDbiBuilder().addSectionContrib(sc); + } + } + + // The * Linker * first section contrib is only used along with /INCREMENTAL, + // to provide trampolines thunks for incremental function patching. Set this + // as "unused" because LLD doesn't support /INCREMENTAL link. + pdb::SectionContrib sc = + createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + linkerModule.setFirstSectionContrib(sc); + + // Add Section Map stream. + ArrayRef sections = { + (const object::coff_section *)sectionTable.data(), + sectionTable.size() / sizeof(object::coff_section)}; + sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections); + dbiBuilder.setSectionMap(sectionMap); + + // Add COFF section header stream. + exitOnErr( + dbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, sectionTable)); +} + +void PDBLinker::commit(codeview::GUID *guid) { + // Write to a file. + exitOnErr(builder.commit(config->pdbPath, guid)); +} + +static uint32_t getSecrelReloc() { + switch (config->machine) { + case AMD64: + return COFF::IMAGE_REL_AMD64_SECREL; + case I386: + return COFF::IMAGE_REL_I386_SECREL; + case ARMNT: + return COFF::IMAGE_REL_ARM_SECREL; + case ARM64: + return COFF::IMAGE_REL_ARM64_SECREL; + default: + llvm_unreachable("unknown machine type"); + } +} + +// Try to find a line table for the given offset Addr into the given chunk C. +// If a line table was found, the line table, the string and checksum tables +// that are used to interpret the line table, and the offset of Addr in the line +// table are stored in the output arguments. Returns whether a line table was +// found. +static bool findLineTable(const SectionChunk *c, uint32_t addr, + DebugStringTableSubsectionRef &cVStrTab, + DebugChecksumsSubsectionRef &checksums, + DebugLinesSubsectionRef &lines, + uint32_t &offsetInLinetable) { + ExitOnError exitOnErr; + uint32_t secrelReloc = getSecrelReloc(); + + for (SectionChunk *dbgC : c->file->getDebugChunks()) { + if (dbgC->getSectionName() != ".debug$S") + continue; + + // Build a mapping of SECREL relocations in dbgC that refer to `c`. + DenseMap secrels; + for (const coff_relocation &r : dbgC->getRelocs()) { + if (r.Type != secrelReloc) + continue; + + if (auto *s = dyn_cast_or_null( + c->file->getSymbols()[r.SymbolTableIndex])) + if (s->getChunk() == c) + secrels[r.VirtualAddress] = s->getValue(); + } + + ArrayRef contents = + SectionChunk::consumeDebugMagic(dbgC->getContents(), ".debug$S"); + DebugSubsectionArray subsections; + BinaryStreamReader reader(contents, support::little); + exitOnErr(reader.readArray(subsections, contents.size())); + + for (const DebugSubsectionRecord &ss : subsections) { + switch (ss.kind()) { + case DebugSubsectionKind::StringTable: { + assert(!cVStrTab.valid() && + "Encountered multiple string table subsections!"); + exitOnErr(cVStrTab.initialize(ss.getRecordData())); + break; + } + case DebugSubsectionKind::FileChecksums: + assert(!checksums.valid() && + "Encountered multiple checksum subsections!"); + exitOnErr(checksums.initialize(ss.getRecordData())); + break; + case DebugSubsectionKind::Lines: { + ArrayRef bytes; + auto ref = ss.getRecordData(); + exitOnErr(ref.readLongestContiguousChunk(0, bytes)); + size_t offsetInDbgC = bytes.data() - dbgC->getContents().data(); + + // Check whether this line table refers to C. + auto i = secrels.find(offsetInDbgC); + if (i == secrels.end()) + break; + + // Check whether this line table covers Addr in C. + DebugLinesSubsectionRef linesTmp; + exitOnErr(linesTmp.initialize(BinaryStreamReader(ref))); + uint32_t offsetInC = i->second + linesTmp.header()->RelocOffset; + if (addr < offsetInC || addr >= offsetInC + linesTmp.header()->CodeSize) + break; + + assert(!lines.header() && + "Encountered multiple line tables for function!"); + exitOnErr(lines.initialize(BinaryStreamReader(ref))); + offsetInLinetable = addr - offsetInC; + break; + } + default: + break; + } + + if (cVStrTab.valid() && checksums.valid() && lines.header()) + return true; + } + } + + return false; +} + +// Use CodeView line tables to resolve a file and line number for the given +// offset into the given chunk and return them, or {"", 0} if a line table was +// not found. +std::pair coff::getFileLine(const SectionChunk *c, + uint32_t addr) { + ExitOnError exitOnErr; + + DebugStringTableSubsectionRef cVStrTab; + DebugChecksumsSubsectionRef checksums; + DebugLinesSubsectionRef lines; + uint32_t offsetInLinetable; + + if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) + return {"", 0}; + + Optional nameIndex; + Optional lineNumber; + for (LineColumnEntry &entry : lines) { + for (const LineNumberEntry &ln : entry.LineNumbers) { + LineInfo li(ln.Flags); + if (ln.Offset > offsetInLinetable) { + if (!nameIndex) { + nameIndex = entry.NameIndex; + lineNumber = li.getStartLine(); + } + StringRef filename = + exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + return {filename, *lineNumber}; + } + nameIndex = entry.NameIndex; + lineNumber = li.getStartLine(); + } + } + if (!nameIndex) + return {"", 0}; + StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + return {filename, *lineNumber}; +} Property changes on: vendor/lld/lld-release_900-r372316/COFF/PDB.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/PDB.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/PDB.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/PDB.h (revision 352529) @@ -0,0 +1,37 @@ +//===- PDB.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_COFF_PDB_H +#define LLD_COFF_PDB_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { +namespace codeview { +union DebugInfo; +} +} + +namespace lld { +namespace coff { +class OutputSection; +class SectionChunk; +class SymbolTable; + +void createPDB(SymbolTable *symtab, + llvm::ArrayRef outputSections, + llvm::ArrayRef sectionTable, + llvm::codeview::DebugInfo *buildId); + +std::pair getFileLine(const SectionChunk *c, + uint32_t addr); +} +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/PDB.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/TypeMerger.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/TypeMerger.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/TypeMerger.h (revision 352529) @@ -0,0 +1,65 @@ +//===- TypeMerger.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_COFF_TYPEMERGER_H +#define LLD_COFF_TYPEMERGER_H + +#include "Config.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/Support/Allocator.h" + +namespace lld { +namespace coff { + +class TypeMerger { +public: + TypeMerger(llvm::BumpPtrAllocator &alloc) + : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc), + globalIDTable(alloc) {} + + /// Get the type table or the global type table if /DEBUG:GHASH is enabled. + inline llvm::codeview::TypeCollection &getTypeTable() { + if (config->debugGHashes) + return globalTypeTable; + return typeTable; + } + + /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. + inline llvm::codeview::TypeCollection &getIDTable() { + if (config->debugGHashes) + return globalIDTable; + return iDTable; + } + + /// Type records that will go into the PDB TPI stream. + llvm::codeview::MergingTypeTableBuilder typeTable; + + /// Item records that will go into the PDB IPI stream. + llvm::codeview::MergingTypeTableBuilder iDTable; + + /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH) + llvm::codeview::GlobalTypeTableBuilder globalTypeTable; + + /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH) + llvm::codeview::GlobalTypeTableBuilder globalIDTable; +}; + +/// Map from type index and item index in a type server PDB to the +/// corresponding index in the destination PDB. +struct CVIndexMap { + llvm::SmallVector tpiMap; + llvm::SmallVector ipiMap; + bool isTypeServerMap = false; + bool isPrecompiledTypeMap = false; +}; + +} // namespace coff +} // namespace lld + +#endif \ No newline at end of file Index: vendor/lld/lld-release_900-r372316/COFF/Writer.h =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/Writer.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/Writer.h (revision 352529) @@ -0,0 +1,85 @@ +//===- Writer.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_COFF_WRITER_H +#define LLD_COFF_WRITER_H + +#include "Chunks.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include +#include +#include + +namespace lld { +namespace coff { +static const int pageSize = 4096; + +void writeResult(); + +class PartialSection { +public: + PartialSection(StringRef n, uint32_t chars) + : name(n), characteristics(chars) {} + StringRef name; + unsigned characteristics; + std::vector chunks; +}; + +// OutputSection represents a section in an output file. It's a +// container of chunks. OutputSection and Chunk are 1:N relationship. +// Chunks cannot belong to more than one OutputSections. The writer +// creates multiple OutputSections and assign them unique, +// non-overlapping file offsets and RVAs. +class OutputSection { +public: + OutputSection(llvm::StringRef n, uint32_t chars) : name(n) { + header.Characteristics = chars; + } + void addChunk(Chunk *c); + void insertChunkAtStart(Chunk *c); + void merge(OutputSection *other); + void setPermissions(uint32_t c); + uint64_t getRVA() { return header.VirtualAddress; } + uint64_t getFileOff() { return header.PointerToRawData; } + void writeHeaderTo(uint8_t *buf); + void addContributingPartialSection(PartialSection *sec); + + // Returns the size of this section in an executable memory image. + // This may be smaller than the raw size (the raw size is multiple + // of disk sector size, so there may be padding at end), or may be + // larger (if that's the case, the loader reserves spaces after end + // of raw data). + uint64_t getVirtualSize() { return header.VirtualSize; } + + // Returns the size of the section in the output file. + uint64_t getRawSize() { return header.SizeOfRawData; } + + // Set offset into the string table storing this section name. + // Used only when the name is longer than 8 bytes. + void setStringTableOff(uint32_t v) { stringTableOff = v; } + + // N.B. The section index is one based. + uint32_t sectionIndex = 0; + + llvm::StringRef name; + llvm::object::coff_section header = {}; + + std::vector chunks; + std::vector origChunks; + + std::vector contribSections; + +private: + uint32_t stringTableOff = 0; +}; + +} // namespace coff +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/COFF/Writer.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/COFF/README.md =================================================================== --- vendor/lld/lld-release_900-r372316/COFF/README.md (nonexistent) +++ vendor/lld/lld-release_900-r372316/COFF/README.md (revision 352529) @@ -0,0 +1 @@ +See docs/NewLLD.rst Index: vendor/lld/lld-release_900-r372316/Common/Args.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Args.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Args.cpp (revision 352529) @@ -0,0 +1,82 @@ +//===- Args.cpp -----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Args.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace lld; + +// TODO(sbc): Remove this once CGOptLevel can be set completely based on bitcode +// function metadata. +CodeGenOpt::Level lld::args::getCGOptLevel(int optLevelLTO) { + if (optLevelLTO == 3) + return CodeGenOpt::Aggressive; + assert(optLevelLTO < 3); + return CodeGenOpt::Default; +} + +int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key, + int64_t Default) { + auto *a = args.getLastArg(key); + if (!a) + return Default; + + int64_t v; + if (to_integer(a->getValue(), v, 10)) + return v; + + StringRef spelling = args.getArgString(a->getIndex()); + error(spelling + ": number expected, but got '" + a->getValue() + "'"); + return 0; +} + +std::vector lld::args::getStrings(opt::InputArgList &args, int id) { + std::vector v; + for (auto *arg : args.filtered(id)) + v.push_back(arg->getValue()); + return v; +} + +uint64_t lld::args::getZOptionValue(opt::InputArgList &args, int id, + StringRef key, uint64_t Default) { + for (auto *arg : args.filtered_reverse(id)) { + std::pair kv = StringRef(arg->getValue()).split('='); + if (kv.first == key) { + uint64_t result = Default; + if (!to_integer(kv.second, result)) + error("invalid " + key + ": " + kv.second); + return result; + } + } + return Default; +} + +std::vector lld::args::getLines(MemoryBufferRef mb) { + SmallVector arr; + mb.getBuffer().split(arr, '\n'); + + std::vector ret; + for (StringRef s : arr) { + s = s.trim(); + if (!s.empty() && s[0] != '#') + ret.push_back(s); + } + return ret; +} + +StringRef lld::args::getFilenameWithoutExe(StringRef path) { + if (path.endswith_lower(".exe")) + return sys::path::stem(path); + return sys::path::filename(path); +} Property changes on: vendor/lld/lld-release_900-r372316/Common/Args.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/Common/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/CMakeLists.txt (revision 352529) @@ -0,0 +1,60 @@ +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc) +find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc) + +set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc") +set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake") + +if(lld_vc) + set(lld_source_dir ${LLD_SOURCE_DIR}) +endif() + +add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${lld_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=LLD" + "-DLLD_SOURCE_DIR=${LLD_SOURCE_DIR}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") + +# Mark the generated header as being generated. +set_source_files_properties("${version_inc}" + PROPERTIES GENERATED TRUE + HEADER_FILE_ONLY TRUE) + +set_property(SOURCE Version.cpp APPEND PROPERTY + COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC") + +add_lld_library(lldCommon + Args.cpp + ErrorHandler.cpp + Filesystem.cpp + Memory.cpp + Reproduce.cpp + Strings.cpp + TargetOptionsCommandFlags.cpp + Threads.cpp + Timer.cpp + VCSVersion.inc + Version.cpp + + ADDITIONAL_HEADER_DIRS + ${LLD_INCLUDE_DIR}/lld/Common + + LINK_COMPONENTS + Codegen + Core + Demangle + MC + Option + Support + Target + + LINK_LIBS + ${LLVM_PTHREAD_LIB} + + DEPENDS + ${tablegen_deps} + ) Property changes on: vendor/lld/lld-release_900-r372316/Common/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/ErrorHandler.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/ErrorHandler.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/ErrorHandler.cpp (revision 352529) @@ -0,0 +1,178 @@ +//===- ErrorHandler.cpp ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/ErrorHandler.h" + +#include "lld/Common/Threads.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#include +#endif + +using namespace llvm; +using namespace lld; + +// The functions defined in this file can be called from multiple threads, +// but outs() or errs() are not thread-safe. We protect them using a mutex. +static std::mutex mu; + +// Prints "\n" or does nothing, depending on Msg contents of +// the previous call of this function. +static void newline(raw_ostream *errorOS, const Twine &msg) { + // True if the previous error message contained "\n". + // We want to separate multi-line error messages with a newline. + static bool flag; + + if (flag) + *errorOS << "\n"; + flag = StringRef(msg.str()).contains('\n'); +} + +ErrorHandler &lld::errorHandler() { + static ErrorHandler handler; + return handler; +} + +void lld::exitLld(int val) { + // Delete any temporary file, while keeping the memory mapping open. + if (errorHandler().outputBuffer) + errorHandler().outputBuffer->discard(); + + // Dealloc/destroy ManagedStatic variables before calling + // _exit(). In a non-LTO build, this is a nop. In an LTO + // build allows us to get the output of -time-passes. + llvm_shutdown(); + + outs().flush(); + errs().flush(); + _exit(val); +} + +void lld::diagnosticHandler(const DiagnosticInfo &di) { + SmallString<128> s; + raw_svector_ostream os(s); + DiagnosticPrinterRawOStream dp(os); + di.print(dp); + switch (di.getSeverity()) { + case DS_Error: + error(s); + break; + case DS_Warning: + warn(s); + break; + case DS_Remark: + case DS_Note: + message(s); + break; + } +} + +void lld::checkError(Error e) { + handleAllErrors(std::move(e), + [&](ErrorInfoBase &eib) { error(eib.message()); }); +} + +static std::string getLocation(std::string msg, std::string defaultMsg) { + static std::vector Regexes{ + std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"), + std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), + std::regex( + R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), + std::regex( + R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"), + std::regex( + R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), + std::regex( + R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"), + std::regex(R"((\S+):(\d+): unclosed quote)"), + }; + + std::smatch Match; + for (std::regex &Re : Regexes) { + if (std::regex_search(msg, Match, Re)) { + return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")" + : Match.str(1); + } + } + return defaultMsg; +} + +void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c, + const Twine &msg) { + + if (vsDiagnostics) { + // A Visual Studio-style error message starts with an error location. + // If a location cannot be extracted then we default to LogName. + *errorOS << getLocation(msg.str(), logName) << ": "; + } else { + *errorOS << logName << ": "; + } + + if (colorDiagnostics) { + errorOS->changeColor(c, true); + *errorOS << s; + errorOS->resetColor(); + } else { + *errorOS << s; + } +} + +void ErrorHandler::log(const Twine &msg) { + if (verbose) { + std::lock_guard lock(mu); + *errorOS << logName << ": " << msg << "\n"; + } +} + +void ErrorHandler::message(const Twine &msg) { + std::lock_guard lock(mu); + outs() << msg << "\n"; + outs().flush(); +} + +void ErrorHandler::warn(const Twine &msg) { + if (fatalWarnings) { + error(msg); + return; + } + + std::lock_guard lock(mu); + newline(errorOS, msg); + printHeader("warning: ", raw_ostream::MAGENTA, msg); + *errorOS << msg << "\n"; +} + +void ErrorHandler::error(const Twine &msg) { + std::lock_guard lock(mu); + newline(errorOS, msg); + + if (errorLimit == 0 || errorCount < errorLimit) { + printHeader("error: ", raw_ostream::RED, msg); + *errorOS << msg << "\n"; + } else if (errorCount == errorLimit) { + printHeader("error: ", raw_ostream::RED, msg); + *errorOS << errorLimitExceededMsg << "\n"; + if (exitEarly) + exitLld(1); + } + + ++errorCount; +} + +void ErrorHandler::fatal(const Twine &msg) { + error(msg); + exitLld(1); +} Property changes on: vendor/lld/lld-release_900-r372316/Common/ErrorHandler.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/Filesystem.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Filesystem.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Filesystem.cpp (revision 352529) @@ -0,0 +1,99 @@ +//===- Filesystem.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains a few utility functions to handle files. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Filesystem.h" +#include "lld/Common/Threads.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#if LLVM_ON_UNIX +#include +#endif +#include + +using namespace llvm; +using namespace lld; + +// Removes a given file asynchronously. This is a performance hack, +// so remove this when operating systems are improved. +// +// On Linux (and probably on other Unix-like systems), unlink(2) is a +// noticeably slow system call. As of 2016, unlink takes 250 +// milliseconds to remove a 1 GB file on ext4 filesystem on my machine. +// +// To create a new result file, we first remove existing file. So, if +// you repeatedly link a 1 GB program in a regular compile-link-debug +// cycle, every cycle wastes 250 milliseconds only to remove a file. +// Since LLD can link a 1 GB binary in about 5 seconds, that waste +// actually counts. +// +// This function spawns a background thread to remove the file. +// The calling thread returns almost immediately. +void lld::unlinkAsync(StringRef path) { +// Removing a file is async on windows. +#if defined(_WIN32) + sys::fs::remove(path); +#else + if (!threadsEnabled || !sys::fs::exists(path) || + !sys::fs::is_regular_file(path)) + return; + + // We cannot just remove path from a different thread because we are now going + // to create path as a new file. + // Instead we open the file and unlink it on this thread. The unlink is fast + // since the open fd guarantees that it is not removing the last reference. + int fd; + std::error_code ec = sys::fs::openFileForRead(path, fd); + sys::fs::remove(path); + + if (ec) + return; + + // close and therefore remove TempPath in background. + std::mutex m; + std::condition_variable cv; + bool started = false; + std::thread([&, fd] { + { + std::lock_guard l(m); + started = true; + cv.notify_all(); + } + ::close(fd); + }).detach(); + + // GLIBC 2.26 and earlier have race condition that crashes an entire process + // if the main thread calls exit(2) while other thread is starting up. + std::unique_lock l(m); + cv.wait(l, [&] { return started; }); +#endif +} + +// Simulate file creation to see if Path is writable. +// +// Determining whether a file is writable or not is amazingly hard, +// and after all the only reliable way of doing that is to actually +// create a file. But we don't want to do that in this function +// because LLD shouldn't update any file if it will end in a failure. +// We also don't want to reimplement heuristics to determine if a +// file is writable. So we'll let FileOutputBuffer do the work. +// +// FileOutputBuffer doesn't touch a desitnation file until commit() +// is called. We use that class without calling commit() to predict +// if the given file is writable. +std::error_code lld::tryCreateFile(StringRef path) { + if (path.empty()) + return std::error_code(); + if (path == "-") + return std::error_code(); + return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError()); +} Index: vendor/lld/lld-release_900-r372316/Common/Memory.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Memory.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Memory.cpp (revision 352529) @@ -0,0 +1,22 @@ +//===- Memory.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Memory.h" + +using namespace llvm; +using namespace lld; + +BumpPtrAllocator lld::bAlloc; +StringSaver lld::saver{bAlloc}; +std::vector lld::SpecificAllocBase::instances; + +void lld::freeArena() { + for (SpecificAllocBase *alloc : SpecificAllocBase::instances) + alloc->reset(); + bAlloc.Reset(); +} Property changes on: vendor/lld/lld-release_900-r372316/Common/Memory.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/Reproduce.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Reproduce.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Reproduce.cpp (revision 352529) @@ -0,0 +1,61 @@ +//===- Reproduce.cpp - Utilities for creating reproducers -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Reproduce.h" +#include "llvm/Option/Arg.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace lld; +using namespace llvm; +using namespace llvm::sys; + +// Makes a given pathname an absolute path first, and then remove +// beginning /. For example, "../foo.o" is converted to "home/john/foo.o", +// assuming that the current directory is "/home/john/bar". +// Returned string is a forward slash separated path even on Windows to avoid +// a mess with backslash-as-escape and backslash-as-path-separator. +std::string lld::relativeToRoot(StringRef path) { + SmallString<128> abs = path; + if (fs::make_absolute(abs)) + return path; + path::remove_dots(abs, /*remove_dot_dot=*/true); + + // This is Windows specific. root_name() returns a drive letter + // (e.g. "c:") or a UNC name (//net). We want to keep it as part + // of the result. + SmallString<128> res; + StringRef root = path::root_name(abs); + if (root.endswith(":")) + res = root.drop_back(); + else if (root.startswith("//")) + res = root.substr(2); + + path::append(res, path::relative_path(abs)); + return path::convert_to_slash(res); +} + +// Quote a given string if it contains a space character. +std::string lld::quote(StringRef s) { + if (s.contains(' ')) + return ("\"" + s + "\"").str(); + return s; +} + +// Converts an Arg to a string representation suitable for a response file. +// To show an Arg in a diagnostic, use Arg::getAsString() instead. +std::string lld::toString(const opt::Arg &arg) { + std::string k = arg.getSpelling(); + if (arg.getNumValues() == 0) + return k; + std::string v = quote(arg.getValue()); + if (arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle) + return k + v; + return k + " " + v; +} Property changes on: vendor/lld/lld-release_900-r372316/Common/Reproduce.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/Strings.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Strings.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Strings.cpp (revision 352529) @@ -0,0 +1,103 @@ +//===- Strings.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Strings.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/LLVM.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Support/GlobPattern.h" +#include +#include +#include + +using namespace llvm; +using namespace lld; + +// Returns the demangled C++ symbol name for Name. +Optional lld::demangleItanium(StringRef name) { + // itaniumDemangle can be used to demangle strings other than symbol + // names which do not necessarily start with "_Z". Name can be + // either a C or C++ symbol. Don't call itaniumDemangle if the name + // does not look like a C++ symbol name to avoid getting unexpected + // result for a C symbol that happens to match a mangled type name. + if (!name.startswith("_Z")) + return None; + + char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr); + if (!buf) + return None; + std::string s(buf); + free(buf); + return s; +} + +Optional lld::demangleMSVC(StringRef name) { + std::string prefix; + if (name.consume_front("__imp_")) + prefix = "__declspec(dllimport) "; + + // Demangle only C++ names. + if (!name.startswith("?")) + return None; + + char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr); + if (!buf) + return None; + std::string s(buf); + free(buf); + return prefix + s; +} + +StringMatcher::StringMatcher(ArrayRef pat) { + for (StringRef s : pat) { + Expected pat = GlobPattern::create(s); + if (!pat) + error(toString(pat.takeError())); + else + patterns.push_back(*pat); + } +} + +bool StringMatcher::match(StringRef s) const { + for (const GlobPattern &pat : patterns) + if (pat.match(s)) + return true; + return false; +} + +// Converts a hex string (e.g. "deadbeef") to a vector. +std::vector lld::parseHex(StringRef s) { + std::vector hex; + while (!s.empty()) { + StringRef b = s.substr(0, 2); + s = s.substr(2); + uint8_t h; + if (!to_integer(b, h, 16)) { + error("not a hexadecimal value: " + b); + return {}; + } + hex.push_back(h); + } + return hex; +} + +// Returns true if S is valid as a C language identifier. +bool lld::isValidCIdentifier(StringRef s) { + return !s.empty() && (isAlpha(s[0]) || s[0] == '_') && + std::all_of(s.begin() + 1, s.end(), + [](char c) { return c == '_' || isAlnum(c); }); +} + +// Write the contents of the a buffer to a file +void lld::saveBuffer(StringRef buffer, const Twine &path) { + std::error_code ec; + raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None); + if (ec) + error("cannot create " + path + ": " + ec.message()); + os << buffer; +} Property changes on: vendor/lld/lld-release_900-r372316/Common/Strings.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/TargetOptionsCommandFlags.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/TargetOptionsCommandFlags.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/TargetOptionsCommandFlags.cpp (revision 352529) @@ -0,0 +1,35 @@ +//===-- TargetOptionsCommandFlags.cpp ---------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file exists as a place for global variables defined in LLVM's +// CodeGen/CommandFlags.inc. By putting the resulting object file in +// an archive and linking with it, the definitions will automatically be +// included when needed and skipped when already present. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/TargetOptionsCommandFlags.h" + +#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/Target/TargetOptions.h" + +// Define an externally visible version of +// initTargetOptionsFromCodeGenFlags, so that its functionality can be +// used without having to include llvm/CodeGen/CommandFlags.inc, which +// would lead to multiple definitions of the command line flags. +llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { + return ::InitTargetOptionsFromCodeGenFlags(); +} + +llvm::Optional lld::getCodeModelFromCMModel() { + return getCodeModel(); +} + +std::string lld::getCPUStr() { return ::getCPUStr(); } + +std::vector lld::getMAttrs() { return ::MAttrs; } Property changes on: vendor/lld/lld-release_900-r372316/Common/TargetOptionsCommandFlags.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/Threads.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Threads.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Threads.cpp (revision 352529) @@ -0,0 +1,11 @@ +//===- Threads.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Threads.h" + +bool lld::threadsEnabled = true; Property changes on: vendor/lld/lld-release_900-r372316/Common/Threads.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/Timer.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Timer.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Timer.cpp (revision 352529) @@ -0,0 +1,79 @@ +//===- Timer.cpp ----------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Timer.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Format.h" + +using namespace lld; +using namespace llvm; + +ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); } + +void ScopedTimer::stop() { + if (!t) + return; + t->stop(); + t = nullptr; +} + +ScopedTimer::~ScopedTimer() { stop(); } + +Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {} +Timer::Timer(llvm::StringRef name, Timer &parent) + : name(name), parent(&parent) {} + +void Timer::start() { + if (parent && total.count() == 0) + parent->children.push_back(this); + startTime = std::chrono::high_resolution_clock::now(); +} + +void Timer::stop() { + total += (std::chrono::high_resolution_clock::now() - startTime); +} + +Timer &Timer::root() { + static Timer rootTimer("Total Link Time"); + return rootTimer; +} + +void Timer::print() { + double totalDuration = static_cast(root().millis()); + + // We want to print the grand total under all the intermediate phases, so we + // print all children first, then print the total under that. + for (const auto &child : children) + child->print(1, totalDuration); + + message(std::string(49, '-')); + + root().print(0, root().millis(), false); +} + +double Timer::millis() const { + return std::chrono::duration_cast>( + total) + .count(); +} + +void Timer::print(int depth, double totalDuration, bool recurse) const { + double p = 100.0 * millis() / totalDuration; + + SmallString<32> str; + llvm::raw_svector_ostream stream(str); + std::string s = std::string(depth * 2, ' ') + name + std::string(":"); + stream << format("%-30s%5d ms (%5.1f%%)", s.c_str(), (int)millis(), p); + + message(str); + + if (recurse) { + for (const auto &child : children) + child->print(depth + 1, totalDuration); + } +} Property changes on: vendor/lld/lld-release_900-r372316/Common/Timer.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/Common/Version.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/Common/Version.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/Common/Version.cpp (revision 352529) @@ -0,0 +1,27 @@ +//===- lib/Common/Version.cpp - LLD Version Number ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines several version-related utility functions for LLD. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Version.h" + +#ifdef HAVE_VCS_VERSION_INC +#include "VCSVersion.inc" +#endif + +// Returns a version string, e.g.: +// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef) +std::string lld::getLLDVersion() { +#if defined(LLD_REPOSITORY) && defined(LLD_REVISION) + return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")"; +#else + return "LLD " LLD_VERSION_STRING; +#endif +} Property changes on: vendor/lld/lld-release_900-r372316/Common/Version.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/LICENSE.TXT =================================================================== --- vendor/lld/lld-release_900-r372316/LICENSE.TXT (nonexistent) +++ vendor/lld/lld-release_900-r372316/LICENSE.TXT (revision 352529) @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2011-2019 by the contributors listed in CREDITS.TXT +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. Property changes on: vendor/lld/lld-release_900-r372316/LICENSE.TXT ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Args.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Args.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Args.h (revision 352529) @@ -0,0 +1,43 @@ +//===- Args.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_ARGS_H +#define LLD_ARGS_H + +#include "lld/Common/LLVM.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +namespace llvm { +namespace opt { +class InputArgList; +} +} // namespace llvm + +namespace lld { +namespace args { + +llvm::CodeGenOpt::Level getCGOptLevel(int optLevelLTO); + +int64_t getInteger(llvm::opt::InputArgList &args, unsigned key, + int64_t Default); + +std::vector getStrings(llvm::opt::InputArgList &args, int id); + +uint64_t getZOptionValue(llvm::opt::InputArgList &args, int id, StringRef key, + uint64_t Default); + +std::vector getLines(MemoryBufferRef mb); + +StringRef getFilenameWithoutExe(StringRef path); + +} // namespace args +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Args.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Driver.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Driver.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Driver.h (revision 352529) @@ -0,0 +1,42 @@ +//===- lld/Common/Driver.h - Linker Driver Emulator -----------------------===// +// +// 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_COMMON_DRIVER_H +#define LLD_COMMON_DRIVER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" + +namespace lld { +namespace coff { +bool link(llvm::ArrayRef args, bool canExitEarly, + llvm::raw_ostream &diag = llvm::errs()); +} + +namespace mingw { +bool link(llvm::ArrayRef args, + llvm::raw_ostream &diag = llvm::errs()); +} + +namespace elf { +bool link(llvm::ArrayRef args, bool canExitEarly, + llvm::raw_ostream &diag = llvm::errs()); +} + +namespace mach_o { +bool link(llvm::ArrayRef args, bool canExitEarly, + llvm::raw_ostream &diag = llvm::errs()); +} + +namespace wasm { +bool link(llvm::ArrayRef args, bool canExitEarly, + llvm::raw_ostream &diag = llvm::errs()); +} +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Driver.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/ErrorHandler.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/ErrorHandler.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/ErrorHandler.h (revision 352529) @@ -0,0 +1,160 @@ +//===- ErrorHandler.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 +// +//===----------------------------------------------------------------------===// +// +// We designed lld's error handlers with the following goals in mind: +// +// - Errors can occur at any place where we handle user input, but we don't +// want them to affect the normal execution path too much. Ideally, +// handling errors should be as simple as reporting them and exit (but +// without actually doing exit). +// +// In particular, the design to wrap all functions that could fail with +// ErrorOr is rejected because otherwise we would have to wrap a large +// number of functions in lld with ErrorOr. With that approach, if some +// function F can fail, not only F but all functions that transitively call +// F have to be wrapped with ErrorOr. That seemed too much. +// +// - Finding only one error at a time is not sufficient. We want to find as +// many errors as possible with one execution of the linker. That means the +// linker needs to keep running after a first error and give up at some +// checkpoint (beyond which it would find cascading, false errors caused by +// the previous errors). +// +// - We want a simple interface to report errors. Unlike Clang, the data we +// handle is compiled binary, so we don't need an error reporting mechanism +// that's as sophisticated as the one that Clang has. +// +// The current lld's error handling mechanism is simple: +// +// - When you find an error, report it using error() and continue as far as +// you can. An internal error counter is incremented by one every time you +// call error(). +// +// A common idiom to handle an error is calling error() and then returning +// a reasonable default value. For example, if your function handles a +// user-supplied alignment value, and if you find an invalid alignment +// (e.g. 17 which is not 2^n), you may report it using error() and continue +// as if it were alignment 1 (which is the simplest reasonable value). +// +// Note that you should not continue with an invalid value; that breaks the +// internal consistency. You need to maintain all variables have some sane +// value even after an error occurred. So, when you have to continue with +// some value, always use a dummy value. +// +// - Find a reasonable checkpoint at where you want to stop the linker, and +// add code to return from the function if errorCount() > 0. In most cases, +// a checkpoint already exists, so you don't need to do anything for this. +// +// This interface satisfies all the goals that we mentioned above. +// +// You should never call fatal() except for reporting a corrupted input file. +// fatal() immediately terminates the linker, so the function is not desirable +// if you are using lld as a subroutine in other program, and with that you +// can find only one error at a time. +// +// warn() doesn't do anything but printing out a given message. +// +// It is not recommended to use llvm::outs() or llvm::errs() directly in lld +// because they are not thread-safe. The functions declared in this file are +// thread-safe. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_ERRORHANDLER_H +#define LLD_COMMON_ERRORHANDLER_H + +#include "lld/Common/LLVM.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" + +namespace llvm { +class DiagnosticInfo; +} + +namespace lld { + +class ErrorHandler { +public: + uint64_t errorCount = 0; + uint64_t errorLimit = 20; + StringRef errorLimitExceededMsg = "too many errors emitted, stopping now"; + StringRef logName = "lld"; + llvm::raw_ostream *errorOS = &llvm::errs(); + bool colorDiagnostics = llvm::errs().has_colors(); + bool exitEarly = true; + bool fatalWarnings = false; + bool verbose = false; + bool vsDiagnostics = false; + + void error(const Twine &msg); + LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg); + void log(const Twine &msg); + void message(const Twine &msg); + void warn(const Twine &msg); + + std::unique_ptr outputBuffer; + +private: + void printHeader(StringRef s, raw_ostream::Colors c, const Twine &msg); +}; + +/// Returns the default error handler. +ErrorHandler &errorHandler(); + +inline void error(const Twine &msg) { errorHandler().error(msg); } +inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) { + errorHandler().fatal(msg); +} +inline void log(const Twine &msg) { errorHandler().log(msg); } +inline void message(const Twine &msg) { errorHandler().message(msg); } +inline void warn(const Twine &msg) { errorHandler().warn(msg); } +inline uint64_t errorCount() { return errorHandler().errorCount; } + +LLVM_ATTRIBUTE_NORETURN void exitLld(int val); + +void diagnosticHandler(const llvm::DiagnosticInfo &di); +void checkError(Error e); + +// check functions are convenient functions to strip errors +// from error-or-value objects. +template T check(ErrorOr e) { + if (auto ec = e.getError()) + fatal(ec.message()); + return std::move(*e); +} + +template T check(Expected e) { + if (!e) + fatal(llvm::toString(e.takeError())); + return std::move(*e); +} + +template +T check2(ErrorOr e, llvm::function_ref prefix) { + if (auto ec = e.getError()) + fatal(prefix() + ": " + ec.message()); + return std::move(*e); +} + +template +T check2(Expected e, llvm::function_ref prefix) { + if (!e) + fatal(prefix() + ": " + toString(e.takeError())); + return std::move(*e); +} + +inline std::string toString(const Twine &s) { return s.str(); } + +// To evaluate the second argument lazily, we use C macro. +#define CHECK(E, S) check2((E), [&] { return toString(S); }) + +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/ErrorHandler.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Filesystem.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Filesystem.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Filesystem.h (revision 352529) @@ -0,0 +1,20 @@ +//===- Filesystem.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_FILESYSTEM_H +#define LLD_FILESYSTEM_H + +#include "lld/Common/LLVM.h" +#include + +namespace lld { +void unlinkAsync(StringRef path); +std::error_code tryCreateFile(StringRef path); +} // namespace lld + +#endif Index: vendor/lld/lld-release_900-r372316/include/lld/Common/LLVM.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/LLVM.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/LLVM.h (revision 352529) @@ -0,0 +1,100 @@ +//===--- LLVM.h - Import various common LLVM datatypes ----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file forward declares and imports various common LLVM datatypes that +// lld wants to use unqualified. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_LLVM_H +#define LLD_COMMON_LLVM_H + +// This should be the only #include, force #includes of all the others on +// clients. +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/Casting.h" +#include + +namespace llvm { +// ADT's. +class raw_ostream; +class Error; +class StringRef; +class Twine; +class MemoryBuffer; +class MemoryBufferRef; +template class ArrayRef; +template class MutableArrayRef; +template class SmallString; +template class SmallVector; +template class ErrorOr; +template class Expected; + +namespace object { +class WasmObjectFile; +struct WasmSection; +struct WasmSegment; +class WasmSymbol; +} // namespace object + +namespace wasm { +struct WasmEvent; +struct WasmEventType; +struct WasmFunction; +struct WasmGlobal; +struct WasmGlobalType; +struct WasmRelocation; +struct WasmSignature; +} // namespace wasm +} // namespace llvm + +namespace lld { +// Casting operators. +using llvm::cast; +using llvm::cast_or_null; +using llvm::dyn_cast; +using llvm::dyn_cast_or_null; +using llvm::isa; + +// ADT's. +using llvm::ArrayRef; +using llvm::MutableArrayRef; +using llvm::Error; +using llvm::ErrorOr; +using llvm::Expected; +using llvm::MemoryBuffer; +using llvm::MemoryBufferRef; +using llvm::raw_ostream; +using llvm::SmallString; +using llvm::SmallVector; +using llvm::StringRef; +using llvm::Twine; + +using llvm::object::WasmObjectFile; +using llvm::object::WasmSection; +using llvm::object::WasmSegment; +using llvm::object::WasmSymbol; +using llvm::wasm::WasmEvent; +using llvm::wasm::WasmEventType; +using llvm::wasm::WasmFunction; +using llvm::wasm::WasmGlobal; +using llvm::wasm::WasmGlobalType; +using llvm::wasm::WasmRelocation; +using llvm::wasm::WasmSignature; +} // end namespace lld. + +namespace std { +template <> struct hash { +public: + size_t operator()(const llvm::StringRef &s) const { + return llvm::hash_value(s); + } +}; +} // namespace std + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/LLVM.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Memory.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Memory.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Memory.h (revision 352529) @@ -0,0 +1,59 @@ +//===- Memory.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 +// +//===----------------------------------------------------------------------===// +// +// This file defines arena allocators. +// +// Almost all large objects, such as files, sections or symbols, are +// used for the entire lifetime of the linker once they are created. +// This usage characteristic makes arena allocator an attractive choice +// where the entire linker is one arena. With an arena, newly created +// objects belong to the arena and freed all at once when everything is done. +// Arena allocators are efficient and easy to understand. +// Most objects are allocated using the arena allocators defined by this file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_MEMORY_H +#define LLD_COMMON_MEMORY_H + +#include "llvm/Support/Allocator.h" +#include "llvm/Support/StringSaver.h" +#include + +namespace lld { + +// Use this arena if your object doesn't have a destructor. +extern llvm::BumpPtrAllocator bAlloc; +extern llvm::StringSaver saver; + +void freeArena(); + +// These two classes are hack to keep track of all +// SpecificBumpPtrAllocator instances. +struct SpecificAllocBase { + SpecificAllocBase() { instances.push_back(this); } + virtual ~SpecificAllocBase() = default; + virtual void reset() = 0; + static std::vector instances; +}; + +template struct SpecificAlloc : public SpecificAllocBase { + void reset() override { alloc.DestroyAll(); } + llvm::SpecificBumpPtrAllocator alloc; +}; + +// Use this arena if your object has a destructor. +// Your destructor will be invoked from freeArena(). +template T *make(U &&... args) { + static SpecificAlloc alloc; + return new (alloc.alloc.Allocate()) T(std::forward(args)...); +} + +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Memory.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Reproduce.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Reproduce.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Reproduce.h (revision 352529) @@ -0,0 +1,34 @@ +//===- Reproduce.h - Utilities for creating reproducers ---------*- 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_COMMON_REPRODUCE_H +#define LLD_COMMON_REPRODUCE_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace opt { class Arg; } +} + +namespace lld { + +// Makes a given pathname an absolute path first, and then remove +// beginning /. For example, "../foo.o" is converted to "home/john/foo.o", +// assuming that the current directory is "/home/john/bar". +std::string relativeToRoot(StringRef path); + +// Quote a given string if it contains a space character. +std::string quote(StringRef s); + +// Returns the string form of the given argument. +std::string toString(const llvm::opt::Arg &arg); +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Reproduce.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Strings.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Strings.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Strings.h (revision 352529) @@ -0,0 +1,45 @@ +//===- Strings.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_STRINGS_H +#define LLD_STRINGS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/GlobPattern.h" +#include +#include + +namespace lld { +// Returns a demangled C++ symbol name. If Name is not a mangled +// name, it returns Optional::None. +llvm::Optional demangleItanium(llvm::StringRef name); +llvm::Optional demangleMSVC(llvm::StringRef s); + +std::vector parseHex(llvm::StringRef s); +bool isValidCIdentifier(llvm::StringRef s); + +// Write the contents of the a buffer to a file +void saveBuffer(llvm::StringRef buffer, const llvm::Twine &path); + +// This class represents multiple glob patterns. +class StringMatcher { +public: + StringMatcher() = default; + explicit StringMatcher(llvm::ArrayRef pat); + + bool match(llvm::StringRef s) const; + +private: + std::vector patterns; +}; + +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Strings.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/TargetOptionsCommandFlags.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/TargetOptionsCommandFlags.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/TargetOptionsCommandFlags.h (revision 352529) @@ -0,0 +1,22 @@ +//===-- TargetOptionsCommandFlags.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 +// +//===----------------------------------------------------------------------===// +// +// Helper to create TargetOptions from command line flags. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Target/TargetOptions.h" + +namespace lld { +llvm::TargetOptions initTargetOptionsFromCodeGenFlags(); +llvm::Optional getCodeModelFromCMModel(); +std::string getCPUStr(); +std::vector getMAttrs(); +} Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/TargetOptionsCommandFlags.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Threads.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Threads.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Threads.h (revision 352529) @@ -0,0 +1,92 @@ +//===- Threads.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 +// +//===----------------------------------------------------------------------===// +// +// LLD supports threads to distribute workloads to multiple cores. Using +// multicore is most effective when more than one core are idle. At the +// last step of a build, it is often the case that a linker is the only +// active process on a computer. So, we are naturally interested in using +// threads wisely to reduce latency to deliver results to users. +// +// That said, we don't want to do "too clever" things using threads. +// Complex multi-threaded algorithms are sometimes extremely hard to +// reason about and can easily mess up the entire design. +// +// Fortunately, when a linker links large programs (when the link time is +// most critical), it spends most of the time to work on massive number of +// small pieces of data of the same kind, and there are opportunities for +// large parallelism there. Here are examples: +// +// - We have hundreds of thousands of input sections that need to be +// copied to a result file at the last step of link. Once we fix a file +// layout, each section can be copied to its destination and its +// relocations can be applied independently. +// +// - We have tens of millions of small strings when constructing a +// mergeable string section. +// +// For the cases such as the former, we can just use parallelForEach +// instead of std::for_each (or a plain for loop). Because tasks are +// completely independent from each other, we can run them in parallel +// without any coordination between them. That's very easy to understand +// and reason about. +// +// For the cases such as the latter, we can use parallel algorithms to +// deal with massive data. We have to write code for a tailored algorithm +// for each problem, but the complexity of multi-threading is isolated in +// a single pass and doesn't affect the linker's overall design. +// +// The above approach seems to be working fairly well. As an example, when +// linking Chromium (output size 1.6 GB), using 4 cores reduces latency to +// 75% compared to single core (from 12.66 seconds to 9.55 seconds) on my +// Ivy Bridge Xeon 2.8 GHz machine. Using 40 cores reduces it to 63% (from +// 12.66 seconds to 7.95 seconds). Because of the Amdahl's law, the +// speedup is not linear, but as you add more cores, it gets faster. +// +// On a final note, if you are trying to optimize, keep the axiom "don't +// guess, measure!" in mind. Some important passes of the linker are not +// that slow. For example, resolving all symbols is not a very heavy pass, +// although it would be very hard to parallelize it. You want to first +// identify a slow pass and then optimize it. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_THREADS_H +#define LLD_COMMON_THREADS_H + +#include "llvm/Support/Parallel.h" +#include + +namespace lld { + +extern bool threadsEnabled; + +template void parallelForEach(R &&range, FuncTy fn) { + if (threadsEnabled) + for_each(llvm::parallel::par, std::begin(range), std::end(range), fn); + else + for_each(llvm::parallel::seq, std::begin(range), std::end(range), fn); +} + +inline void parallelForEachN(size_t begin, size_t end, + llvm::function_ref fn) { + if (threadsEnabled) + for_each_n(llvm::parallel::par, begin, end, fn); + else + for_each_n(llvm::parallel::seq, begin, end, fn); +} + +template void parallelSort(R &&range, FuncTy fn) { + if (threadsEnabled) + sort(llvm::parallel::par, std::begin(range), std::end(range), fn); + else + sort(llvm::parallel::seq, std::begin(range), std::end(range), fn); +} + +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Threads.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Timer.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Timer.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Timer.h (revision 352529) @@ -0,0 +1,58 @@ +//===- Timer.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_COMMON_TIMER_H +#define LLD_COMMON_TIMER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include +#include +#include +#include + +namespace lld { + +class Timer; + +struct ScopedTimer { + explicit ScopedTimer(Timer &t); + + ~ScopedTimer(); + + void stop(); + + Timer *t = nullptr; +}; + +class Timer { +public: + Timer(llvm::StringRef name, Timer &parent); + + static Timer &root(); + + void start(); + void stop(); + void print(); + + double millis() const; + +private: + explicit Timer(llvm::StringRef name); + void print(int depth, double totalDuration, bool recurse = true) const; + + std::chrono::time_point startTime; + std::chrono::nanoseconds total; + std::vector children; + std::string name; + Timer *parent; +}; + +} // namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Timer.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Version.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Version.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Version.h (revision 352529) @@ -0,0 +1,24 @@ +//===- lld/Common/Version.h - LLD Version Number ----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Defines a version-related utility function. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_VERSION_H +#define LLD_VERSION_H + +#include "lld/Common/Version.inc" +#include "llvm/ADT/StringRef.h" + +namespace lld { +/// Retrieves a string representing the complete lld version. +std::string getLLDVersion(); +} + +#endif // LLD_VERSION_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Version.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Common/Version.inc.in =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Common/Version.inc.in (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Common/Version.inc.in (revision 352529) @@ -0,0 +1,6 @@ +#define LLD_VERSION @LLD_VERSION@ +#define LLD_VERSION_STRING "@LLD_VERSION@" +#define LLD_VERSION_MAJOR @LLD_VERSION_MAJOR@ +#define LLD_VERSION_MINOR @LLD_VERSION_MINOR@ +#define LLD_REVISION_STRING "@LLD_REVISION@" +#define LLD_REPOSITORY_STRING "@LLD_REPOSITORY@" Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Common/Version.inc.in ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/AbsoluteAtom.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/AbsoluteAtom.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/AbsoluteAtom.h (revision 352529) @@ -0,0 +1,42 @@ +//===- Core/AbsoluteAtom.h - An absolute Atom -----------------------------===// +// +// 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_CORE_ABSOLUTE_ATOM_H +#define LLD_CORE_ABSOLUTE_ATOM_H + +#include "lld/Core/Atom.h" + +namespace lld { + +/// An AbsoluteAtom has no content. +/// It exists to represent content at fixed addresses in memory. +class AbsoluteAtom : public Atom { +public: + + virtual uint64_t value() const = 0; + + /// scope - The visibility of this atom to other atoms. C static functions + /// have scope scopeTranslationUnit. Regular C functions have scope + /// scopeGlobal. Functions compiled with visibility=hidden have scope + /// scopeLinkageUnit so they can be see by other atoms being linked but not + /// by the OS loader. + virtual Scope scope() const = 0; + + static bool classof(const Atom *a) { + return a->definition() == definitionAbsolute; + } + + static bool classof(const AbsoluteAtom *) { return true; } + +protected: + AbsoluteAtom() : Atom(definitionAbsolute) {} +}; + +} // namespace lld + +#endif // LLD_CORE_ABSOLUTE_ATOM_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/AbsoluteAtom.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/ArchiveLibraryFile.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/ArchiveLibraryFile.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/ArchiveLibraryFile.h (revision 352529) @@ -0,0 +1,46 @@ +//===- Core/ArchiveLibraryFile.h - Models static library ------------------===// +// +// 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_CORE_ARCHIVE_LIBRARY_FILE_H +#define LLD_CORE_ARCHIVE_LIBRARY_FILE_H + +#include "lld/Core/File.h" +#include + +namespace lld { + +/// +/// The ArchiveLibraryFile subclass of File is used to represent unix +/// static library archives. These libraries provide no atoms to the +/// initial set of atoms linked. Instead, when the Resolver will query +/// ArchiveLibraryFile instances for specific symbols names using the +/// find() method. If the archive contains an object file which has a +/// DefinedAtom whose scope is not translationUnit, then that entire +/// object file File is returned. +/// +class ArchiveLibraryFile : public File { +public: + static bool classof(const File *f) { + return f->kind() == kindArchiveLibrary; + } + + /// Check if any member of the archive contains an Atom with the + /// specified name and return the File object for that member, or nullptr. + virtual File *find(StringRef name) = 0; + + virtual std::error_code + parseAllMembers(std::vector> &result) = 0; + +protected: + /// only subclasses of ArchiveLibraryFile can be instantiated + ArchiveLibraryFile(StringRef path) : File(path, kindArchiveLibrary) {} +}; + +} // namespace lld + +#endif // LLD_CORE_ARCHIVE_LIBRARY_FILE_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/ArchiveLibraryFile.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Atom.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Atom.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Atom.h (revision 352529) @@ -0,0 +1,130 @@ +//===- Core/Atom.h - A node in linking graph --------------------*- 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_CORE_ATOM_H +#define LLD_CORE_ATOM_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/StringRef.h" + +namespace lld { + +class File; + +template +class OwningAtomPtr; + +/// +/// The linker has a Graph Theory model of linking. An object file is seen +/// as a set of Atoms with References to other Atoms. Each Atom is a node +/// and each Reference is an edge. An Atom can be a DefinedAtom which has +/// content or a UndefinedAtom which is a placeholder and represents an +/// undefined symbol (extern declaration). +/// +class Atom { + template friend class OwningAtomPtr; + +public: + /// Whether this atom is defined or a proxy for an undefined symbol + enum Definition { + definitionRegular, ///< Normal C/C++ function or global variable. + definitionAbsolute, ///< Asm-only (foo = 10). Not tied to any content. + definitionUndefined, ///< Only in .o files to model reference to undef. + definitionSharedLibrary ///< Only in shared libraries to model export. + }; + + /// The scope in which this atom is acessible to other atoms. + enum Scope { + scopeTranslationUnit, ///< Accessible only to atoms in the same translation + /// unit (e.g. a C static). + scopeLinkageUnit, ///< Accessible to atoms being linked but not visible + /// to runtime loader (e.g. visibility=hidden). + scopeGlobal ///< Accessible to all atoms and visible to runtime + /// loader (e.g. visibility=default). + }; + + /// file - returns the File that produced/owns this Atom + virtual const File& file() const = 0; + + /// name - The name of the atom. For a function atom, it is the (mangled) + /// name of the function. + virtual StringRef name() const = 0; + + /// definition - Whether this atom is a definition or represents an undefined + /// symbol. + Definition definition() const { return _definition; } + + static bool classof(const Atom *a) { return true; } + +protected: + /// Atom is an abstract base class. Only subclasses can access constructor. + explicit Atom(Definition def) : _definition(def) {} + + /// The memory for Atom objects is always managed by the owning File + /// object. Therefore, no one but the owning File object should call + /// delete on an Atom. In fact, some File objects may bulk allocate + /// an array of Atoms, so they cannot be individually deleted by anyone. + virtual ~Atom() = default; + +private: + Definition _definition; +}; + +/// Class which owns an atom pointer and runs the atom destructor when the +/// owning pointer goes out of scope. +template +class OwningAtomPtr { +private: + OwningAtomPtr(const OwningAtomPtr &) = delete; + void operator=(const OwningAtomPtr &) = delete; + +public: + OwningAtomPtr() = default; + OwningAtomPtr(T *atom) : atom(atom) { } + + ~OwningAtomPtr() { + if (atom) + runDestructor(atom); + } + + void runDestructor(Atom *atom) { + atom->~Atom(); + } + + OwningAtomPtr(OwningAtomPtr &&ptr) : atom(ptr.atom) { + ptr.atom = nullptr; + } + + void operator=(OwningAtomPtr&& ptr) { + if (atom) + runDestructor(atom); + atom = ptr.atom; + ptr.atom = nullptr; + } + + T *const &get() const { + return atom; + } + + T *&get() { + return atom; + } + + T *release() { + auto *v = atom; + atom = nullptr; + return v; + } + +private: + T *atom = nullptr; +}; + +} // end namespace lld + +#endif // LLD_CORE_ATOM_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Atom.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/DefinedAtom.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/DefinedAtom.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/DefinedAtom.h (revision 352529) @@ -0,0 +1,373 @@ +//===- Core/DefinedAtom.h - An Atom with content --------------------------===// +// +// 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_CORE_DEFINED_ATOM_H +#define LLD_CORE_DEFINED_ATOM_H + +#include "lld/Common/LLVM.h" +#include "lld/Core/Atom.h" +#include "lld/Core/Reference.h" +#include "llvm/Support/ErrorHandling.h" + +namespace lld { +class File; + +/// The fundamental unit of linking. +/// +/// A C function or global variable is an atom. An atom has content and +/// attributes. The content of a function atom is the instructions that +/// implement the function. The content of a global variable atom is its +/// initial bytes. +/// +/// Here are some example attribute sets for common atoms. If a particular +/// attribute is not listed, the default values are: definition=regular, +/// sectionChoice=basedOnContent, scope=translationUnit, merge=no, +/// deadStrip=normal, interposable=no +/// +/// C function: void foo() {}
    +/// name=foo, type=code, perm=r_x, scope=global +/// +/// C static function: staic void func() {}
    +/// name=func, type=code, perm=r_x +/// +/// C global variable: int count = 1;
    +/// name=count, type=data, perm=rw_, scope=global +/// +/// C tentative definition: int bar;
    +/// name=bar, type=zerofill, perm=rw_, scope=global, +/// merge=asTentative, interposable=yesAndRuntimeWeak +/// +/// Uninitialized C static variable: static int stuff;
    +/// name=stuff, type=zerofill, perm=rw_ +/// +/// Weak C function: __attribute__((weak)) void foo() {}
    +/// name=foo, type=code, perm=r_x, scope=global, merge=asWeak +/// +/// Hidden C function: __attribute__((visibility("hidden"))) void foo() {}
    +/// name=foo, type=code, perm=r_x, scope=linkageUnit +/// +/// No-dead-strip function: __attribute__((used)) void foo() {}
    +/// name=foo, type=code, perm=r_x, scope=global, deadStrip=never +/// +/// Non-inlined C++ inline method: inline void Foo::doit() {}
    +/// name=_ZN3Foo4doitEv, type=code, perm=r_x, scope=global, +/// mergeDupes=asWeak +/// +/// Non-inlined C++ inline method whose address is taken: +/// inline void Foo::doit() {}
    +/// name=_ZN3Foo4doitEv, type=code, perm=r_x, scope=global, +/// mergeDupes=asAddressedWeak +/// +/// literal c-string: "hello"
    +/// name="" type=cstring, perm=r__, scope=linkageUnit +/// +/// literal double: 1.234
    +/// name="" type=literal8, perm=r__, scope=linkageUnit +/// +/// constant: { 1,2,3 }
    +/// name="" type=constant, perm=r__, scope=linkageUnit +/// +/// Pointer to initializer function:
    +/// name="" type=initializer, perm=rw_l, +/// sectionChoice=customRequired +/// +/// C function place in custom section: __attribute__((section("__foo"))) +/// void foo() {}
    +/// name=foo, type=code, perm=r_x, scope=global, +/// sectionChoice=customRequired, customSectionName=__foo +/// +class DefinedAtom : public Atom { +public: + enum Interposable { + interposeNo, // linker can directly bind uses of this atom + interposeYes, // linker must indirect (through GOT) uses + interposeYesAndRuntimeWeak // must indirect and mark symbol weak in final + // linked image + }; + + enum Merge { + mergeNo, // Another atom with same name is error + mergeAsTentative, // Is ANSI C tentative definition, can be coalesced + mergeAsWeak, // Is C++ inline definition that was not inlined, + // but address was not taken, so atom can be hidden + // by linker + mergeAsWeakAndAddressUsed, // Is C++ definition inline definition whose + // address was taken. + mergeSameNameAndSize, // Another atom with different size is error + mergeByLargestSection, // Choose an atom whose section is the largest. + mergeByContent, // Merge with other constants with same content. + }; + + enum ContentType { + typeUnknown, // for use with definitionUndefined + typeMachHeader, // atom representing mach_header [Darwin] + typeCode, // executable code + typeResolver, // function which returns address of target + typeBranchIsland, // linker created for large binaries + typeBranchShim, // linker created to switch thumb mode + typeStub, // linker created for calling external function + typeStubHelper, // linker created for initial stub binding + typeConstant, // a read-only constant + typeCString, // a zero terminated UTF8 C string + typeUTF16String, // a zero terminated UTF16 string + typeCFI, // a FDE or CIE from dwarf unwind info + typeLSDA, // extra unwinding info + typeLiteral4, // a four-btye read-only constant + typeLiteral8, // an eight-btye read-only constant + typeLiteral16, // a sixteen-btye read-only constant + typeData, // read-write data + typeDataFast, // allow data to be quickly accessed + typeZeroFill, // zero-fill data + typeZeroFillFast, // allow zero-fill data to be quicky accessed + typeConstData, // read-only data after dynamic linker is done + typeObjC1Class, // ObjC1 class [Darwin] + typeLazyPointer, // pointer through which a stub jumps + typeLazyDylibPointer, // pointer through which a stub jumps [Darwin] + typeNonLazyPointer, // pointer to external symbol + typeCFString, // NS/CFString object [Darwin] + typeGOT, // pointer to external symbol + typeInitializerPtr, // pointer to initializer function + typeTerminatorPtr, // pointer to terminator function + typeCStringPtr, // pointer to UTF8 C string [Darwin] + typeObjCClassPtr, // pointer to ObjC class [Darwin] + typeObjC2CategoryList, // pointers to ObjC category [Darwin] + typeObjCImageInfo, // pointer to ObjC class [Darwin] + typeObjCMethodList, // pointer to ObjC method list [Darwin] + typeDTraceDOF, // runtime data for Dtrace [Darwin] + typeInterposingTuples, // tuples of interposing info for dyld [Darwin] + typeTempLTO, // temporary atom for bitcode reader + typeCompactUnwindInfo, // runtime data for unwinder [Darwin] + typeProcessedUnwindInfo,// compressed compact unwind info [Darwin] + typeThunkTLV, // thunk used to access a TLV [Darwin] + typeTLVInitialData, // initial data for a TLV [Darwin] + typeTLVInitialZeroFill, // TLV initial zero fill data [Darwin] + typeTLVInitializerPtr, // pointer to thread local initializer [Darwin] + typeDSOHandle, // atom representing DSO handle [Darwin] + typeSectCreate, // Created via the -sectcreate option [Darwin] + }; + + // Permission bits for atoms and segments. The order of these values are + // important, because the layout pass may sort atoms by permission if other + // attributes are the same. + enum ContentPermissions { + perm___ = 0, // mapped as unaccessible + permR__ = 8, // mapped read-only + permRW_ = 8 + 2, // mapped readable and writable + permRW_L = 8 + 2 + 1, // initially mapped r/w, then made read-only + // loader writable + permR_X = 8 + 4, // mapped readable and executable + permRWX = 8 + 2 + 4, // mapped readable and writable and executable + permUnknown = 16 // unknown or invalid permissions + }; + + enum SectionChoice { + sectionBasedOnContent, // linker infers final section based on content + sectionCustomPreferred, // linker may place in specific section + sectionCustomRequired // linker must place in specific section + }; + + enum DeadStripKind { + deadStripNormal, // linker may dead strip this atom + deadStripNever, // linker must never dead strip this atom + deadStripAlways // linker must remove this atom if unused + }; + + enum DynamicExport { + /// The linker may or may not export this atom dynamically depending + /// on the output type and other context of the link. + dynamicExportNormal, + /// The linker will always export this atom dynamically. + dynamicExportAlways, + }; + + // Attributes describe a code model used by the atom. + enum CodeModel { + codeNA, // no specific code model + // MIPS code models + codeMipsPIC, // PIC function in a PIC / non-PIC mixed file + codeMipsMicro, // microMIPS instruction encoding + codeMipsMicroPIC, // microMIPS instruction encoding + PIC + codeMips16, // MIPS-16 instruction encoding + // ARM code models + codeARMThumb, // ARM Thumb instruction set + codeARM_a, // $a-like mapping symbol (for ARM code) + codeARM_d, // $d-like mapping symbol (for data) + codeARM_t, // $t-like mapping symbol (for Thumb code) + }; + + struct Alignment { + Alignment(int v, int m = 0) : value(v), modulus(m) {} + + uint16_t value; + uint16_t modulus; + + bool operator==(const Alignment &rhs) const { + return (value == rhs.value) && (modulus == rhs.modulus); + } + }; + + /// returns a value for the order of this Atom within its file. + /// + /// This is used by the linker to order the layout of Atoms so that the + /// resulting image is stable and reproducible. + virtual uint64_t ordinal() const = 0; + + /// the number of bytes of space this atom's content will occupy in the + /// final linked image. + /// + /// For a function atom, it is the number of bytes of code in the function. + virtual uint64_t size() const = 0; + + /// The size of the section from which the atom is instantiated. + /// + /// Merge::mergeByLargestSection is defined in terms of section size + /// and not in terms of atom size, so we need this function separate + /// from size(). + virtual uint64_t sectionSize() const { return 0; } + + /// The visibility of this atom to other atoms. + /// + /// C static functions have scope scopeTranslationUnit. Regular C functions + /// have scope scopeGlobal. Functions compiled with visibility=hidden have + /// scope scopeLinkageUnit so they can be see by other atoms being linked but + /// not by the OS loader. + virtual Scope scope() const = 0; + + /// Whether the linker should use direct or indirect access to this + /// atom. + virtual Interposable interposable() const = 0; + + /// how the linker should handle if multiple atoms have the same name. + virtual Merge merge() const = 0; + + /// The type of this atom, such as code or data. + virtual ContentType contentType() const = 0; + + /// The alignment constraints on how this atom must be laid out in the + /// final linked image (e.g. 16-byte aligned). + virtual Alignment alignment() const = 0; + + /// Whether this atom must be in a specially named section in the final + /// linked image, or if the linker can infer the section based on the + /// contentType(). + virtual SectionChoice sectionChoice() const = 0; + + /// If sectionChoice() != sectionBasedOnContent, then this return the + /// name of the section the atom should be placed into. + virtual StringRef customSectionName() const = 0; + + /// constraints on whether the linker may dead strip away this atom. + virtual DeadStripKind deadStrip() const = 0; + + /// Under which conditions should this atom be dynamically exported. + virtual DynamicExport dynamicExport() const { + return dynamicExportNormal; + } + + /// Code model used by the atom. + virtual CodeModel codeModel() const { return codeNA; } + + /// Returns the OS memory protections required for this atom's content + /// at runtime. + /// + /// A function atom is R_X, a global variable is RW_, and a read-only constant + /// is R__. + virtual ContentPermissions permissions() const; + + /// returns a reference to the raw (unrelocated) bytes of this Atom's + /// content. + virtual ArrayRef rawContent() const = 0; + + /// This class abstracts iterating over the sequence of References + /// in an Atom. Concrete instances of DefinedAtom must implement + /// the derefIterator() and incrementIterator() methods. + class reference_iterator { + public: + reference_iterator(const DefinedAtom &a, const void *it) + : _atom(a), _it(it) { } + + const Reference *operator*() const { + return _atom.derefIterator(_it); + } + + const Reference *operator->() const { + return _atom.derefIterator(_it); + } + + bool operator==(const reference_iterator &other) const { + return _it == other._it; + } + + bool operator!=(const reference_iterator &other) const { + return !(*this == other); + } + + reference_iterator &operator++() { + _atom.incrementIterator(_it); + return *this; + } + private: + const DefinedAtom &_atom; + const void *_it; + }; + + /// Returns an iterator to the beginning of this Atom's References. + virtual reference_iterator begin() const = 0; + + /// Returns an iterator to the end of this Atom's References. + virtual reference_iterator end() const = 0; + + /// Adds a reference to this atom. + virtual void addReference(Reference::KindNamespace ns, + Reference::KindArch arch, + Reference::KindValue kindValue, uint64_t off, + const Atom *target, Reference::Addend a) { + llvm_unreachable("Subclass does not permit adding references"); + } + + static bool classof(const Atom *a) { + return a->definition() == definitionRegular; + } + + /// Utility for deriving permissions from content type + static ContentPermissions permissions(ContentType type); + + /// Utility function to check if the atom occupies file space + bool occupiesDiskSpace() const { + ContentType atomContentType = contentType(); + return !(atomContentType == DefinedAtom::typeZeroFill || + atomContentType == DefinedAtom::typeZeroFillFast || + atomContentType == DefinedAtom::typeTLVInitialZeroFill); + } + + /// Utility function to check if relocations in this atom to other defined + /// atoms can be implicitly generated, and so we don't need to explicitly + /// emit those relocations. + bool relocsToDefinedCanBeImplicit() const { + ContentType atomContentType = contentType(); + return atomContentType == typeCFI; + } + +protected: + // DefinedAtom is an abstract base class. Only subclasses can access + // constructor. + DefinedAtom() : Atom(definitionRegular) { } + + ~DefinedAtom() override = default; + + /// Returns a pointer to the Reference object that the abstract + /// iterator "points" to. + virtual const Reference *derefIterator(const void *iter) const = 0; + + /// Adjusts the abstract iterator to "point" to the next Reference + /// object for this Atom. + virtual void incrementIterator(const void *&iter) const = 0; +}; +} // end namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/DefinedAtom.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Error.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Error.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Error.h (revision 352529) @@ -0,0 +1,67 @@ +//===- Error.h - system_error extensions for lld ----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This declares a new error_category for the lld library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_ERROR_H +#define LLD_CORE_ERROR_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" +#include + +namespace lld { + +const std::error_category &YamlReaderCategory(); + +enum class YamlReaderError { + unknown_keyword, + illegal_value +}; + +inline std::error_code make_error_code(YamlReaderError e) { + return std::error_code(static_cast(e), YamlReaderCategory()); +} + +/// Creates an error_code object that has associated with it an arbitrary +/// error messsage. The value() of the error_code will always be non-zero +/// but its value is meaningless. The messsage() will be (a copy of) the +/// supplied error string. +/// Note: Once ErrorOr<> is updated to work with errors other than error_code, +/// this can be updated to return some other kind of error. +std::error_code make_dynamic_error_code(StringRef msg); + +/// Generic error. +/// +/// For errors that don't require their own specific sub-error (most errors) +/// this class can be used to describe the error via a string message. +class GenericError : public llvm::ErrorInfo { +public: + static char ID; + GenericError(Twine Msg); + const std::string &getMessage() const { return Msg; } + void log(llvm::raw_ostream &OS) const override; + + std::error_code convertToErrorCode() const override { + return make_dynamic_error_code(getMessage()); + } + +private: + std::string Msg; +}; + +} // end namespace lld + +namespace std { +template <> struct is_error_code_enum : std::true_type {}; +} + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Error.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/File.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/File.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/File.h (revision 352529) @@ -0,0 +1,275 @@ +//===- Core/File.h - A Container of Atoms ---------------------------------===// +// +// 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_CORE_FILE_H +#define LLD_CORE_FILE_H + +#include "lld/Core/AbsoluteAtom.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/SharedLibraryAtom.h" +#include "lld/Core/UndefinedAtom.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include +#include + +namespace lld { + +class LinkingContext; + +/// Every Atom is owned by some File. A common scenario is for a single +/// object file (.o) to be parsed by some reader and produce a single +/// File object that represents the content of that object file. +/// +/// To iterate through the Atoms in a File there are four methods that +/// return collections. For instance to iterate through all the DefinedAtoms +/// in a File object use: +/// for (const DefinedAtoms *atom : file->defined()) { +/// } +/// +/// The Atom objects in a File are owned by the File object. The Atom objects +/// are destroyed when the File object is destroyed. +class File { +public: + virtual ~File(); + + /// Kinds of files that are supported. + enum Kind { + kindErrorObject, ///< a error object file (.o) + kindNormalizedObject, ///< a normalized file (.o) + kindMachObject, ///< a MachO object file (.o) + kindCEntryObject, ///< a file for CEntries + kindHeaderObject, ///< a file for file headers + kindEntryObject, ///< a file for the entry + kindUndefinedSymsObject, ///< a file for undefined symbols + kindStubHelperObject, ///< a file for stub helpers + kindResolverMergedObject, ///< the resolver merged file. + kindSectCreateObject, ///< a sect create object file (.o) + kindSharedLibrary, ///< shared library (.so) + kindArchiveLibrary ///< archive (.a) + }; + + /// Returns file kind. Need for dyn_cast<> on File objects. + Kind kind() const { + return _kind; + } + + /// This returns the path to the file which was used to create this object + /// (e.g. "/tmp/foo.o"). If the file is a member of an archive file, the + /// returned string includes the archive file name. + StringRef path() const { + if (_archivePath.empty()) + return _path; + if (_archiveMemberPath.empty()) + _archiveMemberPath = (_archivePath + "(" + _path + ")").str(); + return _archiveMemberPath; + } + + /// Returns the path of the archive file name if this file is instantiated + /// from an archive file. Otherwise returns the empty string. + StringRef archivePath() const { return _archivePath; } + void setArchivePath(StringRef path) { _archivePath = path; } + + /// Returns the path name of this file. It doesn't include archive file name. + StringRef memberPath() const { return _path; } + + /// Returns the command line order of the file. + uint64_t ordinal() const { + assert(_ordinal != UINT64_MAX); + return _ordinal; + } + + /// Returns true/false depending on whether an ordinal has been set. + bool hasOrdinal() const { return (_ordinal != UINT64_MAX); } + + /// Sets the command line order of the file. + void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; } + + /// Returns the ordinal for the next atom to be defined in this file. + uint64_t getNextAtomOrdinalAndIncrement() const { + return _nextAtomOrdinal++; + } + + /// For allocating any objects owned by this File. + llvm::BumpPtrAllocator &allocator() const { + return _allocator; + } + + /// The type of atom mutable container. + template using AtomVector = std::vector>; + + /// The range type for the atoms. + template class AtomRange { + public: + AtomRange(AtomVector &v) : _v(v) {} + AtomRange(const AtomVector &v) : _v(const_cast &>(v)) {} + + using ConstDerefFn = const T* (*)(const OwningAtomPtr&); + using DerefFn = T* (*)(OwningAtomPtr&); + + typedef llvm::mapped_iterator::const_iterator, + ConstDerefFn> ConstItTy; + typedef llvm::mapped_iterator::iterator, + DerefFn> ItTy; + + static const T* DerefConst(const OwningAtomPtr &p) { + return p.get(); + } + + static T* Deref(OwningAtomPtr &p) { + return p.get(); + } + + ConstItTy begin() const { + return ConstItTy(_v.begin(), ConstDerefFn(DerefConst)); + } + ConstItTy end() const { + return ConstItTy(_v.end(), ConstDerefFn(DerefConst)); + } + + ItTy begin() { + return ItTy(_v.begin(), DerefFn(Deref)); + } + ItTy end() { + return ItTy(_v.end(), DerefFn(Deref)); + } + + llvm::iterator_range::iterator> owning_ptrs() { + return llvm::make_range(_v.begin(), _v.end()); + } + + llvm::iterator_range::iterator> owning_ptrs() const { + return llvm::make_range(_v.begin(), _v.end()); + } + + bool empty() const { + return _v.empty(); + } + + size_t size() const { + return _v.size(); + } + + const OwningAtomPtr &operator[](size_t idx) const { + return _v[idx]; + } + + OwningAtomPtr &operator[](size_t idx) { + return _v[idx]; + } + + private: + AtomVector &_v; + }; + + /// Must be implemented to return the AtomVector object for + /// all DefinedAtoms in this File. + virtual const AtomRange defined() const = 0; + + /// Must be implemented to return the AtomVector object for + /// all UndefinedAtomw in this File. + virtual const AtomRange undefined() const = 0; + + /// Must be implemented to return the AtomVector object for + /// all SharedLibraryAtoms in this File. + virtual const AtomRange sharedLibrary() const = 0; + + /// Must be implemented to return the AtomVector object for + /// all AbsoluteAtoms in this File. + virtual const AtomRange absolute() const = 0; + + /// Drop all of the atoms owned by this file. This will result in all of + /// the atoms running their destructors. + /// This is required because atoms may be allocated on a BumpPtrAllocator + /// of a different file. We need to destruct all atoms before any files. + virtual void clearAtoms() = 0; + + /// If a file is parsed using a different method than doParse(), + /// one must use this method to set the last error status, so that + /// doParse will not be called twice. Only YAML reader uses this + /// (because YAML reader does not read blobs but structured data). + void setLastError(std::error_code err) { _lastError = err; } + + std::error_code parse(); + + // Usually each file owns a std::unique_ptr. + // However, there's one special case. If a file is an archive file, + // the archive file and its children all shares the same memory buffer. + // This method is used by the ArchiveFile to give its children + // co-ownership of the buffer. + void setSharedMemoryBuffer(std::shared_ptr mb) { + _sharedMemoryBuffer = mb; + } + +protected: + /// only subclasses of File can be instantiated + File(StringRef p, Kind kind) + : _path(p), _kind(kind), _ordinal(UINT64_MAX), + _nextAtomOrdinal(0) {} + + /// Subclasses should override this method to parse the + /// memory buffer passed to this file's constructor. + virtual std::error_code doParse() { return std::error_code(); } + + static AtomVector _noDefinedAtoms; + static AtomVector _noUndefinedAtoms; + static AtomVector _noSharedLibraryAtoms; + static AtomVector _noAbsoluteAtoms; + mutable llvm::BumpPtrAllocator _allocator; + +private: + StringRef _path; + std::string _archivePath; + mutable std::string _archiveMemberPath; + Kind _kind; + mutable uint64_t _ordinal; + mutable uint64_t _nextAtomOrdinal; + std::shared_ptr _sharedMemoryBuffer; + llvm::Optional _lastError; + std::mutex _parseMutex; +}; + +/// An ErrorFile represents a file that doesn't exist. +/// If you try to parse a file which doesn't exist, an instance of this +/// class will be returned. That's parse method always returns an error. +/// This is useful to delay erroring on non-existent files, so that we +/// can do unit testing a driver using non-existing file paths. +class ErrorFile : public File { +public: + ErrorFile(StringRef path, std::error_code ec) + : File(path, kindErrorObject), _ec(ec) {} + + std::error_code doParse() override { return _ec; } + + const AtomRange defined() const override { + llvm_unreachable("internal error"); + } + const AtomRange undefined() const override { + llvm_unreachable("internal error"); + } + const AtomRange sharedLibrary() const override { + llvm_unreachable("internal error"); + } + const AtomRange absolute() const override { + llvm_unreachable("internal error"); + } + + void clearAtoms() override { + } + +private: + std::error_code _ec; +}; + +} // end namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/File.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Instrumentation.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Instrumentation.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Instrumentation.h (revision 352529) @@ -0,0 +1,131 @@ +//===- include/Core/Instrumentation.h - Instrumentation API ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Provide an Instrumentation API that optionally uses VTune interfaces. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_INSTRUMENTATION_H +#define LLD_CORE_INSTRUMENTATION_H + +#include "llvm/Support/Compiler.h" +#include + +#ifdef LLD_HAS_VTUNE +# include +#endif + +namespace lld { +#ifdef LLD_HAS_VTUNE +/// A unique global scope for instrumentation data. +/// +/// Domains last for the lifetime of the application and cannot be destroyed. +/// Multiple Domains created with the same name represent the same domain. +class Domain { + __itt_domain *_domain; + +public: + explicit Domain(const char *name) : _domain(__itt_domain_createA(name)) {} + + operator __itt_domain *() const { return _domain; } + __itt_domain *operator->() const { return _domain; } +}; + +/// A global reference to a string constant. +/// +/// These are uniqued by the ITT runtime and cannot be deleted. They are not +/// specific to a domain. +/// +/// Prefer reusing a single StringHandle over passing a ntbs when the same +/// string will be used often. +class StringHandle { + __itt_string_handle *_handle; + +public: + StringHandle(const char *name) : _handle(__itt_string_handle_createA(name)) {} + + operator __itt_string_handle *() const { return _handle; } +}; + +/// A task on a single thread. Nests within other tasks. +/// +/// Each thread has its own task stack and tasks nest recursively on that stack. +/// A task cannot transfer threads. +/// +/// SBRM is used to ensure task starts and ends are ballanced. The lifetime of +/// a task is either the lifetime of this object, or until end is called. +class ScopedTask { + __itt_domain *_domain; + + ScopedTask(const ScopedTask &) = delete; + ScopedTask &operator=(const ScopedTask &) = delete; + +public: + /// Create a task in Domain \p d named \p s. + ScopedTask(const Domain &d, const StringHandle &s) : _domain(d) { + __itt_task_begin(d, __itt_null, __itt_null, s); + } + + ScopedTask(ScopedTask &&other) { + *this = std::move(other); + } + + ScopedTask &operator=(ScopedTask &&other) { + _domain = other._domain; + other._domain = nullptr; + return *this; + } + + /// Prematurely end this task. + void end() { + if (_domain) + __itt_task_end(_domain); + _domain = nullptr; + } + + ~ScopedTask() { end(); } +}; + +/// A specific point in time. Allows metadata to be associated. +class Marker { +public: + Marker(const Domain &d, const StringHandle &s) { + __itt_marker(d, __itt_null, s, __itt_scope_global); + } +}; +#else +class Domain { +public: + Domain(const char *name) {} +}; + +class StringHandle { +public: + StringHandle(const char *name) {} +}; + +class ScopedTask { +public: + ScopedTask(const Domain &d, const StringHandle &s) {} + void end() {} +}; + +class Marker { +public: + Marker(const Domain &d, const StringHandle &s) {} +}; +#endif + +inline const Domain &getDefaultDomain() { + static Domain domain("org.llvm.lld"); + return domain; +} +} // end namespace lld. + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Instrumentation.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/LinkingContext.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/LinkingContext.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/LinkingContext.h (revision 352529) @@ -0,0 +1,256 @@ +//===- lld/Core/LinkingContext.h - Linker Target Info Interface -*- 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_CORE_LINKING_CONTEXT_H +#define LLD_CORE_LINKING_CONTEXT_H + +#include "lld/Core/Node.h" +#include "lld/Core/Reader.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include +#include + +namespace lld { + +class PassManager; +class File; +class Writer; +class Node; +class SharedLibraryFile; + +/// The LinkingContext class encapsulates "what and how" to link. +/// +/// The base class LinkingContext contains the options needed by core linking. +/// Subclasses of LinkingContext have additional options needed by specific +/// Writers. +class LinkingContext { +public: + virtual ~LinkingContext(); + + /// \name Methods needed by core linking + /// @{ + + /// Name of symbol linker should use as "entry point" to program, + /// usually "main" or "start". + virtual StringRef entrySymbolName() const { return _entrySymbolName; } + + /// Whether core linking should remove Atoms not reachable by following + /// References from the entry point Atom or from all global scope Atoms + /// if globalsAreDeadStripRoots() is true. + bool deadStrip() const { return _deadStrip; } + + /// Only used if deadStrip() returns true. Means all global scope Atoms + /// should be marked live (along with all Atoms they reference). Usually + /// this method returns false for main executables, but true for dynamic + /// shared libraries. + bool globalsAreDeadStripRoots() const { return _globalsAreDeadStripRoots; } + + /// Only used if deadStrip() returns true. This method returns the names + /// of DefinedAtoms that should be marked live (along with all Atoms they + /// reference). Only Atoms with scope scopeLinkageUnit or scopeGlobal can + /// be kept live using this method. + ArrayRef deadStripRoots() const { + return _deadStripRoots; + } + + /// Add the given symbol name to the dead strip root set. Only used if + /// deadStrip() returns true. + void addDeadStripRoot(StringRef symbolName) { + assert(!symbolName.empty() && "Empty symbol cannot be a dead strip root"); + _deadStripRoots.push_back(symbolName); + } + + /// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a + /// SharedLibraryAtom for the link to be successful. This method controls + /// whether core linking prints out a list of remaining UndefinedAtoms. + /// + /// \todo This should be a method core linking calls with a list of the + /// UndefinedAtoms so that different drivers can format the error message + /// as needed. + bool printRemainingUndefines() const { return _printRemainingUndefines; } + + /// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a + /// SharedLibraryAtom for the link to be successful. This method controls + /// whether core linking considers remaining undefines to be an error. + bool allowRemainingUndefines() const { return _allowRemainingUndefines; } + + /// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a + /// SharedLibraryAtom for the link to be successful. This method controls + /// whether core linking considers remaining undefines from the shared library + /// to be an error. + bool allowShlibUndefines() const { return _allowShlibUndefines; } + + /// If true, core linking will write the path to each input file to stdout + /// (i.e. llvm::outs()) as it is used. This is used to implement the -t + /// linker option. + /// + /// \todo This should be a method core linking calls so that drivers can + /// format the line as needed. + bool logInputFiles() const { return _logInputFiles; } + + /// Parts of LLVM use global variables which are bound to command line + /// options (see llvm::cl::Options). This method returns "command line" + /// options which are used to configure LLVM's command line settings. + /// For instance the -debug-only XXX option can be used to dynamically + /// trace different parts of LLVM and lld. + ArrayRef llvmOptions() const { return _llvmOptions; } + + /// \name Methods used by Drivers to configure TargetInfo + /// @{ + void setOutputPath(StringRef str) { _outputPath = str; } + + // Set the entry symbol name. You may also need to call addDeadStripRoot() for + // the symbol if your platform supports dead-stripping, so that the symbol + // will not be removed from the output. + void setEntrySymbolName(StringRef name) { + _entrySymbolName = name; + } + + void setDeadStripping(bool enable) { _deadStrip = enable; } + void setGlobalsAreDeadStripRoots(bool v) { _globalsAreDeadStripRoots = v; } + + void setPrintRemainingUndefines(bool print) { + _printRemainingUndefines = print; + } + + void setAllowRemainingUndefines(bool allow) { + _allowRemainingUndefines = allow; + } + + void setAllowShlibUndefines(bool allow) { _allowShlibUndefines = allow; } + void setLogInputFiles(bool log) { _logInputFiles = log; } + + void appendLLVMOption(const char *opt) { _llvmOptions.push_back(opt); } + + std::vector> &getNodes() { return _nodes; } + const std::vector> &getNodes() const { return _nodes; } + + /// This method adds undefined symbols specified by the -u option to the to + /// the list of undefined symbols known to the linker. This option essentially + /// forces an undefined symbol to be created. You may also need to call + /// addDeadStripRoot() for the symbol if your platform supports dead + /// stripping, so that the symbol will not be removed from the output. + void addInitialUndefinedSymbol(StringRef symbolName) { + _initialUndefinedSymbols.push_back(symbolName); + } + + /// Iterators for symbols that appear on the command line. + typedef std::vector StringRefVector; + typedef StringRefVector::iterator StringRefVectorIter; + typedef StringRefVector::const_iterator StringRefVectorConstIter; + + /// Create linker internal files containing atoms for the linker to include + /// during link. Flavors can override this function in their LinkingContext + /// to add more internal files. These internal files are positioned before + /// the actual input files. + virtual void createInternalFiles(std::vector> &) const; + + /// Return the list of undefined symbols that are specified in the + /// linker command line, using the -u option. + ArrayRef initialUndefinedSymbols() const { + return _initialUndefinedSymbols; + } + + /// After all set* methods are called, the Driver calls this method + /// to validate that there are no missing options or invalid combinations + /// of options. If there is a problem, a description of the problem + /// is written to the global error handler. + /// + /// \returns true if there is an error with the current settings. + bool validate(); + + /// Formats symbol name for use in error messages. + virtual std::string demangle(StringRef symbolName) const = 0; + + /// @} + /// \name Methods used by Driver::link() + /// @{ + + /// Returns the file system path to which the linked output should be written. + /// + /// \todo To support in-memory linking, we need an abstraction that allows + /// the linker to write to an in-memory buffer. + StringRef outputPath() const { return _outputPath; } + + /// Accessor for Register object embedded in LinkingContext. + const Registry ®istry() const { return _registry; } + Registry ®istry() { return _registry; } + + /// This method is called by core linking to give the Writer a chance + /// to add file format specific "files" to set of files to be linked. This is + /// how file format specific atoms can be added to the link. + virtual void createImplicitFiles(std::vector> &) = 0; + + /// This method is called by core linking to build the list of Passes to be + /// run on the merged/linked graph of all input files. + virtual void addPasses(PassManager &pm) = 0; + + /// Calls through to the writeFile() method on the specified Writer. + /// + /// \param linkedFile This is the merged/linked graph of all input file Atoms. + virtual llvm::Error writeFile(const File &linkedFile) const; + + /// Return the next ordinal and Increment it. + virtual uint64_t getNextOrdinalAndIncrement() const { return _nextOrdinal++; } + + // This function is called just before the Resolver kicks in. + // Derived classes may use it to change the list of input files. + virtual void finalizeInputFiles() = 0; + + /// Callback invoked for each file the Resolver decides we are going to load. + /// This can be used to update context state based on the file, and emit + /// errors for any differences between the context state and a loaded file. + /// For example, we can error if we try to load a file which is a different + /// arch from that being linked. + virtual llvm::Error handleLoadedFile(File &file) = 0; + + /// @} +protected: + LinkingContext(); // Must be subclassed + + /// Abstract method to lazily instantiate the Writer. + virtual Writer &writer() const = 0; + + /// Method to create an internal file for the entry symbol + virtual std::unique_ptr createEntrySymbolFile() const; + std::unique_ptr createEntrySymbolFile(StringRef filename) const; + + /// Method to create an internal file for an undefined symbol + virtual std::unique_ptr createUndefinedSymbolFile() const; + std::unique_ptr createUndefinedSymbolFile(StringRef filename) const; + + StringRef _outputPath; + StringRef _entrySymbolName; + bool _deadStrip = false; + bool _globalsAreDeadStripRoots = false; + bool _printRemainingUndefines = true; + bool _allowRemainingUndefines = false; + bool _logInputFiles = false; + bool _allowShlibUndefines = false; + std::vector _deadStripRoots; + std::vector _llvmOptions; + StringRefVector _initialUndefinedSymbols; + std::vector> _nodes; + mutable llvm::BumpPtrAllocator _allocator; + mutable uint64_t _nextOrdinal = 0; + Registry _registry; + +private: + /// Validate the subclass bits. Only called by validate. + virtual bool validateImpl() = 0; +}; + +} // end namespace lld + +#endif // LLD_CORE_LINKING_CONTEXT_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/LinkingContext.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Node.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Node.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Node.h (revision 352529) @@ -0,0 +1,74 @@ +//===- lld/Core/Node.h - Input file class -----------------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// The classes in this file represents inputs to the linker. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_NODE_H +#define LLD_CORE_NODE_H + +#include "lld/Core/File.h" +#include +#include + +namespace lld { + +// A Node represents a FileNode or other type of Node. In the latter case, +// the node contains meta information about the input file list. +// Currently only GroupEnd node is defined as a meta node. +class Node { +public: + enum class Kind { File, GroupEnd }; + + explicit Node(Kind type) : _kind(type) {} + virtual ~Node() = default; + + virtual Kind kind() const { return _kind; } + +private: + Kind _kind; +}; + +// This is a marker for --end-group. getSize() returns the number of +// files between the corresponding --start-group and this marker. +class GroupEnd : public Node { +public: + explicit GroupEnd(int size) : Node(Kind::GroupEnd), _size(size) {} + + int getSize() const { return _size; } + + static bool classof(const Node *a) { + return a->kind() == Kind::GroupEnd; + } + +private: + int _size; +}; + +// A container of File. +class FileNode : public Node { +public: + explicit FileNode(std::unique_ptr f) + : Node(Node::Kind::File), _file(std::move(f)) {} + + static bool classof(const Node *a) { + return a->kind() == Node::Kind::File; + } + + File *getFile() { return _file.get(); } + +protected: + std::unique_ptr _file; +}; + +} // end namespace lld + +#endif // LLD_CORE_NODE_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Node.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Pass.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Pass.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Pass.h (revision 352529) @@ -0,0 +1,42 @@ +//===------ Core/Pass.h - Base class for linker passes ----------*- 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_CORE_PASS_H +#define LLD_CORE_PASS_H + +#include "llvm/Support/Error.h" + +namespace lld { + +class SimpleFile; + +/// Once the core linking is done (which resolves references, coalesces atoms +/// and produces a complete Atom graph), the linker runs a series of passes +/// on the Atom graph. The graph is modeled as a File, which means the pass +/// has access to all the atoms and to File level attributes. Each pass does +/// a particular transformation to the Atom graph or to the File attributes. +/// +/// This is the abstract base class for all passes. A Pass does its +/// actual work in it perform() method. It can iterator over Atoms in the +/// graph using the *begin()/*end() atom iterator of the File. It can add +/// new Atoms to the graph using the File's addAtom() method. +class Pass { +public: + virtual ~Pass() = default; + + /// Do the actual work of the Pass. + virtual llvm::Error perform(SimpleFile &mergedFile) = 0; + +protected: + // Only subclassess can be instantiated. + Pass() = default; +}; + +} // end namespace lld + +#endif // LLD_CORE_PASS_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Pass.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/PassManager.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/PassManager.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/PassManager.h (revision 352529) @@ -0,0 +1,47 @@ +//===- lld/Core/PassManager.h - Manage linker passes ----------------------===// +// +// 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_CORE_PASS_MANAGER_H +#define LLD_CORE_PASS_MANAGER_H + +#include "lld/Common/LLVM.h" +#include "lld/Core/Pass.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace lld { +class SimpleFile; +class Pass; + +/// Owns and runs a collection of passes. +/// +/// This class is currently just a container for passes and a way to run them. +/// +/// In the future this should handle timing pass runs, running parallel passes, +/// and validate/satisfy pass dependencies. +class PassManager { +public: + void add(std::unique_ptr pass) { + _passes.push_back(std::move(pass)); + } + + llvm::Error runOnFile(SimpleFile &file) { + for (std::unique_ptr &pass : _passes) + if (llvm::Error EC = pass->perform(file)) + return EC; + return llvm::Error::success(); + } + +private: + /// Passes in the order they should run. + std::vector> _passes; +}; +} // end namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/PassManager.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Reader.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Reader.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Reader.h (revision 352529) @@ -0,0 +1,154 @@ +//===- lld/Core/Reader.h - Abstract File Format Reading Interface ---------===// +// +// 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_CORE_READER_H +#define LLD_CORE_READER_H + +#include "lld/Common/LLVM.h" +#include "lld/Core/Reference.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include +#include + +namespace llvm { +namespace yaml { +class IO; +} // end namespace yaml +} // end namespace llvm + +namespace lld { + +class File; +class LinkingContext; +class MachOLinkingContext; + +/// An abstract class for reading object files, library files, and +/// executable files. +/// +/// Each file format (e.g. mach-o, etc) has a concrete subclass of Reader. +class Reader { +public: + virtual ~Reader() = default; + + /// Sniffs the file to determine if this Reader can parse it. + /// The method is called with: + /// 1) the file_magic enumeration returned by identify_magic() + /// 2) the whole file content buffer if the above is not enough. + virtual bool canParse(llvm::file_magic magic, MemoryBufferRef mb) const = 0; + + /// Parse a supplied buffer (already filled with the contents of a + /// file) and create a File object. + /// The resulting File object takes ownership of the MemoryBuffer. + virtual ErrorOr> + loadFile(std::unique_ptr mb, const class Registry &) const = 0; +}; + +/// An abstract class for handling alternate yaml representations +/// of object files. +/// +/// The YAML syntax allows "tags" which are used to specify the type of +/// the YAML node. In lld, top level YAML documents can be in many YAML +/// representations (e.g mach-o encoded as yaml, etc). A tag is used to +/// specify which representation is used in the following YAML document. +/// To work, there must be a YamlIOTaggedDocumentHandler registered that +/// handles each tag type. +class YamlIOTaggedDocumentHandler { +public: + virtual ~YamlIOTaggedDocumentHandler(); + + /// This method is called on each registered YamlIOTaggedDocumentHandler + /// until one returns true. If the subclass handles tag type !xyz, then + /// this method should call io.mapTag("!xzy") to see if that is the current + /// document type, and if so, process the rest of the document using + /// YAML I/O, then convert the result into an lld::File* and return it. + virtual bool handledDocTag(llvm::yaml::IO &io, const lld::File *&f) const = 0; +}; + +/// A registry to hold the list of currently registered Readers and +/// tables which map Reference kind values to strings. +/// The linker does not directly invoke Readers. Instead, it registers +/// Readers based on it configuration and command line options, then calls +/// the Registry object to parse files. +class Registry { +public: + Registry(); + + /// Walk the list of registered Readers and find one that can parse the + /// supplied file and parse it. + ErrorOr> + loadFile(std::unique_ptr mb) const; + + /// Walk the list of registered kind tables to convert a Reference Kind + /// name to a value. + bool referenceKindFromString(StringRef inputStr, Reference::KindNamespace &ns, + Reference::KindArch &a, + Reference::KindValue &value) const; + + /// Walk the list of registered kind tables to convert a Reference Kind + /// value to a string. + bool referenceKindToString(Reference::KindNamespace ns, Reference::KindArch a, + Reference::KindValue value, StringRef &) const; + + /// Walk the list of registered tag handlers and have the one that handles + /// the current document type process the yaml into an lld::File*. + bool handleTaggedDoc(llvm::yaml::IO &io, const lld::File *&file) const; + + // These methods are called to dynamically add support for various file + // formats. The methods are also implemented in the appropriate lib*.a + // library, so that the code for handling a format is only linked in, if this + // method is used. Any options that a Reader might need must be passed + // as parameters to the addSupport*() method. + void addSupportArchives(bool logLoading); + void addSupportYamlFiles(); + void addSupportMachOObjects(MachOLinkingContext &); + + /// To convert between kind values and names, the registry walks the list + /// of registered kind tables. Each table is a zero terminated array of + /// KindStrings elements. + struct KindStrings { + Reference::KindValue value; + StringRef name; + }; + + /// A Reference Kind value is a tuple of . All + /// entries in a conversion table have the same . The + /// array then contains the value/name pairs. + void addKindTable(Reference::KindNamespace ns, Reference::KindArch arch, + const KindStrings array[]); + +private: + struct KindEntry { + Reference::KindNamespace ns; + Reference::KindArch arch; + const KindStrings *array; + }; + + void add(std::unique_ptr); + void add(std::unique_ptr); + + std::vector> _readers; + std::vector> _yamlHandlers; + std::vector _kindEntries; +}; + +// Utilities for building a KindString table. For instance: +// static const Registry::KindStrings table[] = { +// LLD_KIND_STRING_ENTRY(R_VAX_ADDR16), +// LLD_KIND_STRING_ENTRY(R_VAX_DATA16), +// LLD_KIND_STRING_END +// }; +#define LLD_KIND_STRING_ENTRY(name) { name, #name } +#define LLD_KIND_STRING_END { 0, "" } + +} // end namespace lld + +#endif // LLD_CORE_READER_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Reader.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Reference.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Reference.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Reference.h (revision 352529) @@ -0,0 +1,118 @@ +//===- Core/References.h - A Reference to Another Atom ----------*- 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_CORE_REFERENCES_H +#define LLD_CORE_REFERENCES_H + +#include + +namespace lld { + +class Atom; + +/// +/// The linker has a Graph Theory model of linking. An object file is seen +/// as a set of Atoms with References to other Atoms. Each Atom is a node +/// and each Reference is an edge. +/// +/// For example if a function contains a call site to "malloc" 40 bytes into +/// the Atom, then the function Atom will have a Reference of: offsetInAtom=40, +/// kind=callsite, target=malloc, addend=0. +/// +/// Besides supporting traditional "relocations", references are also used +/// forcing layout (one atom must follow another), marking data-in-code +/// (jump tables or ARM constants), etc. +/// +/// The "kind" of a reference is a tuple of . This +/// enable us to re-use existing relocation types definded for various +/// file formats and architectures. +/// +/// References and atoms form a directed graph. The dead-stripping pass +/// traverses them starting from dead-strip root atoms to garbage collect +/// unreachable ones. +/// +/// References of any kind are considered as directed edges. In addition to +/// that, references of some kind is considered as bidirected edges. +class Reference { +public: + /// Which universe defines the kindValue(). + enum class KindNamespace { + all = 0, + testing = 1, + mach_o = 2, + }; + + KindNamespace kindNamespace() const { return (KindNamespace)_kindNamespace; } + void setKindNamespace(KindNamespace ns) { _kindNamespace = (uint8_t)ns; } + + // Which architecture the kind value is for. + enum class KindArch { all, AArch64, ARM, x86, x86_64}; + + KindArch kindArch() const { return (KindArch)_kindArch; } + void setKindArch(KindArch a) { _kindArch = (uint8_t)a; } + + typedef uint16_t KindValue; + + KindValue kindValue() const { return _kindValue; } + + /// setKindValue() is needed because during linking, some optimizations may + /// change the codegen and hence the reference kind. + void setKindValue(KindValue value) { + _kindValue = value; + } + + /// KindValues used with KindNamespace::all and KindArch::all. + enum { + // kindLayoutAfter is treated as a bidirected edge by the dead-stripping + // pass. + kindLayoutAfter = 1, + kindAssociate, + }; + + // A value to be added to the value of a target + typedef int64_t Addend; + + /// If the reference is a fixup in the Atom, then this returns the + /// byte offset into the Atom's content to do the fix up. + virtual uint64_t offsetInAtom() const = 0; + + /// Returns the atom this reference refers to. + virtual const Atom *target() const = 0; + + /// During linking, the linker may merge graphs which coalesces some nodes + /// (i.e. Atoms). To switch the target of a reference, this method is called. + virtual void setTarget(const Atom *) = 0; + + /// Some relocations require a symbol and a value (e.g. foo + 4). + virtual Addend addend() const = 0; + + /// During linking, some optimzations may change addend value. + virtual void setAddend(Addend) = 0; + + /// Returns target specific attributes of the reference. + virtual uint32_t tag() const { return 0; } + +protected: + /// Reference is an abstract base class. Only subclasses can use constructor. + Reference(KindNamespace ns, KindArch a, KindValue value) + : _kindValue(value), _kindNamespace((uint8_t)ns), _kindArch((uint8_t)a) {} + + /// The memory for Reference objects is always managed by the owning File + /// object. Therefore, no one but the owning File object should call + /// delete on an Reference. In fact, some File objects may bulk allocate + /// an array of References, so they cannot be individually deleted by anyone. + virtual ~Reference() = default; + + KindValue _kindValue; + uint8_t _kindNamespace; + uint8_t _kindArch; +}; + +} // end namespace lld + +#endif // LLD_CORE_REFERENCES_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Reference.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Resolver.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Resolver.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Resolver.h (revision 352529) @@ -0,0 +1,105 @@ +//===- Core/Resolver.h - Resolves Atom References -------------------------===// +// +// 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_CORE_RESOLVER_H +#define LLD_CORE_RESOLVER_H + +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/File.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Core/Simple.h" +#include "lld/Core/SymbolTable.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/ErrorOr.h" +#include +#include +#include +#include + +namespace lld { + +class Atom; +class LinkingContext; + +/// The Resolver is responsible for merging all input object files +/// and producing a merged graph. +class Resolver { +public: + Resolver(LinkingContext &ctx) : _ctx(ctx), _result(new MergedFile()) {} + + // InputFiles::Handler methods + void doDefinedAtom(OwningAtomPtr atom); + bool doUndefinedAtom(OwningAtomPtr atom); + void doSharedLibraryAtom(OwningAtomPtr atom); + void doAbsoluteAtom(OwningAtomPtr atom); + + // Handle files, this adds atoms from the current file thats + // being processed by the resolver + llvm::Expected handleFile(File &); + + // Handle an archive library file. + llvm::Expected handleArchiveFile(File &); + + // Handle a shared library file. + llvm::Error handleSharedLibrary(File &); + + /// do work of merging and resolving and return list + bool resolve(); + + std::unique_ptr resultFile() { return std::move(_result); } + +private: + typedef std::function(StringRef)> UndefCallback; + + bool undefinesAdded(int begin, int end); + File *getFile(int &index); + + /// The main function that iterates over the files to resolve + bool resolveUndefines(); + void updateReferences(); + void deadStripOptimize(); + bool checkUndefines(); + void removeCoalescedAwayAtoms(); + llvm::Expected forEachUndefines(File &file, UndefCallback callback); + + void markLive(const Atom *atom); + + class MergedFile : public SimpleFile { + public: + MergedFile() : SimpleFile("", kindResolverMergedObject) {} + void addAtoms(llvm::MutableArrayRef> atoms); + }; + + LinkingContext &_ctx; + SymbolTable _symbolTable; + std::vector> _atoms; + std::set _deadStripRoots; + llvm::DenseSet _liveAtoms; + llvm::DenseSet _deadAtoms; + std::unique_ptr _result; + std::unordered_multimap _reverseRef; + + // --start-group and --end-group + std::vector _files; + std::map _newUndefinesAdded; + + // List of undefined symbols. + std::vector _undefines; + + // Start position in _undefines for each archive/shared library file. + // Symbols from index 0 to the start position are already searched before. + // Searching them again would never succeed. When we look for undefined + // symbols from an archive/shared library file, start from its start + // position to save time. + std::map _undefineIndex; +}; + +} // namespace lld + +#endif // LLD_CORE_RESOLVER_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Resolver.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryAtom.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryAtom.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryAtom.h (revision 352529) @@ -0,0 +1,52 @@ +//===- Core/SharedLibraryAtom.h - A Shared Library Atom -------------------===// +// +// 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_CORE_SHARED_LIBRARY_ATOM_H +#define LLD_CORE_SHARED_LIBRARY_ATOM_H + +#include "lld/Core/Atom.h" + +namespace lld { + +/// A SharedLibraryAtom has no content. +/// It exists to represent a symbol which will be bound at runtime. +class SharedLibraryAtom : public Atom { +public: + enum class Type : uint32_t { + Unknown, + Code, + Data, + }; + + /// Returns shared library name used to load it at runtime. + /// On Darwin it is the LC_DYLIB_LOAD dylib name. + virtual StringRef loadName() const = 0; + + /// Returns if shared library symbol can be missing at runtime and if + /// so the loader should silently resolve address of symbol to be nullptr. + virtual bool canBeNullAtRuntime() const = 0; + + virtual Type type() const = 0; + + virtual uint64_t size() const = 0; + + static bool classof(const Atom *a) { + return a->definition() == definitionSharedLibrary; + } + + static inline bool classof(const SharedLibraryAtom *) { return true; } + +protected: + SharedLibraryAtom() : Atom(definitionSharedLibrary) {} + + ~SharedLibraryAtom() override = default; +}; + +} // namespace lld + +#endif // LLD_CORE_SHARED_LIBRARY_ATOM_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryAtom.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryFile.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryFile.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryFile.h (revision 352529) @@ -0,0 +1,69 @@ +//===- Core/SharedLibraryFile.h - Models shared libraries as Atoms --------===// +// +// 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_CORE_SHARED_LIBRARY_FILE_H +#define LLD_CORE_SHARED_LIBRARY_FILE_H + +#include "lld/Core/File.h" + +namespace lld { + +/// +/// The SharedLibraryFile subclass of File is used to represent dynamic +/// shared libraries being linked against. +/// +class SharedLibraryFile : public File { +public: + static bool classof(const File *f) { + return f->kind() == kindSharedLibrary; + } + + /// Check if the shared library exports a symbol with the specified name. + /// If so, return a SharedLibraryAtom which represents that exported + /// symbol. Otherwise return nullptr. + virtual OwningAtomPtr exports(StringRef name) const = 0; + + // Returns the install name. + virtual StringRef getDSOName() const = 0; + + const AtomRange defined() const override { + return _definedAtoms; + } + + const AtomRange undefined() const override { + return _undefinedAtoms; + } + + const AtomRange sharedLibrary() const override { + return _sharedLibraryAtoms; + } + + const AtomRange absolute() const override { + return _absoluteAtoms; + } + + void clearAtoms() override { + _definedAtoms.clear(); + _undefinedAtoms.clear(); + _sharedLibraryAtoms.clear(); + _absoluteAtoms.clear(); + } + +protected: + /// only subclasses of SharedLibraryFile can be instantiated + explicit SharedLibraryFile(StringRef path) : File(path, kindSharedLibrary) {} + + AtomVector _definedAtoms; + AtomVector _undefinedAtoms; + AtomVector _sharedLibraryAtoms; + AtomVector _absoluteAtoms; +}; + +} // namespace lld + +#endif // LLD_CORE_SHARED_LIBRARY_FILE_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/SharedLibraryFile.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Simple.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Simple.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Simple.h (revision 352529) @@ -0,0 +1,270 @@ +//===- lld/Core/Simple.h - Simple implementations of Atom and File --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Provide simple implementations for Atoms and File. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_SIMPLE_H +#define LLD_CORE_SIMPLE_H + +#include "lld/Core/AbsoluteAtom.h" +#include "lld/Core/Atom.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/Reference.h" +#include "lld/Core/SharedLibraryAtom.h" +#include "lld/Core/UndefinedAtom.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include +#include + +namespace lld { + +class SimpleFile : public File { +public: + SimpleFile(StringRef path, File::Kind kind) + : File(path, kind) {} + + ~SimpleFile() override { + _defined.clear(); + _undefined.clear(); + _shared.clear(); + _absolute.clear(); + } + + void addAtom(DefinedAtom &a) { + _defined.push_back(OwningAtomPtr(&a)); + } + void addAtom(UndefinedAtom &a) { + _undefined.push_back(OwningAtomPtr(&a)); + } + void addAtom(SharedLibraryAtom &a) { + _shared.push_back(OwningAtomPtr(&a)); + } + void addAtom(AbsoluteAtom &a) { + _absolute.push_back(OwningAtomPtr(&a)); + } + + void addAtom(const Atom &atom) { + if (auto *p = dyn_cast(&atom)) { + addAtom(const_cast(*p)); + } else if (auto *p = dyn_cast(&atom)) { + addAtom(const_cast(*p)); + } else if (auto *p = dyn_cast(&atom)) { + addAtom(const_cast(*p)); + } else if (auto *p = dyn_cast(&atom)) { + addAtom(const_cast(*p)); + } else { + llvm_unreachable("atom has unknown definition kind"); + } + } + + void removeDefinedAtomsIf(std::function pred) { + auto &atoms = _defined; + auto newEnd = std::remove_if(atoms.begin(), atoms.end(), + [&pred](OwningAtomPtr &p) { + return pred(p.get()); + }); + atoms.erase(newEnd, atoms.end()); + } + + const AtomRange defined() const override { return _defined; } + + const AtomRange undefined() const override { + return _undefined; + } + + const AtomRange sharedLibrary() const override { + return _shared; + } + + const AtomRange absolute() const override { + return _absolute; + } + + void clearAtoms() override { + _defined.clear(); + _undefined.clear(); + _shared.clear(); + _absolute.clear(); + } + +private: + AtomVector _defined; + AtomVector _undefined; + AtomVector _shared; + AtomVector _absolute; +}; + +class SimpleReference : public Reference, + public llvm::ilist_node { +public: + SimpleReference(Reference::KindNamespace ns, Reference::KindArch arch, + Reference::KindValue value, uint64_t off, const Atom *t, + Reference::Addend a) + : Reference(ns, arch, value), _target(t), _offsetInAtom(off), _addend(a) { + } + SimpleReference() + : Reference(Reference::KindNamespace::all, Reference::KindArch::all, 0), + _target(nullptr), _offsetInAtom(0), _addend(0) {} + + uint64_t offsetInAtom() const override { return _offsetInAtom; } + + const Atom *target() const override { + assert(_target); + return _target; + } + + Addend addend() const override { return _addend; } + void setAddend(Addend a) override { _addend = a; } + void setTarget(const Atom *newAtom) override { _target = newAtom; } + +private: + const Atom *_target; + uint64_t _offsetInAtom; + Addend _addend; +}; + +class SimpleDefinedAtom : public DefinedAtom { +public: + explicit SimpleDefinedAtom(const File &f) + : _file(f), _ordinal(f.getNextAtomOrdinalAndIncrement()) {} + + ~SimpleDefinedAtom() override { + _references.clearAndLeakNodesUnsafely(); + } + + const File &file() const override { return _file; } + + StringRef name() const override { return StringRef(); } + + uint64_t ordinal() const override { return _ordinal; } + + Scope scope() const override { return DefinedAtom::scopeLinkageUnit; } + + Interposable interposable() const override { + return DefinedAtom::interposeNo; + } + + Merge merge() const override { return DefinedAtom::mergeNo; } + + Alignment alignment() const override { return 1; } + + SectionChoice sectionChoice() const override { + return DefinedAtom::sectionBasedOnContent; + } + + StringRef customSectionName() const override { return StringRef(); } + DeadStripKind deadStrip() const override { + return DefinedAtom::deadStripNormal; + } + + DefinedAtom::reference_iterator begin() const override { + const void *it = + reinterpret_cast(_references.begin().getNodePtr()); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + const void *it = + reinterpret_cast(_references.end().getNodePtr()); + return reference_iterator(*this, it); + } + + const Reference *derefIterator(const void *it) const override { + return &*RefList::const_iterator( + *reinterpret_cast *>(it)); + } + + void incrementIterator(const void *&it) const override { + RefList::const_iterator ref( + *reinterpret_cast *>(it)); + it = reinterpret_cast(std::next(ref).getNodePtr()); + } + + void addReference(Reference::KindNamespace ns, + Reference::KindArch arch, + Reference::KindValue kindValue, uint64_t off, + const Atom *target, Reference::Addend a) override { + assert(target && "trying to create reference to nothing"); + auto node = new (_file.allocator()) + SimpleReference(ns, arch, kindValue, off, target, a); + _references.push_back(node); + } + + /// Sort references in a canonical order (by offset, then by kind). + void sortReferences() const { + // Cannot sort a linked list, so move elements into a temporary vector, + // sort the vector, then reconstruct the list. + llvm::SmallVector elements; + for (SimpleReference &node : _references) { + elements.push_back(&node); + } + std::sort(elements.begin(), elements.end(), + [] (const SimpleReference *lhs, const SimpleReference *rhs) -> bool { + uint64_t lhsOffset = lhs->offsetInAtom(); + uint64_t rhsOffset = rhs->offsetInAtom(); + if (rhsOffset != lhsOffset) + return (lhsOffset < rhsOffset); + if (rhs->kindNamespace() != lhs->kindNamespace()) + return (lhs->kindNamespace() < rhs->kindNamespace()); + if (rhs->kindArch() != lhs->kindArch()) + return (lhs->kindArch() < rhs->kindArch()); + return (lhs->kindValue() < rhs->kindValue()); + }); + _references.clearAndLeakNodesUnsafely(); + for (SimpleReference *node : elements) { + _references.push_back(node); + } + } + + void setOrdinal(uint64_t ord) { _ordinal = ord; } + +private: + typedef llvm::ilist RefList; + + const File &_file; + uint64_t _ordinal; + mutable RefList _references; +}; + +class SimpleUndefinedAtom : public UndefinedAtom { +public: + SimpleUndefinedAtom(const File &f, StringRef name) : _file(f), _name(name) { + assert(!name.empty() && "UndefinedAtoms must have a name"); + } + + ~SimpleUndefinedAtom() override = default; + + /// file - returns the File that produced/owns this Atom + const File &file() const override { return _file; } + + /// name - The name of the atom. For a function atom, it is the (mangled) + /// name of the function. + StringRef name() const override { return _name; } + + CanBeNull canBeNull() const override { return UndefinedAtom::canBeNullNever; } + +private: + const File &_file; + StringRef _name; +}; + +} // end namespace lld + +#endif // LLD_CORE_SIMPLE_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Simple.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/SymbolTable.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/SymbolTable.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/SymbolTable.h (revision 352529) @@ -0,0 +1,95 @@ +//===- Core/SymbolTable.h - Main Symbol Table -----------------------------===// +// +// 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_CORE_SYMBOL_TABLE_H +#define LLD_CORE_SYMBOL_TABLE_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/DJB.h" +#include +#include +#include + +namespace lld { + +class AbsoluteAtom; +class Atom; +class DefinedAtom; +class LinkingContext; +class ResolverOptions; +class SharedLibraryAtom; +class UndefinedAtom; + +/// The SymbolTable class is responsible for coalescing atoms. +/// +/// All atoms coalescable by-name or by-content should be added. +/// The method replacement() can be used to find the replacement atom +/// if an atom has been coalesced away. +class SymbolTable { +public: + /// add atom to symbol table + bool add(const DefinedAtom &); + + /// add atom to symbol table + bool add(const UndefinedAtom &); + + /// add atom to symbol table + bool add(const SharedLibraryAtom &); + + /// add atom to symbol table + bool add(const AbsoluteAtom &); + + /// returns atom in symbol table for specified name (or nullptr) + const Atom *findByName(StringRef sym); + + /// returns vector of remaining UndefinedAtoms + std::vector undefines(); + + /// if atom has been coalesced away, return replacement, else return atom + const Atom *replacement(const Atom *); + + /// if atom has been coalesced away, return true + bool isCoalescedAway(const Atom *); + +private: + typedef llvm::DenseMap AtomToAtom; + + struct StringRefMappingInfo { + static StringRef getEmptyKey() { return StringRef(); } + static StringRef getTombstoneKey() { return StringRef(" ", 1); } + static unsigned getHashValue(StringRef const val) { + return llvm::djbHash(val, 0); + } + static bool isEqual(StringRef const lhs, StringRef const rhs) { + return lhs.equals(rhs); + } + }; + typedef llvm::DenseMap NameToAtom; + + struct AtomMappingInfo { + static const DefinedAtom * getEmptyKey() { return nullptr; } + static const DefinedAtom * getTombstoneKey() { return (DefinedAtom*)(-1); } + static unsigned getHashValue(const DefinedAtom * const Val); + static bool isEqual(const DefinedAtom * const LHS, + const DefinedAtom * const RHS); + }; + typedef llvm::DenseSet AtomContentSet; + + bool addByName(const Atom &); + bool addByContent(const DefinedAtom &); + + AtomToAtom _replacedAtoms; + NameToAtom _nameTable; + AtomContentSet _contentTable; +}; + +} // namespace lld + +#endif // LLD_CORE_SYMBOL_TABLE_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/SymbolTable.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/UndefinedAtom.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/UndefinedAtom.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/UndefinedAtom.h (revision 352529) @@ -0,0 +1,67 @@ +//===- Core/UndefinedAtom.h - An Undefined Atom ---------------------------===// +// +// 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_CORE_UNDEFINED_ATOM_H +#define LLD_CORE_UNDEFINED_ATOM_H + +#include "lld/Core/Atom.h" + +namespace lld { + +/// An UndefinedAtom has no content. +/// It exists as a placeholder for a future atom. +class UndefinedAtom : public Atom { +public: + /// Whether this undefined symbol needs to be resolved, + /// or whether it can just evaluate to nullptr. + /// This concept is often called "weak", but that term + /// is overloaded to mean other things too. + enum CanBeNull { + /// Normal symbols must be resolved at build time + canBeNullNever, + + /// This symbol can be missing at runtime and will evalute to nullptr. + /// That is, the static linker still must find a definition (usually + /// is some shared library), but at runtime, the dynamic loader + /// will allow the symbol to be missing and resolved to nullptr. + /// + /// On Darwin this is generated using a function prototype with + /// __attribute__((weak_import)). + /// On linux this is generated using a function prototype with + /// __attribute__((weak)). + /// On Windows this feature is not supported. + canBeNullAtRuntime, + + /// This symbol can be missing at build time. + /// That is, the static linker will not error if a definition for + /// this symbol is not found at build time. Instead, the linker + /// will build an executable that lets the dynamic loader find the + /// symbol at runtime. + /// This feature is not supported on Darwin nor Windows. + /// On linux this is generated using a function prototype with + /// __attribute__((weak)). + canBeNullAtBuildtime + }; + + virtual CanBeNull canBeNull() const = 0; + + static bool classof(const Atom *a) { + return a->definition() == definitionUndefined; + } + + static bool classof(const UndefinedAtom *) { return true; } + +protected: + UndefinedAtom() : Atom(definitionUndefined) {} + + ~UndefinedAtom() override = default; +}; + +} // namespace lld + +#endif // LLD_CORE_UNDEFINED_ATOM_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/UndefinedAtom.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/Core/Writer.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/Core/Writer.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/Core/Writer.h (revision 352529) @@ -0,0 +1,46 @@ +//===- lld/Core/Writer.h - Abstract File Format Interface -----------------===// +// +// 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_CORE_WRITER_H +#define LLD_CORE_WRITER_H + +#include "lld/Common/LLVM.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace lld { +class File; +class LinkingContext; +class MachOLinkingContext; + +/// The Writer is an abstract class for writing object files, shared +/// library files, and executable files. Each file format (e.g. mach-o, etc) +/// has a concrete subclass of Writer. +class Writer { +public: + virtual ~Writer(); + + /// Write a file from the supplied File object + virtual llvm::Error writeFile(const File &linkedFile, StringRef path) = 0; + + /// This method is called by Core Linking to give the Writer a chance + /// to add file format specific "files" to set of files to be linked. This is + /// how file format specific atoms can be added to the link. + virtual void createImplicitFiles(std::vector> &) {} + +protected: + // only concrete subclasses can be instantiated + Writer(); +}; + +std::unique_ptr createWriterMachO(const MachOLinkingContext &); +std::unique_ptr createWriterYAML(const LinkingContext &); +} // end namespace lld + +#endif Property changes on: vendor/lld/lld-release_900-r372316/include/lld/Core/Writer.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/MachOLinkingContext.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/MachOLinkingContext.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/MachOLinkingContext.h (revision 352529) @@ -0,0 +1,507 @@ +//===- lld/ReaderWriter/MachOLinkingContext.h -----------------------------===// +// +// 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_READER_WRITER_MACHO_LINKING_CONTEXT_H +#define LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H + +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/ErrorHandling.h" +#include + +using llvm::MachO::HeaderFileType; + +namespace lld { + +namespace mach_o { +class ArchHandler; +class MachODylibFile; +class MachOFile; +class SectCreateFile; +} + +class MachOLinkingContext : public LinkingContext { +public: + MachOLinkingContext(); + ~MachOLinkingContext() override; + + enum Arch { + arch_unknown, + arch_ppc, + arch_x86, + arch_x86_64, + arch_armv6, + arch_armv7, + arch_armv7s, + arch_arm64, + }; + + enum class OS { + unknown, + macOSX, + iOS, + iOS_simulator + }; + + enum class ExportMode { + globals, // Default, all global symbols exported. + whiteList, // -exported_symbol[s_list], only listed symbols exported. + blackList // -unexported_symbol[s_list], no listed symbol exported. + }; + + enum class DebugInfoMode { + addDebugMap, // Default + noDebugMap // -S option + }; + + enum class UndefinedMode { + error, + warning, + suppress, + dynamicLookup + }; + + enum ObjCConstraint { + objc_unknown = 0, + objc_supports_gc = 2, + objc_gc_only = 4, + // Image optimized by dyld = 8 + // GC compaction = 16 + objc_retainReleaseForSimulator = 32, + objc_retainRelease + }; + + /// Initializes the context to sane default values given the specified output + /// file type, arch, os, and minimum os version. This should be called before + /// other setXXX() methods. + void configure(HeaderFileType type, Arch arch, OS os, uint32_t minOSVersion, + bool exportDynamicSymbols); + + void addPasses(PassManager &pm) override; + bool validateImpl() override; + std::string demangle(StringRef symbolName) const override; + + void createImplicitFiles(std::vector> &) override; + + /// Creates a new file which is owned by the context. Returns a pointer to + /// the new file. + template + typename std::enable_if::value, T *>::type + make_file(Args &&... args) const { + auto file = std::unique_ptr(new T(std::forward(args)...)); + auto *filePtr = file.get(); + auto *ctx = const_cast(this); + ctx->getNodes().push_back(llvm::make_unique(std::move(file))); + return filePtr; + } + + uint32_t getCPUType() const; + uint32_t getCPUSubType() const; + + bool addEntryPointLoadCommand() const; + bool addUnixThreadLoadCommand() const; + bool outputTypeHasEntry() const; + bool is64Bit() const; + + virtual uint64_t pageZeroSize() const { return _pageZeroSize; } + virtual uint64_t pageSize() const { return _pageSize; } + + mach_o::ArchHandler &archHandler() const; + + HeaderFileType outputMachOType() const { return _outputMachOType; } + + Arch arch() const { return _arch; } + StringRef archName() const { return nameFromArch(_arch); } + OS os() const { return _os; } + + ExportMode exportMode() const { return _exportMode; } + void setExportMode(ExportMode mode) { _exportMode = mode; } + void addExportSymbol(StringRef sym); + bool exportRestrictMode() const { return _exportMode != ExportMode::globals; } + bool exportSymbolNamed(StringRef sym) const; + + DebugInfoMode debugInfoMode() const { return _debugInfoMode; } + void setDebugInfoMode(DebugInfoMode mode) { + _debugInfoMode = mode; + } + + void appendOrderedSymbol(StringRef symbol, StringRef filename); + + bool keepPrivateExterns() const { return _keepPrivateExterns; } + void setKeepPrivateExterns(bool v) { _keepPrivateExterns = v; } + bool demangleSymbols() const { return _demangle; } + void setDemangleSymbols(bool d) { _demangle = d; } + bool mergeObjCCategories() const { return _mergeObjCCategories; } + void setMergeObjCCategories(bool v) { _mergeObjCCategories = v; } + /// Create file at specified path which will contain a binary encoding + /// of all input and output file paths. + std::error_code createDependencyFile(StringRef path); + void addInputFileDependency(StringRef path) const; + void addInputFileNotFound(StringRef path) const; + void addOutputFileDependency(StringRef path) const; + + bool minOS(StringRef mac, StringRef iOS) const; + void setDoNothing(bool value) { _doNothing = value; } + bool doNothing() const { return _doNothing; } + bool printAtoms() const { return _printAtoms; } + bool testingFileUsage() const { return _testingFileUsage; } + const StringRefVector &searchDirs() const { return _searchDirs; } + const StringRefVector &frameworkDirs() const { return _frameworkDirs; } + void setSysLibRoots(const StringRefVector &paths); + const StringRefVector &sysLibRoots() const { return _syslibRoots; } + bool PIE() const { return _pie; } + void setPIE(bool pie) { _pie = pie; } + bool generateVersionLoadCommand() const { + return _generateVersionLoadCommand; + } + void setGenerateVersionLoadCommand(bool v) { + _generateVersionLoadCommand = v; + } + + bool generateFunctionStartsLoadCommand() const { + return _generateFunctionStartsLoadCommand; + } + void setGenerateFunctionStartsLoadCommand(bool v) { + _generateFunctionStartsLoadCommand = v; + } + + bool generateDataInCodeLoadCommand() const { + return _generateDataInCodeLoadCommand; + } + void setGenerateDataInCodeLoadCommand(bool v) { + _generateDataInCodeLoadCommand = v; + } + + uint64_t stackSize() const { return _stackSize; } + void setStackSize(uint64_t stackSize) { _stackSize = stackSize; } + + uint64_t baseAddress() const { return _baseAddress; } + void setBaseAddress(uint64_t baseAddress) { _baseAddress = baseAddress; } + + ObjCConstraint objcConstraint() const { return _objcConstraint; } + + uint32_t osMinVersion() const { return _osMinVersion; } + + uint32_t sdkVersion() const { return _sdkVersion; } + void setSdkVersion(uint64_t v) { _sdkVersion = v; } + + uint64_t sourceVersion() const { return _sourceVersion; } + void setSourceVersion(uint64_t v) { _sourceVersion = v; } + + uint32_t swiftVersion() const { return _swiftVersion; } + + /// Checks whether a given path on the filesystem exists. + /// + /// When running in -test_file_usage mode, this method consults an + /// internally maintained list of files that exist (provided by -path_exists) + /// instead of the actual filesystem. + bool pathExists(StringRef path) const; + + /// Like pathExists() but only used on files - not directories. + bool fileExists(StringRef path) const; + + /// Adds any library search paths derived from the given base, possibly + /// modified by -syslibroots. + /// + /// The set of paths added consists of approximately all syslibroot-prepended + /// versions of libPath that exist, or the original libPath if there are none + /// for whatever reason. With various edge-cases for compatibility. + void addModifiedSearchDir(StringRef libPath, bool isSystemPath = false); + + /// Determine whether -lFoo can be resolve within the given path, and + /// return the filename if so. + /// + /// The -lFoo option is documented to search for libFoo.dylib and libFoo.a in + /// that order, unless Foo ends in ".o", in which case only the exact file + /// matches (e.g. -lfoo.o would only find foo.o). + llvm::Optional searchDirForLibrary(StringRef path, + StringRef libName) const; + + /// Iterates through all search path entries looking for libName (as + /// specified by -lFoo). + llvm::Optional searchLibrary(StringRef libName) const; + + /// Add a framework search path. Internally, this method may be prepended + /// the path with syslibroot. + void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false); + + /// Iterates through all framework directories looking for + /// Foo.framework/Foo (when fwName = "Foo"). + llvm::Optional findPathForFramework(StringRef fwName) const; + + /// The dylib's binary compatibility version, in the raw uint32 format. + /// + /// When building a dynamic library, this is the compatibility version that + /// gets embedded into the result. Other Mach-O binaries that link against + /// this library will store the compatibility version in its load command. At + /// runtime, the loader will verify that the binary is compatible with the + /// installed dynamic library. + uint32_t compatibilityVersion() const { return _compatibilityVersion; } + + /// The dylib's current version, in the the raw uint32 format. + /// + /// When building a dynamic library, this is the current version that gets + /// embedded into the result. Other Mach-O binaries that link against + /// this library will store the compatibility version in its load command. + uint32_t currentVersion() const { return _currentVersion; } + + /// The dylib's install name. + /// + /// Binaries that link against the dylib will embed this path into the dylib + /// load command. When loading the binaries at runtime, this is the location + /// on disk that the loader will look for the dylib. + StringRef installName() const { return _installName; } + + /// Whether or not the dylib has side effects during initialization. + /// + /// Dylibs marked as being dead strippable provide the guarantee that loading + /// the dylib has no side effects, allowing the linker to strip out the dylib + /// when linking a binary that does not use any of its symbols. + bool deadStrippableDylib() const { return _deadStrippableDylib; } + + /// Whether or not to use flat namespace. + /// + /// MachO usually uses a two-level namespace, where each external symbol + /// referenced by the target is associated with the dylib that will provide + /// the symbol's definition at runtime. Using flat namespace overrides this + /// behavior: the linker searches all dylibs on the command line and all + /// dylibs those original dylibs depend on, but does not record which dylib + /// an external symbol came from. At runtime dyld again searches all images + /// and uses the first definition it finds. In addition, any undefines in + /// loaded flat_namespace dylibs must be resolvable at build time. + bool useFlatNamespace() const { return _flatNamespace; } + + /// How to handle undefined symbols. + /// + /// Options are: + /// * error: Report an error and terminate linking. + /// * warning: Report a warning, but continue linking. + /// * suppress: Ignore and continue linking. + /// * dynamic_lookup: For use with -twolevel namespace: Records source dylibs + /// for symbols that are defined in a linked dylib at static link time. + /// Undefined symbols are handled by searching all loaded images at + /// runtime. + UndefinedMode undefinedMode() const { return _undefinedMode; } + + /// The path to the executable that will load the bundle at runtime. + /// + /// When building a Mach-O bundle, this executable will be examined if there + /// are undefined symbols after the main link phase. It is expected that this + /// binary will be loading the bundle at runtime and will provide the symbols + /// at that point. + StringRef bundleLoader() const { return _bundleLoader; } + + void setCompatibilityVersion(uint32_t vers) { _compatibilityVersion = vers; } + void setCurrentVersion(uint32_t vers) { _currentVersion = vers; } + void setInstallName(StringRef name) { _installName = name; } + void setDeadStrippableDylib(bool deadStrippable) { + _deadStrippableDylib = deadStrippable; + } + void setUseFlatNamespace(bool flatNamespace) { + _flatNamespace = flatNamespace; + } + + void setUndefinedMode(UndefinedMode undefinedMode) { + _undefinedMode = undefinedMode; + } + + void setBundleLoader(StringRef loader) { _bundleLoader = loader; } + void setPrintAtoms(bool value=true) { _printAtoms = value; } + void setTestingFileUsage(bool value = true) { + _testingFileUsage = value; + } + void addExistingPathForDebug(StringRef path) { + _existingPaths.insert(path); + } + + void addRpath(StringRef rpath); + const StringRefVector &rpaths() const { return _rpaths; } + + /// Add section alignment constraint on final layout. + void addSectionAlignment(StringRef seg, StringRef sect, uint16_t align); + + /// Add a section based on a command-line sectcreate option. + void addSectCreateSection(StringRef seg, StringRef sect, + std::unique_ptr content); + + /// Returns true if specified section had alignment constraints. + bool sectionAligned(StringRef seg, StringRef sect, uint16_t &align) const; + + StringRef dyldPath() const { return "/usr/lib/dyld"; } + + /// Stub creation Pass should be run. + bool needsStubsPass() const; + + // GOT creation Pass should be run. + bool needsGOTPass() const; + + /// Pass to add TLV sections. + bool needsTLVPass() const; + + /// Pass to transform __compact_unwind into __unwind_info should be run. + bool needsCompactUnwindPass() const; + + /// Pass to add shims switching between thumb and arm mode. + bool needsShimPass() const; + + /// Pass to add objc image info and optimized objc data. + bool needsObjCPass() const; + + /// Magic symbol name stubs will need to help lazy bind. + StringRef binderSymbolName() const; + + /// Used to keep track of direct and indirect dylibs. + void registerDylib(mach_o::MachODylibFile *dylib, bool upward) const; + + // Reads a file from disk to memory. Returns only a needed chunk + // if a fat binary. + ErrorOr> getMemoryBuffer(StringRef path); + + /// Used to find indirect dylibs. Instantiates a MachODylibFile if one + /// has not already been made for the requested dylib. Uses -L and -F + /// search paths to allow indirect dylibs to be overridden. + mach_o::MachODylibFile* findIndirectDylib(StringRef path); + + uint32_t dylibCurrentVersion(StringRef installName) const; + + uint32_t dylibCompatVersion(StringRef installName) const; + + ArrayRef allDylibs() const { + return _allDylibs; + } + + /// Creates a copy (owned by this MachOLinkingContext) of a string. + StringRef copy(StringRef str) { return str.copy(_allocator); } + + /// If the memoryBuffer is a fat file with a slice for the current arch, + /// this method will return the offset and size of that slice. + bool sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, uint32_t &size); + + /// Returns if a command line option specified dylib is an upward link. + bool isUpwardDylib(StringRef installName) const; + + static bool isThinObjectFile(StringRef path, Arch &arch); + static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype); + static Arch archFromName(StringRef archName); + static StringRef nameFromArch(Arch arch); + static uint32_t cpuTypeFromArch(Arch arch); + static uint32_t cpuSubtypeFromArch(Arch arch); + static bool is64Bit(Arch arch); + static bool isHostEndian(Arch arch); + static bool isBigEndian(Arch arch); + + /// Construct 32-bit value from string "X.Y.Z" where + /// bits are xxxx.yy.zz. Largest number is 65535.255.255 + static bool parsePackedVersion(StringRef str, uint32_t &result); + + /// Construct 64-bit value from string "A.B.C.D.E" where + /// bits are aaaa.bb.cc.dd.ee. Largest number is 16777215.1023.1023.1023.1023 + static bool parsePackedVersion(StringRef str, uint64_t &result); + + void finalizeInputFiles() override; + + llvm::Error handleLoadedFile(File &file) override; + + bool customAtomOrderer(const DefinedAtom *left, const DefinedAtom *right, + bool &leftBeforeRight) const; + + /// Return the 'flat namespace' file. This is the file that supplies + /// atoms for otherwise undefined symbols when the -flat_namespace or + /// -undefined dynamic_lookup options are used. + File* flatNamespaceFile() const { return _flatNamespaceFile; } + +private: + Writer &writer() const override; + mach_o::MachODylibFile* loadIndirectDylib(StringRef path); + void checkExportWhiteList(const DefinedAtom *atom) const; + void checkExportBlackList(const DefinedAtom *atom) const; + struct ArchInfo { + StringRef archName; + MachOLinkingContext::Arch arch; + bool littleEndian; + uint32_t cputype; + uint32_t cpusubtype; + }; + + struct SectionAlign { + StringRef segmentName; + StringRef sectionName; + uint16_t align; + }; + + struct OrderFileNode { + StringRef fileFilter; + unsigned order; + }; + + static bool findOrderOrdinal(const std::vector &nodes, + const DefinedAtom *atom, unsigned &ordinal); + + static ArchInfo _s_archInfos[]; + + std::set _existingPaths; // For testing only. + StringRefVector _searchDirs; + StringRefVector _syslibRoots; + StringRefVector _frameworkDirs; + HeaderFileType _outputMachOType = llvm::MachO::MH_EXECUTE; + bool _outputMachOTypeStatic = false; // Disambiguate static vs dynamic prog + bool _doNothing = false; // for -help and -v which just print info + bool _pie = false; + Arch _arch = arch_unknown; + OS _os = OS::macOSX; + uint32_t _osMinVersion = 0; + uint32_t _sdkVersion = 0; + uint64_t _sourceVersion = 0; + uint64_t _pageZeroSize = 0; + uint64_t _pageSize = 4096; + uint64_t _baseAddress = 0; + uint64_t _stackSize = 0; + uint32_t _compatibilityVersion = 0; + uint32_t _currentVersion = 0; + ObjCConstraint _objcConstraint = objc_unknown; + uint32_t _swiftVersion = 0; + StringRef _installName; + StringRefVector _rpaths; + bool _flatNamespace = false; + UndefinedMode _undefinedMode = UndefinedMode::error; + bool _deadStrippableDylib = false; + bool _printAtoms = false; + bool _testingFileUsage = false; + bool _keepPrivateExterns = false; + bool _demangle = false; + bool _mergeObjCCategories = true; + bool _generateVersionLoadCommand = false; + bool _generateFunctionStartsLoadCommand = false; + bool _generateDataInCodeLoadCommand = false; + StringRef _bundleLoader; + mutable std::unique_ptr _archHandler; + mutable std::unique_ptr _writer; + std::vector _sectAligns; + mutable llvm::StringMap _pathToDylibMap; + mutable std::vector _allDylibs; + mutable std::set _upwardDylibs; + mutable std::vector> _indirectDylibs; + mutable std::mutex _dylibsMutex; + ExportMode _exportMode = ExportMode::globals; + llvm::StringSet<> _exportedSymbols; + DebugInfoMode _debugInfoMode = DebugInfoMode::addDebugMap; + std::unique_ptr _dependencyInfo; + llvm::StringMap> _orderFiles; + unsigned _orderFileEntries = 0; + File *_flatNamespaceFile = nullptr; + mach_o::SectCreateFile *_sectCreateFile = nullptr; +}; + +} // end namespace lld + +#endif // LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/MachOLinkingContext.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/YamlContext.h =================================================================== --- vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/YamlContext.h (nonexistent) +++ vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/YamlContext.h (revision 352529) @@ -0,0 +1,42 @@ +//===- lld/ReaderWriter/YamlContext.h - object used in YAML I/O context ---===// +// +// 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_READER_WRITER_YAML_CONTEXT_H +#define LLD_READER_WRITER_YAML_CONTEXT_H + +#include "lld/Common/LLVM.h" +#include +#include +#include + +namespace lld { +class File; +class LinkingContext; +class Registry; +namespace mach_o { +namespace normalized { +struct NormalizedFile; +} +} + +using lld::mach_o::normalized::NormalizedFile; + +/// When YAML I/O is used in lld, the yaml context always holds a YamlContext +/// object. We need to support hetergenous yaml documents which each require +/// different context info. This struct supports all clients. +struct YamlContext { + const LinkingContext *_ctx = nullptr; + const Registry *_registry = nullptr; + File *_file = nullptr; + NormalizedFile *_normalizeMachOFile = nullptr; + StringRef _path; +}; + +} // end namespace lld + +#endif // LLD_READER_WRITER_YAML_CONTEXT_H Property changes on: vendor/lld/lld-release_900-r372316/include/lld/ReaderWriter/YamlContext.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/DefinedAtom.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/DefinedAtom.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/DefinedAtom.cpp (revision 352529) @@ -0,0 +1,81 @@ +//===- DefinedAtom.cpp ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ErrorHandling.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" + +namespace lld { + +DefinedAtom::ContentPermissions DefinedAtom::permissions() const { + // By default base permissions on content type. + return permissions(this->contentType()); +} + +// Utility function for deriving permissions from content type +DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) { + switch (type) { + case typeCode: + case typeResolver: + case typeBranchIsland: + case typeBranchShim: + case typeStub: + case typeStubHelper: + case typeMachHeader: + return permR_X; + + case typeConstant: + case typeCString: + case typeUTF16String: + case typeCFI: + case typeLSDA: + case typeLiteral4: + case typeLiteral8: + case typeLiteral16: + case typeDTraceDOF: + case typeCompactUnwindInfo: + case typeProcessedUnwindInfo: + case typeObjCImageInfo: + case typeObjCMethodList: + return permR__; + + case typeData: + case typeDataFast: + case typeZeroFill: + case typeZeroFillFast: + case typeObjC1Class: + case typeLazyPointer: + case typeLazyDylibPointer: + case typeNonLazyPointer: + case typeThunkTLV: + return permRW_; + + case typeGOT: + case typeConstData: + case typeCFString: + case typeInitializerPtr: + case typeTerminatorPtr: + case typeCStringPtr: + case typeObjCClassPtr: + case typeObjC2CategoryList: + case typeInterposingTuples: + case typeTLVInitialData: + case typeTLVInitialZeroFill: + case typeTLVInitializerPtr: + return permRW_L; + + case typeUnknown: + case typeTempLTO: + case typeSectCreate: + case typeDSOHandle: + return permUnknown; + } + llvm_unreachable("unknown content type"); +} + +} // namespace Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/DefinedAtom.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/Error.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/Error.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/Error.cpp (revision 352529) @@ -0,0 +1,92 @@ +//===- Error.cpp - system_error extensions for lld --------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Error.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include + +using namespace lld; + +namespace { +class _YamlReaderErrorCategory : public std::error_category { +public: + const char* name() const noexcept override { + return "lld.yaml.reader"; + } + + std::string message(int ev) const override { + switch (static_cast(ev)) { + case YamlReaderError::unknown_keyword: + return "Unknown keyword found in yaml file"; + case YamlReaderError::illegal_value: + return "Bad value found in yaml file"; + } + llvm_unreachable("An enumerator of YamlReaderError does not have a " + "message defined."); + } +}; +} // end anonymous namespace + +const std::error_category &lld::YamlReaderCategory() { + static _YamlReaderErrorCategory o; + return o; +} + +namespace lld { + +/// Temporary class to enable make_dynamic_error_code() until +/// llvm::ErrorOr<> is updated to work with error encapsulations +/// other than error_code. +class dynamic_error_category : public std::error_category { +public: + ~dynamic_error_category() override = default; + + const char *name() const noexcept override { + return "lld.dynamic_error"; + } + + std::string message(int ev) const override { + assert(ev >= 0); + assert(ev < (int)_messages.size()); + // The value is an index into the string vector. + return _messages[ev]; + } + + int add(std::string msg) { + std::lock_guard lock(_mutex); + // Value zero is always the successs value. + if (_messages.empty()) + _messages.push_back("Success"); + _messages.push_back(msg); + // Return the index of the string just appended. + return _messages.size() - 1; + } + +private: + std::vector _messages; + std::recursive_mutex _mutex; +}; + +static dynamic_error_category categorySingleton; + +std::error_code make_dynamic_error_code(StringRef msg) { + return std::error_code(categorySingleton.add(msg), categorySingleton); +} + +char GenericError::ID = 0; + +GenericError::GenericError(Twine Msg) : Msg(Msg.str()) { } + +void GenericError::log(raw_ostream &OS) const { + OS << Msg; +} + +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/Error.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/File.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/File.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/File.cpp (revision 352529) @@ -0,0 +1,28 @@ +//===- Core/File.cpp - A Container of Atoms -------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/File.h" +#include + +namespace lld { + +File::~File() = default; + +File::AtomVector File::_noDefinedAtoms; +File::AtomVector File::_noUndefinedAtoms; +File::AtomVector File::_noSharedLibraryAtoms; +File::AtomVector File::_noAbsoluteAtoms; + +std::error_code File::parse() { + std::lock_guard lock(_parseMutex); + if (!_lastError.hasValue()) + _lastError = doParse(); + return _lastError.getValue(); +} + +} // end namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/File.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/LinkingContext.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/LinkingContext.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/LinkingContext.cpp (revision 352529) @@ -0,0 +1,69 @@ +//===- lib/Core/LinkingContext.cpp - Linker Context Object Interface ------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/LinkingContext.h" +#include "lld/Core/File.h" +#include "lld/Core/Node.h" +#include "lld/Core/Simple.h" +#include "lld/Core/Writer.h" +#include + +namespace lld { + +LinkingContext::LinkingContext() = default; + +LinkingContext::~LinkingContext() = default; + +bool LinkingContext::validate() { + return validateImpl(); +} + +llvm::Error LinkingContext::writeFile(const File &linkedFile) const { + return this->writer().writeFile(linkedFile, _outputPath); +} + +std::unique_ptr LinkingContext::createEntrySymbolFile() const { + return createEntrySymbolFile(""); +} + +std::unique_ptr +LinkingContext::createEntrySymbolFile(StringRef filename) const { + if (entrySymbolName().empty()) + return nullptr; + std::unique_ptr entryFile(new SimpleFile(filename, + File::kindEntryObject)); + entryFile->addAtom( + *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName()))); + return std::move(entryFile); +} + +std::unique_ptr LinkingContext::createUndefinedSymbolFile() const { + return createUndefinedSymbolFile(""); +} + +std::unique_ptr +LinkingContext::createUndefinedSymbolFile(StringRef filename) const { + if (_initialUndefinedSymbols.empty()) + return nullptr; + std::unique_ptr undefinedSymFile( + new SimpleFile(filename, File::kindUndefinedSymsObject)); + for (StringRef undefSym : _initialUndefinedSymbols) + undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom( + *undefinedSymFile, undefSym))); + return std::move(undefinedSymFile); +} + +void LinkingContext::createInternalFiles( + std::vector> &result) const { + if (std::unique_ptr file = createEntrySymbolFile()) + result.push_back(std::move(file)); + if (std::unique_ptr file = createUndefinedSymbolFile()) + result.push_back(std::move(file)); +} + +} // end namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/LinkingContext.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/Reader.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/Reader.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/Reader.cpp (revision 352529) @@ -0,0 +1,113 @@ +//===- lib/Core/Reader.cpp ------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Reader.h" +#include "lld/Core/File.h" +#include "lld/Core/Reference.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include +#include + +using llvm::file_magic; +using llvm::identify_magic; + +namespace lld { + +YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() = default; + +void Registry::add(std::unique_ptr reader) { + _readers.push_back(std::move(reader)); +} + +void Registry::add(std::unique_ptr handler) { + _yamlHandlers.push_back(std::move(handler)); +} + +ErrorOr> +Registry::loadFile(std::unique_ptr mb) const { + // Get file magic. + StringRef content(mb->getBufferStart(), mb->getBufferSize()); + file_magic fileType = identify_magic(content); + + // Ask each registered reader if it can handle this file type or extension. + for (const std::unique_ptr &reader : _readers) { + if (!reader->canParse(fileType, mb->getMemBufferRef())) + continue; + return reader->loadFile(std::move(mb), *this); + } + + // No Reader could parse this file. + return make_error_code(llvm::errc::executable_format_error); +} + +static const Registry::KindStrings kindStrings[] = { + {Reference::kindLayoutAfter, "layout-after"}, + {Reference::kindAssociate, "associate"}, + LLD_KIND_STRING_END}; + +Registry::Registry() { + addKindTable(Reference::KindNamespace::all, Reference::KindArch::all, + kindStrings); +} + +bool Registry::handleTaggedDoc(llvm::yaml::IO &io, + const lld::File *&file) const { + for (const std::unique_ptr &h : _yamlHandlers) + if (h->handledDocTag(io, file)) + return true; + return false; +} + +void Registry::addKindTable(Reference::KindNamespace ns, + Reference::KindArch arch, + const KindStrings array[]) { + KindEntry entry = { ns, arch, array }; + _kindEntries.push_back(entry); +} + +bool Registry::referenceKindFromString(StringRef inputStr, + Reference::KindNamespace &ns, + Reference::KindArch &arch, + Reference::KindValue &value) const { + for (const KindEntry &entry : _kindEntries) { + for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) { + if (!inputStr.equals(pair->name)) + continue; + ns = entry.ns; + arch = entry.arch; + value = pair->value; + return true; + } + } + return false; +} + +bool Registry::referenceKindToString(Reference::KindNamespace ns, + Reference::KindArch arch, + Reference::KindValue value, + StringRef &str) const { + for (const KindEntry &entry : _kindEntries) { + if (entry.ns != ns) + continue; + if (entry.arch != arch) + continue; + for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) { + if (pair->value != value) + continue; + str = pair->name; + return true; + } + } + return false; +} + +} // end namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/Reader.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/Resolver.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/Resolver.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/Resolver.cpp (revision 352529) @@ -0,0 +1,504 @@ +//===- Core/Resolver.cpp - Resolves Atom References -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Resolver.h" +#include "lld/Common/LLVM.h" +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/Atom.h" +#include "lld/Core/File.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Core/SymbolTable.h" +#include "lld/Core/UndefinedAtom.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +namespace lld { + +llvm::Expected Resolver::handleFile(File &file) { + if (auto ec = _ctx.handleLoadedFile(file)) + return std::move(ec); + bool undefAdded = false; + for (auto &atom : file.defined().owning_ptrs()) + doDefinedAtom(std::move(atom)); + for (auto &atom : file.undefined().owning_ptrs()) { + if (doUndefinedAtom(std::move(atom))) + undefAdded = true; + } + for (auto &atom : file.sharedLibrary().owning_ptrs()) + doSharedLibraryAtom(std::move(atom)); + for (auto &atom : file.absolute().owning_ptrs()) + doAbsoluteAtom(std::move(atom)); + return undefAdded; +} + +llvm::Expected Resolver::forEachUndefines(File &file, + UndefCallback callback) { + size_t i = _undefineIndex[&file]; + bool undefAdded = false; + do { + for (; i < _undefines.size(); ++i) { + StringRef undefName = _undefines[i]; + if (undefName.empty()) + continue; + const Atom *atom = _symbolTable.findByName(undefName); + if (!isa(atom) || _symbolTable.isCoalescedAway(atom)) { + // The symbol was resolved by some other file. Cache the result. + _undefines[i] = ""; + continue; + } + auto undefAddedOrError = callback(undefName); + if (auto ec = undefAddedOrError.takeError()) + return std::move(ec); + undefAdded |= undefAddedOrError.get(); + } + } while (i < _undefines.size()); + _undefineIndex[&file] = i; + return undefAdded; +} + +llvm::Expected Resolver::handleArchiveFile(File &file) { + ArchiveLibraryFile *archiveFile = cast(&file); + return forEachUndefines(file, + [&](StringRef undefName) -> llvm::Expected { + if (File *member = archiveFile->find(undefName)) { + member->setOrdinal(_ctx.getNextOrdinalAndIncrement()); + return handleFile(*member); + } + return false; + }); +} + +llvm::Error Resolver::handleSharedLibrary(File &file) { + // Add all the atoms from the shared library + SharedLibraryFile *sharedLibrary = cast(&file); + auto undefAddedOrError = handleFile(*sharedLibrary); + if (auto ec = undefAddedOrError.takeError()) + return ec; + undefAddedOrError = + forEachUndefines(file, [&](StringRef undefName) -> llvm::Expected { + auto atom = sharedLibrary->exports(undefName); + if (atom.get()) + doSharedLibraryAtom(std::move(atom)); + return false; + }); + + if (auto ec = undefAddedOrError.takeError()) + return ec; + return llvm::Error::success(); +} + +bool Resolver::doUndefinedAtom(OwningAtomPtr atom) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << " UndefinedAtom: " + << llvm::format("0x%09lX", atom.get()) + << ", name=" << atom.get()->name() << "\n"); + + // tell symbol table + bool newUndefAdded = _symbolTable.add(*atom.get()); + if (newUndefAdded) + _undefines.push_back(atom.get()->name()); + + // add to list of known atoms + _atoms.push_back(OwningAtomPtr(atom.release())); + + return newUndefAdded; +} + +// Called on each atom when a file is added. Returns true if a given +// atom is added to the symbol table. +void Resolver::doDefinedAtom(OwningAtomPtr atom) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << " DefinedAtom: " + << llvm::format("0x%09lX", atom.get()) + << ", file=#" + << atom.get()->file().ordinal() + << ", atom=#" + << atom.get()->ordinal() + << ", name=" + << atom.get()->name() + << ", type=" + << atom.get()->contentType() + << "\n"); + + // An atom that should never be dead-stripped is a dead-strip root. + if (_ctx.deadStrip() && + atom.get()->deadStrip() == DefinedAtom::deadStripNever) { + _deadStripRoots.insert(atom.get()); + } + + // add to list of known atoms + _symbolTable.add(*atom.get()); + _atoms.push_back(OwningAtomPtr(atom.release())); +} + +void Resolver::doSharedLibraryAtom(OwningAtomPtr atom) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << " SharedLibraryAtom: " + << llvm::format("0x%09lX", atom.get()) + << ", name=" + << atom.get()->name() + << "\n"); + + // tell symbol table + _symbolTable.add(*atom.get()); + + // add to list of known atoms + _atoms.push_back(OwningAtomPtr(atom.release())); +} + +void Resolver::doAbsoluteAtom(OwningAtomPtr atom) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << " AbsoluteAtom: " + << llvm::format("0x%09lX", atom.get()) + << ", name=" + << atom.get()->name() + << "\n"); + + // tell symbol table + if (atom.get()->scope() != Atom::scopeTranslationUnit) + _symbolTable.add(*atom.get()); + + // add to list of known atoms + _atoms.push_back(OwningAtomPtr(atom.release())); +} + +// Returns true if at least one of N previous files has created an +// undefined symbol. +bool Resolver::undefinesAdded(int begin, int end) { + std::vector> &inputs = _ctx.getNodes(); + for (int i = begin; i < end; ++i) + if (FileNode *node = dyn_cast(inputs[i].get())) + if (_newUndefinesAdded[node->getFile()]) + return true; + return false; +} + +File *Resolver::getFile(int &index) { + std::vector> &inputs = _ctx.getNodes(); + if ((size_t)index >= inputs.size()) + return nullptr; + if (GroupEnd *group = dyn_cast(inputs[index].get())) { + // We are at the end of the current group. If one or more new + // undefined atom has been added in the last groupSize files, we + // reiterate over the files. + int size = group->getSize(); + if (undefinesAdded(index - size, index)) { + index -= size; + return getFile(index); + } + ++index; + return getFile(index); + } + return cast(inputs[index++].get())->getFile(); +} + +// Keep adding atoms until _ctx.getNextFile() returns an error. This +// function is where undefined atoms are resolved. +bool Resolver::resolveUndefines() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Resolving undefines:\n"); + ScopedTask task(getDefaultDomain(), "resolveUndefines"); + int index = 0; + std::set seen; + for (;;) { + bool undefAdded = false; + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "Loading file #" << index << "\n"); + File *file = getFile(index); + if (!file) + return true; + if (std::error_code ec = file->parse()) { + llvm::errs() << "Cannot open " + file->path() + << ": " << ec.message() << "\n"; + return false; + } + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "Loaded file: " << file->path() << "\n"); + switch (file->kind()) { + case File::kindErrorObject: + case File::kindNormalizedObject: + case File::kindMachObject: + case File::kindCEntryObject: + case File::kindHeaderObject: + case File::kindEntryObject: + case File::kindUndefinedSymsObject: + case File::kindStubHelperObject: + case File::kindResolverMergedObject: + case File::kindSectCreateObject: { + // The same file may be visited more than once if the file is + // in --start-group and --end-group. Only library files should + // be processed more than once. + if (seen.count(file)) + break; + seen.insert(file); + assert(!file->hasOrdinal()); + file->setOrdinal(_ctx.getNextOrdinalAndIncrement()); + auto undefAddedOrError = handleFile(*file); + if (auto EC = undefAddedOrError.takeError()) { + // FIXME: This should be passed to logAllUnhandledErrors but it needs + // to be passed a Twine instead of a string. + llvm::errs() << "Error in " + file->path() << ": "; + logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string()); + return false; + } + undefAdded = undefAddedOrError.get(); + break; + } + case File::kindArchiveLibrary: { + if (!file->hasOrdinal()) + file->setOrdinal(_ctx.getNextOrdinalAndIncrement()); + auto undefAddedOrError = handleArchiveFile(*file); + if (auto EC = undefAddedOrError.takeError()) { + // FIXME: This should be passed to logAllUnhandledErrors but it needs + // to be passed a Twine instead of a string. + llvm::errs() << "Error in " + file->path() << ": "; + logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string()); + return false; + } + undefAdded = undefAddedOrError.get(); + break; + } + case File::kindSharedLibrary: + if (!file->hasOrdinal()) + file->setOrdinal(_ctx.getNextOrdinalAndIncrement()); + if (auto EC = handleSharedLibrary(*file)) { + // FIXME: This should be passed to logAllUnhandledErrors but it needs + // to be passed a Twine instead of a string. + llvm::errs() << "Error in " + file->path() << ": "; + logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string()); + return false; + } + break; + } + _newUndefinesAdded[file] = undefAdded; + } +} + +// switch all references to undefined or coalesced away atoms +// to the new defined atom +void Resolver::updateReferences() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Updating references:\n"); + ScopedTask task(getDefaultDomain(), "updateReferences"); + for (const OwningAtomPtr &atom : _atoms) { + if (const DefinedAtom *defAtom = dyn_cast(atom.get())) { + for (const Reference *ref : *defAtom) { + // A reference of type kindAssociate should't be updated. + // Instead, an atom having such reference will be removed + // if the target atom is coalesced away, so that they will + // go away as a group. + if (ref->kindNamespace() == lld::Reference::KindNamespace::all && + ref->kindValue() == lld::Reference::kindAssociate) { + if (_symbolTable.isCoalescedAway(atom.get())) + _deadAtoms.insert(ref->target()); + continue; + } + const Atom *newTarget = _symbolTable.replacement(ref->target()); + const_cast(ref)->setTarget(newTarget); + } + } + } +} + +// For dead code stripping, recursively mark atoms "live" +void Resolver::markLive(const Atom *atom) { + // Mark the atom is live. If it's already marked live, then stop recursion. + auto exists = _liveAtoms.insert(atom); + if (!exists.second) + return; + + // Mark all atoms it references as live + if (const DefinedAtom *defAtom = dyn_cast(atom)) { + for (const Reference *ref : *defAtom) + markLive(ref->target()); + for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) { + const Atom *target = p.second; + markLive(target); + } + } +} + +static bool isBackref(const Reference *ref) { + if (ref->kindNamespace() != lld::Reference::KindNamespace::all) + return false; + return (ref->kindValue() == lld::Reference::kindLayoutAfter); +} + +// remove all atoms not actually used +void Resolver::deadStripOptimize() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Dead stripping unused atoms:\n"); + ScopedTask task(getDefaultDomain(), "deadStripOptimize"); + // only do this optimization with -dead_strip + if (!_ctx.deadStrip()) + return; + + // Some type of references prevent referring atoms to be dead-striped. + // Make a reverse map of such references before traversing the graph. + // While traversing the list of atoms, mark AbsoluteAtoms as live + // in order to avoid reclaim. + for (const OwningAtomPtr &atom : _atoms) { + if (const DefinedAtom *defAtom = dyn_cast(atom.get())) + for (const Reference *ref : *defAtom) + if (isBackref(ref)) + _reverseRef.insert(std::make_pair(ref->target(), atom.get())); + if (const AbsoluteAtom *absAtom = dyn_cast(atom.get())) + markLive(absAtom); + } + + // By default, shared libraries are built with all globals as dead strip roots + if (_ctx.globalsAreDeadStripRoots()) + for (const OwningAtomPtr &atom : _atoms) + if (const DefinedAtom *defAtom = dyn_cast(atom.get())) + if (defAtom->scope() == DefinedAtom::scopeGlobal) + _deadStripRoots.insert(defAtom); + + // Or, use list of names that are dead strip roots. + for (const StringRef &name : _ctx.deadStripRoots()) { + const Atom *symAtom = _symbolTable.findByName(name); + assert(symAtom); + _deadStripRoots.insert(symAtom); + } + + // mark all roots as live, and recursively all atoms they reference + for (const Atom *dsrAtom : _deadStripRoots) + markLive(dsrAtom); + + // now remove all non-live atoms from _atoms + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), + [&](OwningAtomPtr &a) { + return _liveAtoms.count(a.get()) == 0; + }), + _atoms.end()); +} + +// error out if some undefines remain +bool Resolver::checkUndefines() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Checking for undefines:\n"); + + // build vector of remaining undefined symbols + std::vector undefinedAtoms = _symbolTable.undefines(); + if (_ctx.deadStrip()) { + // When dead code stripping, we don't care if dead atoms are undefined. + undefinedAtoms.erase( + std::remove_if(undefinedAtoms.begin(), undefinedAtoms.end(), + [&](const Atom *a) { return _liveAtoms.count(a) == 0; }), + undefinedAtoms.end()); + } + + if (undefinedAtoms.empty()) + return false; + + // Warn about unresolved symbols. + bool foundUndefines = false; + for (const UndefinedAtom *undef : undefinedAtoms) { + // Skip over a weak symbol. + if (undef->canBeNull() != UndefinedAtom::canBeNullNever) + continue; + + // If this is a library and undefined symbols are allowed on the + // target platform, skip over it. + if (isa(undef->file()) && _ctx.allowShlibUndefines()) + continue; + + // If the undefine is coalesced away, skip over it. + if (_symbolTable.isCoalescedAway(undef)) + continue; + + // Seems like this symbol is undefined. Warn that. + foundUndefines = true; + if (_ctx.printRemainingUndefines()) { + llvm::errs() << "Undefined symbol: " << undef->file().path() + << ": " << _ctx.demangle(undef->name()) + << "\n"; + } + } + if (!foundUndefines) + return false; + if (_ctx.printRemainingUndefines()) + llvm::errs() << "symbol(s) not found\n"; + return true; +} + +// remove from _atoms all coaleseced away atoms +void Resolver::removeCoalescedAwayAtoms() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Removing coalesced away atoms:\n"); + ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms"); + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), + [&](OwningAtomPtr &a) { + return _symbolTable.isCoalescedAway(a.get()) || + _deadAtoms.count(a.get()); + }), + _atoms.end()); +} + +bool Resolver::resolve() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Resolving atom references:\n"); + if (!resolveUndefines()) + return false; + updateReferences(); + deadStripOptimize(); + if (checkUndefines()) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... "); + if (!_ctx.allowRemainingUndefines()) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n"); + return false; + } + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n"); + } + removeCoalescedAwayAtoms(); + _result->addAtoms(_atoms); + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n"); + return true; +} + +void Resolver::MergedFile::addAtoms( + llvm::MutableArrayRef> all) { + ScopedTask task(getDefaultDomain(), "addAtoms"); + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n"); + + for (OwningAtomPtr &atom : all) { +#ifndef NDEBUG + if (auto *definedAtom = dyn_cast(atom.get())) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << llvm::format(" 0x%09lX", definedAtom) + << ", file=#" + << definedAtom->file().ordinal() + << ", atom=#" + << definedAtom->ordinal() + << ", name=" + << definedAtom->name() + << ", type=" + << definedAtom->contentType() + << "\n"); + } else { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << llvm::format(" 0x%09lX", atom.get()) + << ", name=" + << atom.get()->name() + << "\n"); + } +#endif + addAtom(*atom.release()); + } +} + +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/Resolver.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/SymbolTable.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/SymbolTable.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/SymbolTable.cpp (revision 352529) @@ -0,0 +1,290 @@ +//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/SymbolTable.h" +#include "lld/Common/LLVM.h" +#include "lld/Core/AbsoluteAtom.h" +#include "lld/Core/Atom.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Resolver.h" +#include "lld/Core/SharedLibraryAtom.h" +#include "lld/Core/UndefinedAtom.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +namespace lld { +bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); } + +bool SymbolTable::add(const SharedLibraryAtom &atom) { return addByName(atom); } + +bool SymbolTable::add(const AbsoluteAtom &atom) { return addByName(atom); } + +bool SymbolTable::add(const DefinedAtom &atom) { + if (!atom.name().empty() && + atom.scope() != DefinedAtom::scopeTranslationUnit) { + // Named atoms cannot be merged by content. + assert(atom.merge() != DefinedAtom::mergeByContent); + // Track named atoms that are not scoped to file (static). + return addByName(atom); + } + if (atom.merge() == DefinedAtom::mergeByContent) { + // Named atoms cannot be merged by content. + assert(atom.name().empty()); + // Currently only read-only constants can be merged. + if (atom.permissions() == DefinedAtom::permR__) + return addByContent(atom); + // TODO: support mergeByContent of data atoms by comparing content & fixups. + } + return false; +} + +enum NameCollisionResolution { + NCR_First, + NCR_Second, + NCR_DupDef, + NCR_DupUndef, + NCR_DupShLib, + NCR_Error +}; + +static NameCollisionResolution cases[4][4] = { + //regular absolute undef sharedLib + { + // first is regular + NCR_DupDef, NCR_Error, NCR_First, NCR_First + }, + { + // first is absolute + NCR_Error, NCR_Error, NCR_First, NCR_First + }, + { + // first is undef + NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second + }, + { + // first is sharedLib + NCR_Second, NCR_Second, NCR_First, NCR_DupShLib + } +}; + +static NameCollisionResolution collide(Atom::Definition first, + Atom::Definition second) { + return cases[first][second]; +} + +enum MergeResolution { + MCR_First, + MCR_Second, + MCR_Largest, + MCR_SameSize, + MCR_Error +}; + +static MergeResolution mergeCases[][6] = { + // no tentative weak weakAddress sameNameAndSize largest + {MCR_Error, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // no + {MCR_Second, MCR_Largest, MCR_Second, MCR_Second, MCR_SameSize, MCR_Largest}, // tentative + {MCR_Second, MCR_First, MCR_First, MCR_Second, MCR_SameSize, MCR_Largest}, // weak + {MCR_Second, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // weakAddress + {MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize}, // sameSize + {MCR_Largest, MCR_Largest, MCR_Largest, MCR_Largest, MCR_SameSize, MCR_Largest}, // largest +}; + +static MergeResolution mergeSelect(DefinedAtom::Merge first, + DefinedAtom::Merge second) { + assert(first != DefinedAtom::mergeByContent); + assert(second != DefinedAtom::mergeByContent); + return mergeCases[first][second]; +} + +bool SymbolTable::addByName(const Atom &newAtom) { + StringRef name = newAtom.name(); + assert(!name.empty()); + const Atom *existing = findByName(name); + if (existing == nullptr) { + // Name is not in symbol table yet, add it associate with this atom. + _nameTable[name] = &newAtom; + return true; + } + + // Do nothing if the same object is added more than once. + if (existing == &newAtom) + return false; + + // Name is already in symbol table and associated with another atom. + bool useNew = true; + switch (collide(existing->definition(), newAtom.definition())) { + case NCR_First: + useNew = false; + break; + case NCR_Second: + useNew = true; + break; + case NCR_DupDef: { + const auto *existingDef = cast(existing); + const auto *newDef = cast(&newAtom); + switch (mergeSelect(existingDef->merge(), newDef->merge())) { + case MCR_First: + useNew = false; + break; + case MCR_Second: + useNew = true; + break; + case MCR_Largest: { + uint64_t existingSize = existingDef->sectionSize(); + uint64_t newSize = newDef->sectionSize(); + useNew = (newSize >= existingSize); + break; + } + case MCR_SameSize: { + uint64_t existingSize = existingDef->sectionSize(); + uint64_t newSize = newDef->sectionSize(); + if (existingSize == newSize) { + useNew = true; + break; + } + llvm::errs() << "Size mismatch: " + << existing->name() << " (" << existingSize << ") " + << newAtom.name() << " (" << newSize << ")\n"; + LLVM_FALLTHROUGH; + } + case MCR_Error: + llvm::errs() << "Duplicate symbols: " + << existing->name() + << ":" + << existing->file().path() + << " and " + << newAtom.name() + << ":" + << newAtom.file().path() + << "\n"; + llvm::report_fatal_error("duplicate symbol error"); + break; + } + break; + } + case NCR_DupUndef: { + const UndefinedAtom* existingUndef = cast(existing); + const UndefinedAtom* newUndef = cast(&newAtom); + + bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull()); + if (sameCanBeNull) + useNew = false; + else + useNew = (newUndef->canBeNull() < existingUndef->canBeNull()); + break; + } + case NCR_DupShLib: { + useNew = false; + break; + } + case NCR_Error: + llvm::errs() << "SymbolTable: error while merging " << name << "\n"; + llvm::report_fatal_error("duplicate symbol error"); + break; + } + + if (useNew) { + // Update name table to use new atom. + _nameTable[name] = &newAtom; + // Add existing atom to replacement table. + _replacedAtoms[existing] = &newAtom; + } else { + // New atom is not being used. Add it to replacement table. + _replacedAtoms[&newAtom] = existing; + } + return false; +} + +unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) { + auto content = atom->rawContent(); + return llvm::hash_combine(atom->size(), + atom->contentType(), + llvm::hash_combine_range(content.begin(), + content.end())); +} + +bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l, + const DefinedAtom * const r) { + if (l == r) + return true; + if (l == getEmptyKey() || r == getEmptyKey()) + return false; + if (l == getTombstoneKey() || r == getTombstoneKey()) + return false; + if (l->contentType() != r->contentType()) + return false; + if (l->size() != r->size()) + return false; + if (l->sectionChoice() != r->sectionChoice()) + return false; + if (l->sectionChoice() == DefinedAtom::sectionCustomRequired) { + if (!l->customSectionName().equals(r->customSectionName())) + return false; + } + ArrayRef lc = l->rawContent(); + ArrayRef rc = r->rawContent(); + return memcmp(lc.data(), rc.data(), lc.size()) == 0; +} + +bool SymbolTable::addByContent(const DefinedAtom &newAtom) { + AtomContentSet::iterator pos = _contentTable.find(&newAtom); + if (pos == _contentTable.end()) { + _contentTable.insert(&newAtom); + return true; + } + const Atom* existing = *pos; + // New atom is not being used. Add it to replacement table. + _replacedAtoms[&newAtom] = existing; + return false; +} + +const Atom *SymbolTable::findByName(StringRef sym) { + NameToAtom::iterator pos = _nameTable.find(sym); + if (pos == _nameTable.end()) + return nullptr; + return pos->second; +} + +const Atom *SymbolTable::replacement(const Atom *atom) { + // Find the replacement for a given atom. Atoms in _replacedAtoms + // may be chained, so find the last one. + for (;;) { + AtomToAtom::iterator pos = _replacedAtoms.find(atom); + if (pos == _replacedAtoms.end()) + return atom; + atom = pos->second; + } +} + +bool SymbolTable::isCoalescedAway(const Atom *atom) { + return _replacedAtoms.count(atom) > 0; +} + +std::vector SymbolTable::undefines() { + std::vector ret; + for (auto it : _nameTable) { + const Atom *atom = it.second; + assert(atom != nullptr); + if (const auto *undef = dyn_cast(atom)) + if (_replacedAtoms.count(undef) == 0) + ret.push_back(undef); + } + return ret; +} + +} // namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/SymbolTable.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/Writer.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/Writer.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/Writer.cpp (revision 352529) @@ -0,0 +1,17 @@ +//===- lib/Core/Writer.cpp ------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Writer.h" + +namespace lld { + +Writer::Writer() = default; + +Writer::~Writer() = default; + +} // end namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/Writer.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Core/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Core/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Core/CMakeLists.txt (revision 352529) @@ -0,0 +1,28 @@ +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lld_library(lldCore + DefinedAtom.cpp + Error.cpp + File.cpp + LinkingContext.cpp + Reader.cpp + Resolver.cpp + SymbolTable.cpp + Writer.cpp + + ADDITIONAL_HEADER_DIRS + ${LLD_INCLUDE_DIR}/lld/Core + + LINK_COMPONENTS + BinaryFormat + MC + Support + + LINK_LIBS + ${LLVM_PTHREAD_LIB} + + DEPENDS + ${tablegen_deps} + ) Property changes on: vendor/lld/lld-release_900-r372316/lib/Core/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdDriver.cpp =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdDriver.cpp (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdDriver.cpp (revision 352529) @@ -0,0 +1,1229 @@ +//===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Concrete instance of the Driver for darwin's ld. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Args.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/LLVM.h" +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Node.h" +#include "lld/Core/PassManager.h" +#include "lld/Core/Resolver.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace lld; + +namespace { + +// Create enum with OPT_xxx values for each option in DarwinLdOptions.td +enum { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELP, META, VALUES) \ + OPT_##ID, +#include "DarwinLdOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in DarwinLdOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "DarwinLdOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in DarwinLdOptions.td +static const llvm::opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "DarwinLdOptions.inc" +#undef OPTION +}; + +// Create OptTable class for parsing actual command line arguments +class DarwinLdOptTable : public llvm::opt::OptTable { +public: + DarwinLdOptTable() : OptTable(InfoTable) {} +}; + +static std::vector> +makeErrorFile(StringRef path, std::error_code ec) { + std::vector> result; + result.push_back(llvm::make_unique(path, ec)); + return result; +} + +static std::vector> +parseMemberFiles(std::unique_ptr file) { + std::vector> members; + if (auto *archive = dyn_cast(file.get())) { + if (std::error_code ec = archive->parseAllMembers(members)) + return makeErrorFile(file->path(), ec); + } else { + members.push_back(std::move(file)); + } + return members; +} + +std::vector> loadFile(MachOLinkingContext &ctx, + StringRef path, bool wholeArchive, + bool upwardDylib) { + if (ctx.logInputFiles()) + message(path); + + ErrorOr> mbOrErr = ctx.getMemoryBuffer(path); + if (std::error_code ec = mbOrErr.getError()) + return makeErrorFile(path, ec); + ErrorOr> fileOrErr = + ctx.registry().loadFile(std::move(mbOrErr.get())); + if (std::error_code ec = fileOrErr.getError()) + return makeErrorFile(path, ec); + std::unique_ptr &file = fileOrErr.get(); + + // If file is a dylib, inform LinkingContext about it. + if (SharedLibraryFile *shl = dyn_cast(file.get())) { + if (std::error_code ec = shl->parse()) + return makeErrorFile(path, ec); + ctx.registerDylib(reinterpret_cast(shl), + upwardDylib); + } + if (wholeArchive) + return parseMemberFiles(std::move(file)); + std::vector> files; + files.push_back(std::move(file)); + return files; +} + +} // end anonymous namespace + +// Test may be running on Windows. Canonicalize the path +// separator to '/' to get consistent outputs for tests. +static std::string canonicalizePath(StringRef path) { + char sep = llvm::sys::path::get_separator().front(); + if (sep != '/') { + std::string fixedPath = path; + std::replace(fixedPath.begin(), fixedPath.end(), sep, '/'); + return fixedPath; + } else { + return path; + } +} + +static void addFile(StringRef path, MachOLinkingContext &ctx, + bool loadWholeArchive, bool upwardDylib) { + std::vector> files = + loadFile(ctx, path, loadWholeArchive, upwardDylib); + for (std::unique_ptr &file : files) + ctx.getNodes().push_back(llvm::make_unique(std::move(file))); +} + +// Export lists are one symbol per line. Blank lines are ignored. +// Trailing comments start with #. +static std::error_code parseExportsList(StringRef exportFilePath, + MachOLinkingContext &ctx) { + // Map in export list file. + ErrorOr> mb = + MemoryBuffer::getFileOrSTDIN(exportFilePath); + if (std::error_code ec = mb.getError()) + return ec; + ctx.addInputFileDependency(exportFilePath); + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + // Ignore trailing # comments. + std::pair symAndComment = line.split('#'); + StringRef sym = symAndComment.first.trim(); + if (!sym.empty()) + ctx.addExportSymbol(sym); + buffer = lineAndRest.second; + } + return std::error_code(); +} + +/// Order files are one symbol per line. Blank lines are ignored. +/// Trailing comments start with #. Symbol names can be prefixed with an +/// architecture name and/or .o leaf name. Examples: +/// _foo +/// bar.o:_bar +/// libfrob.a(bar.o):_bar +/// x86_64:_foo64 +static std::error_code parseOrderFile(StringRef orderFilePath, + MachOLinkingContext &ctx) { + // Map in order file. + ErrorOr> mb = + MemoryBuffer::getFileOrSTDIN(orderFilePath); + if (std::error_code ec = mb.getError()) + return ec; + ctx.addInputFileDependency(orderFilePath); + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + buffer = lineAndRest.second; + // Ignore trailing # comments. + std::pair symAndComment = line.split('#'); + if (symAndComment.first.empty()) + continue; + StringRef sym = symAndComment.first.trim(); + if (sym.empty()) + continue; + // Check for prefix. + StringRef prefix; + std::pair prefixAndSym = sym.split(':'); + if (!prefixAndSym.second.empty()) { + sym = prefixAndSym.second; + prefix = prefixAndSym.first; + if (!prefix.endswith(".o") && !prefix.endswith(".o)")) { + // If arch name prefix does not match arch being linked, ignore symbol. + if (!ctx.archName().equals(prefix)) + continue; + prefix = ""; + } + } else + sym = prefixAndSym.first; + if (!sym.empty()) { + ctx.appendOrderedSymbol(sym, prefix); + //llvm::errs() << sym << ", prefix=" << prefix << "\n"; + } + } + return std::error_code(); +} + +// +// There are two variants of the -filelist option: +// +// -filelist +// In this variant, the path is to a text file which contains one file path +// per line. There are no comments or trimming of whitespace. +// +// -fileList , +// In this variant, the path is to a text file which contains a partial path +// per line. The prefix is prepended to each partial path. +// +static llvm::Error loadFileList(StringRef fileListPath, + MachOLinkingContext &ctx, bool forceLoad) { + // If there is a comma, split off . + std::pair opt = fileListPath.split(','); + StringRef filePath = opt.first; + StringRef dirName = opt.second; + ctx.addInputFileDependency(filePath); + // Map in file list file. + ErrorOr> mb = + MemoryBuffer::getFileOrSTDIN(filePath); + if (std::error_code ec = mb.getError()) + return llvm::errorCodeToError(ec); + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + StringRef path; + if (!dirName.empty()) { + // If there is a then prepend dir to each line. + SmallString<256> fullPath; + fullPath.assign(dirName); + llvm::sys::path::append(fullPath, Twine(line)); + path = ctx.copy(fullPath.str()); + } else { + // No use whole line as input file path. + path = ctx.copy(line); + } + if (!ctx.pathExists(path)) { + return llvm::make_error(Twine("File not found '") + + path + + "'"); + } + if (ctx.testingFileUsage()) { + message("Found filelist entry " + canonicalizePath(path)); + } + addFile(path, ctx, forceLoad, false); + buffer = lineAndRest.second; + } + return llvm::Error::success(); +} + +/// Parse number assuming it is base 16, but allow 0x prefix. +static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) { + if (numStr.startswith_lower("0x")) + numStr = numStr.drop_front(2); + return numStr.getAsInteger(16, baseAddress); +} + +static void parseLLVMOptions(const LinkingContext &ctx) { + // Honor -mllvm + if (!ctx.llvmOptions().empty()) { + unsigned numArgs = ctx.llvmOptions().size(); + auto **args = new const char *[numArgs + 2]; + args[0] = "lld (LLVM option parsing)"; + for (unsigned i = 0; i != numArgs; ++i) + args[i + 1] = ctx.llvmOptions()[i]; + args[numArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(numArgs + 1, args); + } +} + +namespace lld { +namespace mach_o { + +bool parse(llvm::ArrayRef args, MachOLinkingContext &ctx) { + // Parse command line options using DarwinLdOptions.td + DarwinLdOptTable table; + unsigned missingIndex; + unsigned missingCount; + llvm::opt::InputArgList parsedArgs = + table.ParseArgs(args.slice(1), missingIndex, missingCount); + if (missingCount) { + error("missing arg value for '" + + Twine(parsedArgs.getArgString(missingIndex)) + "' expected " + + Twine(missingCount) + " argument(s)."); + return false; + } + + for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) { + warn("ignoring unknown argument: " + + Twine(unknownArg->getAsString(parsedArgs))); + } + + errorHandler().verbose = parsedArgs.hasArg(OPT_v); + errorHandler().errorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20); + + // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) + llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE; + bool isStaticExecutable = false; + if (llvm::opt::Arg *kind = parsedArgs.getLastArg( + OPT_dylib, OPT_relocatable, OPT_bundle, OPT_static, OPT_preload)) { + switch (kind->getOption().getID()) { + case OPT_dylib: + fileType = llvm::MachO::MH_DYLIB; + break; + case OPT_relocatable: + fileType = llvm::MachO::MH_OBJECT; + break; + case OPT_bundle: + fileType = llvm::MachO::MH_BUNDLE; + break; + case OPT_static: + fileType = llvm::MachO::MH_EXECUTE; + isStaticExecutable = true; + break; + case OPT_preload: + fileType = llvm::MachO::MH_PRELOAD; + break; + } + } + + // Handle -arch xxx + MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown; + if (llvm::opt::Arg *archStr = parsedArgs.getLastArg(OPT_arch)) { + arch = MachOLinkingContext::archFromName(archStr->getValue()); + if (arch == MachOLinkingContext::arch_unknown) { + error("unknown arch named '" + Twine(archStr->getValue()) + "'"); + return false; + } + } + // If no -arch specified, scan input files to find first non-fat .o file. + if (arch == MachOLinkingContext::arch_unknown) { + for (auto &inFile : parsedArgs.filtered(OPT_INPUT)) { + // This is expensive because it opens and maps the file. But that is + // ok because no -arch is rare. + if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch)) + break; + } + if (arch == MachOLinkingContext::arch_unknown && + !parsedArgs.getLastArg(OPT_test_file_usage)) { + // If no -arch and no options at all, print usage message. + if (parsedArgs.size() == 0) { + table.PrintHelp(llvm::outs(), + (std::string(args[0]) + " [options] file...").c_str(), + "LLVM Linker", false); + } else { + error("-arch not specified and could not be inferred"); + } + return false; + } + } + + // Handle -macosx_version_min or -ios_version_min + MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown; + uint32_t minOSVersion = 0; + if (llvm::opt::Arg *minOS = + parsedArgs.getLastArg(OPT_macosx_version_min, OPT_ios_version_min, + OPT_ios_simulator_version_min)) { + switch (minOS->getOption().getID()) { + case OPT_macosx_version_min: + os = MachOLinkingContext::OS::macOSX; + if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), + minOSVersion)) { + error("malformed macosx_version_min value"); + return false; + } + break; + case OPT_ios_version_min: + os = MachOLinkingContext::OS::iOS; + if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), + minOSVersion)) { + error("malformed ios_version_min value"); + return false; + } + break; + case OPT_ios_simulator_version_min: + os = MachOLinkingContext::OS::iOS_simulator; + if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), + minOSVersion)) { + error("malformed ios_simulator_version_min value"); + return false; + } + break; + } + } else { + // No min-os version on command line, check environment variables + } + + // Handle export_dynamic + // FIXME: Should we warn when this applies to something other than a static + // executable or dylib? Those are the only cases where this has an effect. + // Note, this has to come before ctx.configure() so that we get the correct + // value for _globalsAreDeadStripRoots. + bool exportDynamicSymbols = parsedArgs.hasArg(OPT_export_dynamic); + + // Now that there's enough information parsed in, let the linking context + // set up default values. + ctx.configure(fileType, arch, os, minOSVersion, exportDynamicSymbols); + + // Handle -e xxx + if (llvm::opt::Arg *entry = parsedArgs.getLastArg(OPT_entry)) + ctx.setEntrySymbolName(entry->getValue()); + + // Handle -o xxx + if (llvm::opt::Arg *outpath = parsedArgs.getLastArg(OPT_output)) + ctx.setOutputPath(outpath->getValue()); + else + ctx.setOutputPath("a.out"); + + // Handle -image_base XXX and -seg1addr XXXX + if (llvm::opt::Arg *imageBase = parsedArgs.getLastArg(OPT_image_base)) { + uint64_t baseAddress; + if (parseNumberBase16(imageBase->getValue(), baseAddress)) { + error("image_base expects a hex number"); + return false; + } else if (baseAddress < ctx.pageZeroSize()) { + error("image_base overlaps with __PAGEZERO"); + return false; + } else if (baseAddress % ctx.pageSize()) { + error("image_base must be a multiple of page size (0x" + + llvm::utohexstr(ctx.pageSize()) + ")"); + return false; + } + + ctx.setBaseAddress(baseAddress); + } + + // Handle -dead_strip + if (parsedArgs.getLastArg(OPT_dead_strip)) + ctx.setDeadStripping(true); + + bool globalWholeArchive = false; + // Handle -all_load + if (parsedArgs.getLastArg(OPT_all_load)) + globalWholeArchive = true; + + // Handle -install_name + if (llvm::opt::Arg *installName = parsedArgs.getLastArg(OPT_install_name)) + ctx.setInstallName(installName->getValue()); + else + ctx.setInstallName(ctx.outputPath()); + + // Handle -mark_dead_strippable_dylib + if (parsedArgs.getLastArg(OPT_mark_dead_strippable_dylib)) + ctx.setDeadStrippableDylib(true); + + // Handle -compatibility_version and -current_version + if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_compatibility_version)) { + if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { + error("-compatibility_version can only be used with -dylib"); + return false; + } + uint32_t parsedVers; + if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { + error("-compatibility_version value is malformed"); + return false; + } + ctx.setCompatibilityVersion(parsedVers); + } + + if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_current_version)) { + if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { + error("-current_version can only be used with -dylib"); + return false; + } + uint32_t parsedVers; + if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { + error("-current_version value is malformed"); + return false; + } + ctx.setCurrentVersion(parsedVers); + } + + // Handle -bundle_loader + if (llvm::opt::Arg *loader = parsedArgs.getLastArg(OPT_bundle_loader)) + ctx.setBundleLoader(loader->getValue()); + + // Handle -sectalign segname sectname align + for (auto &alignArg : parsedArgs.filtered(OPT_sectalign)) { + const char* segName = alignArg->getValue(0); + const char* sectName = alignArg->getValue(1); + const char* alignStr = alignArg->getValue(2); + if ((alignStr[0] == '0') && (alignStr[1] == 'x')) + alignStr += 2; + unsigned long long alignValue; + if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) { + error("-sectalign alignment value '" + Twine(alignStr) + + "' not a valid number"); + return false; + } + uint16_t align = 1 << llvm::countTrailingZeros(alignValue); + if (!llvm::isPowerOf2_64(alignValue)) { + std::string Msg; + llvm::raw_string_ostream OS(Msg); + OS << "alignment for '-sectalign " << segName << " " << sectName + << llvm::format(" 0x%llX", alignValue) + << "' is not a power of two, using " << llvm::format("0x%08X", align); + OS.flush(); + warn(Msg); + } + ctx.addSectionAlignment(segName, sectName, align); + } + + // Handle -mllvm + for (auto &llvmArg : parsedArgs.filtered(OPT_mllvm)) { + ctx.appendLLVMOption(llvmArg->getValue()); + } + + // Handle -print_atoms + if (parsedArgs.getLastArg(OPT_print_atoms)) + ctx.setPrintAtoms(); + + // Handle -t (trace) option. + if (parsedArgs.getLastArg(OPT_t)) + ctx.setLogInputFiles(true); + + // Handle -demangle option. + if (parsedArgs.getLastArg(OPT_demangle)) + ctx.setDemangleSymbols(true); + + // Handle -keep_private_externs + if (parsedArgs.getLastArg(OPT_keep_private_externs)) { + ctx.setKeepPrivateExterns(true); + if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT) + warn("-keep_private_externs only used in -r mode"); + } + + // Handle -dependency_info used by Xcode. + if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info)) + if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) + warn(ec.message() + ", processing '-dependency_info " + + depInfo->getValue()); + + // In -test_file_usage mode, we'll be given an explicit list of paths that + // exist. We'll also be expected to print out information about how we located + // libraries and so on that the user specified, but not to actually do any + // linking. + if (parsedArgs.getLastArg(OPT_test_file_usage)) { + ctx.setTestingFileUsage(); + + // With paths existing by fiat, linking is not going to end well. + ctx.setDoNothing(true); + + // Only bother looking for an existence override if we're going to use it. + for (auto existingPath : parsedArgs.filtered(OPT_path_exists)) { + ctx.addExistingPathForDebug(existingPath->getValue()); + } + } + + // Register possible input file parsers. + if (!ctx.doNothing()) { + ctx.registry().addSupportMachOObjects(ctx); + ctx.registry().addSupportArchives(ctx.logInputFiles()); + ctx.registry().addSupportYamlFiles(); + } + + // Now construct the set of library search directories, following ld64's + // baroque set of accumulated hacks. Mostly, the algorithm constructs + // { syslibroots } x { libpaths } + // + // Unfortunately, there are numerous exceptions: + // 1. Only absolute paths get modified by syslibroot options. + // 2. If there is just 1 -syslibroot, system paths not found in it are + // skipped. + // 3. If the last -syslibroot is "/", all of them are ignored entirely. + // 4. If { syslibroots } x path == {}, the original path is kept. + std::vector sysLibRoots; + for (auto syslibRoot : parsedArgs.filtered(OPT_syslibroot)) { + sysLibRoots.push_back(syslibRoot->getValue()); + } + if (!sysLibRoots.empty()) { + // Ignore all if last -syslibroot is "/". + if (sysLibRoots.back() != "/") + ctx.setSysLibRoots(sysLibRoots); + } + + // Paths specified with -L come first, and are not considered system paths for + // the case where there is precisely 1 -syslibroot. + for (auto libPath : parsedArgs.filtered(OPT_L)) { + ctx.addModifiedSearchDir(libPath->getValue()); + } + + // Process -F directories (where to look for frameworks). + for (auto fwPath : parsedArgs.filtered(OPT_F)) { + ctx.addFrameworkSearchDir(fwPath->getValue()); + } + + // -Z suppresses the standard search paths. + if (!parsedArgs.hasArg(OPT_Z)) { + ctx.addModifiedSearchDir("/usr/lib", true); + ctx.addModifiedSearchDir("/usr/local/lib", true); + ctx.addFrameworkSearchDir("/Library/Frameworks", true); + ctx.addFrameworkSearchDir("/System/Library/Frameworks", true); + } + + // Now that we've constructed the final set of search paths, print out those + // search paths in verbose mode. + if (errorHandler().verbose) { + message("Library search paths:"); + for (auto path : ctx.searchDirs()) { + message(" " + path); + } + message("Framework search paths:"); + for (auto path : ctx.frameworkDirs()) { + message(" " + path); + } + } + + // Handle -exported_symbols_list + for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { + error("-exported_symbols_list cannot be combined with " + "-unexported_symbol[s_list]"); + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) { + error(ec.message() + ", processing '-exported_symbols_list " + + expFile->getValue()); + return false; + } + } + + // Handle -exported_symbol + for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { + error("-exported_symbol cannot be combined with " + "-unexported_symbol[s_list]"); + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); + ctx.addExportSymbol(symbol->getValue()); + } + + // Handle -unexported_symbols_list + for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { + error("-unexported_symbols_list cannot be combined with " + "-exported_symbol[s_list]"); + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) { + error(ec.message() + ", processing '-unexported_symbols_list " + + expFile->getValue()); + return false; + } + } + + // Handle -unexported_symbol + for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { + error("-unexported_symbol cannot be combined with " + "-exported_symbol[s_list]"); + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); + ctx.addExportSymbol(symbol->getValue()); + } + + // Handle obosolete -multi_module and -single_module + if (llvm::opt::Arg *mod = + parsedArgs.getLastArg(OPT_multi_module, OPT_single_module)) { + if (mod->getOption().getID() == OPT_multi_module) + warn("-multi_module is obsolete and being ignored"); + else if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) + warn("-single_module being ignored. It is only for use when producing a " + "dylib"); + } + + // Handle obsolete ObjC options: -objc_gc_compaction, -objc_gc, -objc_gc_only + if (parsedArgs.getLastArg(OPT_objc_gc_compaction)) { + error("-objc_gc_compaction is not supported"); + return false; + } + + if (parsedArgs.getLastArg(OPT_objc_gc)) { + error("-objc_gc is not supported"); + return false; + } + + if (parsedArgs.getLastArg(OPT_objc_gc_only)) { + error("-objc_gc_only is not supported"); + return false; + } + + // Handle -pie or -no_pie + if (llvm::opt::Arg *pie = parsedArgs.getLastArg(OPT_pie, OPT_no_pie)) { + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_EXECUTE: + switch (ctx.os()) { + case MachOLinkingContext::OS::macOSX: + if ((minOSVersion < 0x000A0500) && + (pie->getOption().getID() == OPT_pie)) { + error("-pie can only be used when targeting Mac OS X 10.5 or later"); + return false; + } + break; + case MachOLinkingContext::OS::iOS: + if ((minOSVersion < 0x00040200) && + (pie->getOption().getID() == OPT_pie)) { + error("-pie can only be used when targeting iOS 4.2 or later"); + return false; + } + break; + case MachOLinkingContext::OS::iOS_simulator: + if (pie->getOption().getID() == OPT_no_pie) { + error("iOS simulator programs must be built PIE"); + return false; + } + break; + case MachOLinkingContext::OS::unknown: + break; + } + ctx.setPIE(pie->getOption().getID() == OPT_pie); + break; + case llvm::MachO::MH_PRELOAD: + break; + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + warn(pie->getSpelling() + + " being ignored. It is only used when linking main executables"); + break; + default: + error(pie->getSpelling() + + " can only used when linking main executables"); + return false; + } + } + + // Handle -version_load_command or -no_version_load_command + { + bool flagOn = false; + bool flagOff = false; + if (auto *arg = parsedArgs.getLastArg(OPT_version_load_command, + OPT_no_version_load_command)) { + flagOn = arg->getOption().getID() == OPT_version_load_command; + flagOff = arg->getOption().getID() == OPT_no_version_load_command; + } + + // default to adding version load command for dynamic code, + // static code must opt-in + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_OBJECT: + ctx.setGenerateVersionLoadCommand(false); + break; + case llvm::MachO::MH_EXECUTE: + // dynamic executables default to generating a version load command, + // while static exectuables only generate it if required. + if (isStaticExecutable) { + if (flagOn) + ctx.setGenerateVersionLoadCommand(true); + } else { + if (!flagOff) + ctx.setGenerateVersionLoadCommand(true); + } + break; + case llvm::MachO::MH_PRELOAD: + case llvm::MachO::MH_KEXT_BUNDLE: + if (flagOn) + ctx.setGenerateVersionLoadCommand(true); + break; + case llvm::MachO::MH_DYLINKER: + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + if (!flagOff) + ctx.setGenerateVersionLoadCommand(true); + break; + case llvm::MachO::MH_FVMLIB: + case llvm::MachO::MH_DYLDLINK: + case llvm::MachO::MH_DYLIB_STUB: + case llvm::MachO::MH_DSYM: + // We don't generate load commands for these file types, even if + // forced on. + break; + } + } + + // Handle -function_starts or -no_function_starts + { + bool flagOn = false; + bool flagOff = false; + if (auto *arg = parsedArgs.getLastArg(OPT_function_starts, + OPT_no_function_starts)) { + flagOn = arg->getOption().getID() == OPT_function_starts; + flagOff = arg->getOption().getID() == OPT_no_function_starts; + } + + // default to adding functions start for dynamic code, static code must + // opt-in + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_OBJECT: + ctx.setGenerateFunctionStartsLoadCommand(false); + break; + case llvm::MachO::MH_EXECUTE: + // dynamic executables default to generating a version load command, + // while static exectuables only generate it if required. + if (isStaticExecutable) { + if (flagOn) + ctx.setGenerateFunctionStartsLoadCommand(true); + } else { + if (!flagOff) + ctx.setGenerateFunctionStartsLoadCommand(true); + } + break; + case llvm::MachO::MH_PRELOAD: + case llvm::MachO::MH_KEXT_BUNDLE: + if (flagOn) + ctx.setGenerateFunctionStartsLoadCommand(true); + break; + case llvm::MachO::MH_DYLINKER: + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + if (!flagOff) + ctx.setGenerateFunctionStartsLoadCommand(true); + break; + case llvm::MachO::MH_FVMLIB: + case llvm::MachO::MH_DYLDLINK: + case llvm::MachO::MH_DYLIB_STUB: + case llvm::MachO::MH_DSYM: + // We don't generate load commands for these file types, even if + // forced on. + break; + } + } + + // Handle -data_in_code_info or -no_data_in_code_info + { + bool flagOn = false; + bool flagOff = false; + if (auto *arg = parsedArgs.getLastArg(OPT_data_in_code_info, + OPT_no_data_in_code_info)) { + flagOn = arg->getOption().getID() == OPT_data_in_code_info; + flagOff = arg->getOption().getID() == OPT_no_data_in_code_info; + } + + // default to adding data in code for dynamic code, static code must + // opt-in + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_OBJECT: + if (!flagOff) + ctx.setGenerateDataInCodeLoadCommand(true); + break; + case llvm::MachO::MH_EXECUTE: + // dynamic executables default to generating a version load command, + // while static exectuables only generate it if required. + if (isStaticExecutable) { + if (flagOn) + ctx.setGenerateDataInCodeLoadCommand(true); + } else { + if (!flagOff) + ctx.setGenerateDataInCodeLoadCommand(true); + } + break; + case llvm::MachO::MH_PRELOAD: + case llvm::MachO::MH_KEXT_BUNDLE: + if (flagOn) + ctx.setGenerateDataInCodeLoadCommand(true); + break; + case llvm::MachO::MH_DYLINKER: + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + if (!flagOff) + ctx.setGenerateDataInCodeLoadCommand(true); + break; + case llvm::MachO::MH_FVMLIB: + case llvm::MachO::MH_DYLDLINK: + case llvm::MachO::MH_DYLIB_STUB: + case llvm::MachO::MH_DSYM: + // We don't generate load commands for these file types, even if + // forced on. + break; + } + } + + // Handle sdk_version + if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_sdk_version)) { + uint32_t sdkVersion = 0; + if (MachOLinkingContext::parsePackedVersion(arg->getValue(), + sdkVersion)) { + error("malformed sdkVersion value"); + return false; + } + ctx.setSdkVersion(sdkVersion); + } else if (ctx.generateVersionLoadCommand()) { + // If we don't have an sdk version, but were going to emit a load command + // with min_version, then we need to give an warning as we have no sdk + // version to put in that command. + // FIXME: We need to decide whether to make this an error. + warn("-sdk_version is required when emitting min version load command. " + "Setting sdk version to match provided min version"); + ctx.setSdkVersion(ctx.osMinVersion()); + } + + // Handle source_version + if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_source_version)) { + uint64_t version = 0; + if (MachOLinkingContext::parsePackedVersion(arg->getValue(), + version)) { + error("malformed source_version value"); + return false; + } + ctx.setSourceVersion(version); + } + + // Handle stack_size + if (llvm::opt::Arg *stackSize = parsedArgs.getLastArg(OPT_stack_size)) { + uint64_t stackSizeVal; + if (parseNumberBase16(stackSize->getValue(), stackSizeVal)) { + error("stack_size expects a hex number"); + return false; + } + if ((stackSizeVal % ctx.pageSize()) != 0) { + error("stack_size must be a multiple of page size (0x" + + llvm::utohexstr(ctx.pageSize()) + ")"); + return false; + } + + ctx.setStackSize(stackSizeVal); + } + + // Handle debug info handling options: -S + if (parsedArgs.hasArg(OPT_S)) + ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap); + + // Handle -order_file + for (auto orderFile : parsedArgs.filtered(OPT_order_file)) { + if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx)) { + error(ec.message() + ", processing '-order_file " + orderFile->getValue() + + "'"); + return false; + } + } + + // Handle -flat_namespace. + if (llvm::opt::Arg *ns = + parsedArgs.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) { + if (ns->getOption().getID() == OPT_flat_namespace) + ctx.setUseFlatNamespace(true); + } + + // Handle -undefined + if (llvm::opt::Arg *undef = parsedArgs.getLastArg(OPT_undefined)) { + MachOLinkingContext::UndefinedMode UndefMode; + if (StringRef(undef->getValue()).equals("error")) + UndefMode = MachOLinkingContext::UndefinedMode::error; + else if (StringRef(undef->getValue()).equals("warning")) + UndefMode = MachOLinkingContext::UndefinedMode::warning; + else if (StringRef(undef->getValue()).equals("suppress")) + UndefMode = MachOLinkingContext::UndefinedMode::suppress; + else if (StringRef(undef->getValue()).equals("dynamic_lookup")) + UndefMode = MachOLinkingContext::UndefinedMode::dynamicLookup; + else { + error("invalid option to -undefined [ warning | error | suppress | " + "dynamic_lookup ]"); + return false; + } + + if (ctx.useFlatNamespace()) { + // If we're using -flat_namespace then 'warning', 'suppress' and + // 'dynamic_lookup' are all equivalent, so map them to 'suppress'. + if (UndefMode != MachOLinkingContext::UndefinedMode::error) + UndefMode = MachOLinkingContext::UndefinedMode::suppress; + } else { + // If we're using -twolevel_namespace then 'warning' and 'suppress' are + // illegal. Emit a diagnostic if they've been (mis)used. + if (UndefMode == MachOLinkingContext::UndefinedMode::warning || + UndefMode == MachOLinkingContext::UndefinedMode::suppress) { + error("can't use -undefined warning or suppress with " + "-twolevel_namespace"); + return false; + } + } + + ctx.setUndefinedMode(UndefMode); + } + + // Handle -no_objc_category_merging. + if (parsedArgs.getLastArg(OPT_no_objc_category_merging)) + ctx.setMergeObjCCategories(false); + + // Handle -rpath + if (parsedArgs.hasArg(OPT_rpath)) { + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_EXECUTE: + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + if (!ctx.minOS("10.5", "2.0")) { + if (ctx.os() == MachOLinkingContext::OS::macOSX) + error("-rpath can only be used when targeting OS X 10.5 or later"); + else + error("-rpath can only be used when targeting iOS 2.0 or later"); + return false; + } + break; + default: + error("-rpath can only be used when creating a dynamic final linked " + "image"); + return false; + } + + for (auto rPath : parsedArgs.filtered(OPT_rpath)) { + ctx.addRpath(rPath->getValue()); + } + } + + // Parse the LLVM options before we process files in case the file handling + // makes use of things like LLVM_DEBUG(). + parseLLVMOptions(ctx); + + // Handle input files and sectcreate. + for (auto &arg : parsedArgs) { + bool upward; + llvm::Optional resolvedPath; + switch (arg->getOption().getID()) { + default: + continue; + case OPT_INPUT: + addFile(arg->getValue(), ctx, globalWholeArchive, false); + break; + case OPT_upward_library: + addFile(arg->getValue(), ctx, false, true); + break; + case OPT_force_load: + addFile(arg->getValue(), ctx, true, false); + break; + case OPT_l: + case OPT_upward_l: + upward = (arg->getOption().getID() == OPT_upward_l); + resolvedPath = ctx.searchLibrary(arg->getValue()); + if (!resolvedPath) { + error("Unable to find library for " + arg->getSpelling() + + arg->getValue()); + return false; + } else if (ctx.testingFileUsage()) { + message(Twine("Found ") + (upward ? "upward " : " ") + "library " + + canonicalizePath(resolvedPath.getValue())); + } + addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward); + break; + case OPT_framework: + case OPT_upward_framework: + upward = (arg->getOption().getID() == OPT_upward_framework); + resolvedPath = ctx.findPathForFramework(arg->getValue()); + if (!resolvedPath) { + error("Unable to find framework for " + arg->getSpelling() + " " + + arg->getValue()); + return false; + } else if (ctx.testingFileUsage()) { + message(Twine("Found ") + (upward ? "upward " : " ") + "framework " + + canonicalizePath(resolvedPath.getValue())); + } + addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward); + break; + case OPT_filelist: + if (auto ec = loadFileList(arg->getValue(), ctx, globalWholeArchive)) { + handleAllErrors(std::move(ec), [&](const llvm::ErrorInfoBase &EI) { + error(EI.message() + ", processing '-filelist " + arg->getValue()); + }); + return false; + } + break; + case OPT_sectcreate: { + const char* seg = arg->getValue(0); + const char* sect = arg->getValue(1); + const char* fileName = arg->getValue(2); + + ErrorOr> contentOrErr = + MemoryBuffer::getFile(fileName); + + if (!contentOrErr) { + error("can't open -sectcreate file " + Twine(fileName)); + return false; + } + + ctx.addSectCreateSection(seg, sect, std::move(*contentOrErr)); + } + break; + } + } + + if (ctx.getNodes().empty()) { + error("No input files"); + return false; + } + + // Validate the combination of options used. + return ctx.validate(); +} + +static void createFiles(MachOLinkingContext &ctx, bool Implicit) { + std::vector> Files; + if (Implicit) + ctx.createImplicitFiles(Files); + else + ctx.createInternalFiles(Files); + for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) { + auto &members = ctx.getNodes(); + members.insert(members.begin(), llvm::make_unique(std::move(*i))); + } +} + +/// This is where the link is actually performed. +bool link(llvm::ArrayRef args, bool CanExitEarly, + raw_ostream &Error) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorLimitExceededMsg = + "too many errors emitted, stopping now (use " + "'-error-limit 0' to see all errors)"; + errorHandler().errorOS = &Error; + errorHandler().exitEarly = CanExitEarly; + errorHandler().colorDiagnostics = Error.has_colors(); + + MachOLinkingContext ctx; + if (!parse(args, ctx)) + return false; + if (ctx.doNothing()) + return true; + if (ctx.getNodes().empty()) + return false; + + for (std::unique_ptr &ie : ctx.getNodes()) + if (FileNode *node = dyn_cast(ie.get())) + node->getFile()->parse(); + + createFiles(ctx, false /* Implicit */); + + // Give target a chance to add files + createFiles(ctx, true /* Implicit */); + + // Give target a chance to postprocess input files. + // Mach-O uses this chance to move all object files before library files. + ctx.finalizeInputFiles(); + + // Do core linking. + ScopedTask resolveTask(getDefaultDomain(), "Resolve"); + Resolver resolver(ctx); + if (!resolver.resolve()) + return false; + SimpleFile *merged = nullptr; + { + std::unique_ptr mergedFile = resolver.resultFile(); + merged = mergedFile.get(); + auto &members = ctx.getNodes(); + members.insert(members.begin(), + llvm::make_unique(std::move(mergedFile))); + } + resolveTask.end(); + + // Run passes on linked atoms. + ScopedTask passTask(getDefaultDomain(), "Passes"); + PassManager pm; + ctx.addPasses(pm); + if (auto ec = pm.runOnFile(*merged)) { + // FIXME: This should be passed to logAllUnhandledErrors but it needs + // to be passed a Twine instead of a string. + *errorHandler().errorOS << "Failed to run passes on file '" + << ctx.outputPath() << "': "; + logAllUnhandledErrors(std::move(ec), *errorHandler().errorOS, + std::string()); + return false; + } + + passTask.end(); + + // Give linked atoms to Writer to generate output file. + ScopedTask writeTask(getDefaultDomain(), "Write"); + if (auto ec = ctx.writeFile(*merged)) { + // FIXME: This should be passed to logAllUnhandledErrors but it needs + // to be passed a Twine instead of a string. + *errorHandler().errorOS << "Failed to write file '" << ctx.outputPath() + << "': "; + logAllUnhandledErrors(std::move(ec), *errorHandler().errorOS, + std::string()); + return false; + } + + // Call exit() if we can to avoid calling destructors. + if (CanExitEarly) + exitLld(errorCount() ? 1 : 0); + + + return true; +} + +} // end namespace mach_o +} // end namespace lld Property changes on: vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdDriver.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Driver/CMakeLists.txt =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Driver/CMakeLists.txt (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Driver/CMakeLists.txt (revision 352529) @@ -0,0 +1,23 @@ +set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td) +tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs) +add_public_tablegen_target(DriverOptionsTableGen) + +add_lld_library(lldDriver + DarwinLdDriver.cpp + + ADDITIONAL_HEADER_DIRS + ${LLD_INCLUDE_DIR}/lld/Driver + + LINK_COMPONENTS + Option + Support + + LINK_LIBS + lldCommon + lldCore + lldMachO + lldReaderWriter + lldYAML + ) + +add_dependencies(lldDriver DriverOptionsTableGen) Property changes on: vendor/lld/lld-release_900-r372316/lib/Driver/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdOptions.td =================================================================== --- vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdOptions.td (nonexistent) +++ vendor/lld/lld-release_900-r372316/lib/Driver/DarwinLdOptions.td (revision 352529) @@ -0,0 +1,250 @@ +include "llvm/Option/OptParser.td" + + +// output kinds +def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">; +def relocatable : Flag<["-"], "r">, + HelpText<"Create relocatable object file">, Group; +def static : Flag<["-"], "static">, + HelpText<"Create static executable">, Group; +def dynamic : Flag<["-"], "dynamic">, + HelpText<"Create dynamic executable (default)">,Group; +def dylib : Flag<["-"], "dylib">, + HelpText<"Create dynamic library">, Group; +def bundle : Flag<["-"], "bundle">, + HelpText<"Create dynamic bundle">, Group; +def execute : Flag<["-"], "execute">, + HelpText<"Create main executable (default)">, Group; +def preload : Flag<["-"], "preload">, + HelpText<"Create binary for use with embedded systems">, Group; + +// optimizations +def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">; +def dead_strip : Flag<["-"], "dead_strip">, + HelpText<"Remove unreference code and data">, Group; +def macosx_version_min : Separate<["-"], "macosx_version_min">, + MetaVarName<"">, + HelpText<"Minimum Mac OS X version">, Group; +def ios_version_min : Separate<["-"], "ios_version_min">, + MetaVarName<"">, + HelpText<"Minimum iOS version">, Group; +def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">, + Alias; +def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">, + MetaVarName<"">, + HelpText<"Minimum iOS simulator version">, Group; +def sdk_version : Separate<["-"], "sdk_version">, + MetaVarName<"">, + HelpText<"SDK version">, Group; +def source_version : Separate<["-"], "source_version">, + MetaVarName<"">, + HelpText<"Source version">, Group; +def version_load_command : Flag<["-"], "version_load_command">, + HelpText<"Force generation of a version load command">, Group; +def no_version_load_command : Flag<["-"], "no_version_load_command">, + HelpText<"Disable generation of a version load command">, Group; +def function_starts : Flag<["-"], "function_starts">, + HelpText<"Force generation of a function starts load command">, + Group; +def no_function_starts : Flag<["-"], "no_function_starts">, + HelpText<"Disable generation of a function starts load command">, + Group; +def data_in_code_info : Flag<["-"], "data_in_code_info">, + HelpText<"Force generation of a data in code load command">, + Group; +def no_data_in_code_info : Flag<["-"], "no_data_in_code_info">, + HelpText<"Disable generation of a data in code load command">, + Group; +def mllvm : Separate<["-"], "mllvm">, + MetaVarName<"