diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -913,84 +913,71 @@ } } +// Write the CMSE import library to disk. +// The CMSE import library is a relocatable object with only a symbol table. +// The symbols are copies of the (absolute) symbols of the secure gateways +// in the executable output by this link. +// See ArmĀ® v8-M Security Extensions: Requirements on Development Tools +// https://developer.arm.com/documentation/ecm0359818/latest template void elf::writeARMCmseImportLib() { - if (config->emachine != EM_ARM) + if ((in.armCmseSGSection->getSize() == 0) || config->cmseOutputLib.empty()) return; - if (in.armCmseSGSection->getSize() == 0) - return; - - if (config->out_implib.empty()) - return; - - SmallVector inputSections; - SmallVector outputSections; + StringTableSection *IsShStrTab = + make(".shstrtab", /*dynamic=*/false); + StringTableSection *IsStrTab = + make(".strtab", /*dynamic=*/false); + SymbolTableBaseSection *IsSymTab = make>(*IsStrTab); - StringTableSection *shStrTab = make(".shstrtab", false); - StringTableSection *strTab = make(".strtab", false); - SymbolTableBaseSection *symTab = make>(*strTab); - SymtabShndxSection *symTabShndx = make(); + SmallVector, 0> OsIsPairs; + OsIsPairs.push_back( + std::make_pair(make(IsSymTab->name, 0, 0), IsSymTab)); + OsIsPairs.push_back( + std::make_pair(make(IsStrTab->name, 0, 0), IsStrTab)); + OsIsPairs.push_back( + std::make_pair(make(IsShStrTab->name, 0, 0), IsShStrTab)); - inputSections.push_back(symTab); - inputSections.push_back(symTabShndx); - inputSections.push_back(shStrTab); - inputSections.push_back(strTab); - - OutputSection *elfHeader1 = make(symTab->name, 0, 0); - OutputSection *elfHeader2 = make(strTab->name, 0, 0); - OutputSection *elfHeader3 = make(shStrTab->name, 0, 0); - OutputSection *elfHeader4 = make(symTabShndx->name, 0, 0); - - auto recordInputSections = [](OutputSection *osec, InputSectionBase *isec) { + for (auto &[osec, isec] : OsIsPairs) { osec->recordSection(isec); osec->finalizeInputSections(); - }; - recordInputSections(elfHeader1, symTab); - recordInputSections(elfHeader2, strTab); - recordInputSections(elfHeader3, shStrTab); - recordInputSections(elfHeader4, symTabShndx); - - in.armCmseSGSection->exportEntries(symTab); + } - auto addOutputSection = [&](OutputSection &osec) { - outputSections.push_back(&osec); - osec.sectionIndex = outputSections.size(); - osec.shName = shStrTab->addString(osec.name); - }; - addOutputSection(*elfHeader1); - addOutputSection(*elfHeader2); - addOutputSection(*elfHeader3); - addOutputSection(*elfHeader4); + // Copy the secure gateway entry symbols in .gnu.sgstubsto the symbol table. + in.armCmseSGSection->exportEntries(IsSymTab); - elfHeader1->size = symTab->getSize(); - elfHeader2->size = strTab->getSize(); - elfHeader3->size = shStrTab->getSize(); - elfHeader4->size = symTabShndx->getSize(); + // Assign section indices and record section names in .shstrtab + size_t idx = 0; + for (auto &[osec, _] : OsIsPairs) { + osec->sectionIndex = ++idx; + osec->shName = IsShStrTab->addString(osec->name); + } - for (SyntheticSection *isec : inputSections) + for (auto &[osec, isec] : OsIsPairs) { + osec->size = isec->getSize(); isec->finalizeContents(); + } uint64_t off = sizeof(typename ELFT::Ehdr); // Assign file offsets to output sections. - for (OutputSection *osec : outputSections) { - osec->offset = alignToPowerOf2(off, osec->alignment); + for (auto &[osec, _] : OsIsPairs) { + osec->offset = alignToPowerOf2(off, osec->addralign); off = osec->offset + osec->size; } uint64_t sectionHeaderOff = alignToPowerOf2(off, config->wordsize); - uint64_t fileSize = sectionHeaderOff + - (outputSections.size() + 1) * sizeof(typename ELFT::Shdr); + auto shnum = OsIsPairs.size() + 1; + uint64_t fileSize = sectionHeaderOff + shnum * sizeof(typename ELFT::Shdr); - unlinkAsync(config->out_implib); + unlinkAsync(config->cmseOutputLib); unsigned flags = 0; if (!config->mmapOutputFile) flags |= FileOutputBuffer::F_no_mmap; Expected> bufferOrErr = - FileOutputBuffer::create(config->out_implib, fileSize, flags); + FileOutputBuffer::create(config->cmseOutputLib, fileSize, flags); if (!bufferOrErr) { - error("failed to open " + config->out_implib + ": " + + error("failed to open " + config->cmseOutputLib + ": " + llvm::toString(bufferOrErr.takeError())); return; } @@ -1000,6 +987,7 @@ alignToPowerOf2(off, config->wordsize); auto *eHdr = reinterpret_cast(buf); + eHdr->e_type = ET_REL; eHdr->e_entry = 0; eHdr->e_shoff = sectionHeaderOff; @@ -1007,34 +995,23 @@ Partition part; elf::writeEhdr(buf, part); // 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(buf + eHdr->e_shoff); - // TODO: eHdr->e_shnum = outputSections.size() + 1 gives warning; - // unable to get the associated symbol table for SHT_SYMTAB_SHNDX section with - // index 4: sh_link (5) is greater than or equal to the total number of - // sections (5). Find out why. - eHdr->e_shnum = outputSections.size(); - eHdr->e_shstrndx = elfHeader3->sectionIndex; + eHdr->e_shnum = shnum; + eHdr->e_shstrndx = IsShStrTab->getParent()->sectionIndex; - for (OutputSection *osec : outputSections) + for (auto &[osec, _] : OsIsPairs) osec->writeHeaderTo(++sHdrs); // Write section contents to a mmap'ed file. - parallel::TaskGroup tg; - - elfHeader1->writeTo(buf + elfHeader1->offset, tg); - elfHeader2->writeTo(buf + elfHeader2->offset, tg); - elfHeader3->writeTo(buf + elfHeader3->offset, tg); - elfHeader4->writeTo(buf + elfHeader4->offset, tg); + { + parallel::TaskGroup tg; + for (auto &[osec, _] : OsIsPairs) + osec->writeTo(buf + osec->offset, tg); + } - errorToErrorCode(buffer->commit()); + if (auto e = buffer->commit()) + fatal("failed to write output '" + buffer->getPath() + + "': " + toString(std::move(e))); } template void elf::writeARMCmseImportLib(); diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -166,8 +166,8 @@ llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; llvm::StringRef whyExtract; - llvm::StringRef in_implib; - llvm::StringRef out_implib; + llvm::StringRef cmseInputLib; + llvm::StringRef cmseOutputLib; StringRef zBtiReport = "none"; StringRef zCetReport = "none"; llvm::StringRef ltoBasicBlockSections; @@ -188,12 +188,13 @@ llvm::MapVector, uint64_t> callGraphProfile; - bool cmse_implib = false; + bool cmseImplib = false; bool allowMultipleDefinition; bool androidPackDynRelocs = false; bool armHasBlx = false; bool armHasMovtMovw = false; bool armJ1J2BranchEncoding = false; + bool armCMSESupport = false; bool asNeeded = false; BsymbolicKind bsymbolic = BsymbolicKind::None; bool callGraphProfileSort; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -340,13 +340,13 @@ error("the .gnu.hash section is not compatible with the MIPS target"); if (config->emachine != EM_ARM) { - if (config->cmse_implib) + if (config->cmseImplib) error("--cmse-implib is only supported on ARM targets"); - if (!config->in_implib.empty()) + if (!config->cmseInputLib.empty()) error("--in-implib is only supported on ARM targets"); - if (!config->out_implib.empty()) + if (!config->cmseOutputLib.empty()) error("--out-implib is only supported on ARM targets"); } @@ -1124,9 +1124,9 @@ config->fini = args.getLastArgValue(OPT_fini, "_fini"); config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) && !args.hasArg(OPT_relocatable); - config->cmse_implib = args.hasArg(OPT_cmse_implib); - config->in_implib = args.getLastArgValue(OPT_in_implib); - config->out_implib = args.getLastArgValue(OPT_out_implib); + config->cmseImplib = args.hasArg(OPT_cmse_implib); + config->cmseInputLib = args.getLastArgValue(OPT_in_implib); + config->cmseOutputLib = args.getLastArgValue(OPT_out_implib); config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable); config->fortranCommon = @@ -2351,6 +2351,97 @@ } } +// Check symbol attributes of the acle_se_sym, sym pair. +// Both symbols should be global/weak Thumb code symbol definitions. +static bool checkCmseSymAttributes(Symbol *acle_se_sym, Symbol *sym) { + Defined *a = dyn_cast_or_null(acle_se_sym); + if (!a) { + error(toString(acle_se_sym->file) + ": cmse special symbol '" + + acle_se_sym->getName() + "'; undefined symbol"); + return false; + } + + if (!a->isFunc() || (a->value & 1) != 1) { + error(toString(a->file) + ": cmse special symbol '" + a->getName() + + "'; is not a Thumb function definition"); + return false; + } + + if (a->section == nullptr) { + error(toString(a->file) + ": cmse special symbol '" + a->getName() + + "'; cannot be an absolute symbol"); + return false; + } + + Defined *s = dyn_cast_or_null(sym); + if (!s) { + error(toString(s->file) + ": cmse entry function '" + s->getName() + + "'; is an undefined symbol"); + return false; + } + + if (!s->isFunc() || (s->value & 1) != 1) { + error(toString(s->file) + ": cmse entry function '" + s->getName() + + "'; is not a Thumb function definition"); + return false; + } + + if (s->section == nullptr) { + error(toString(s->file) + ": cmse entry function '" + s->getName() + + "'; cannot be an absolute symbol"); + return false; + } + + return true; +} + +// Cortex-M Security Extensions. Prefix for functions that should be exported +// for the non-secure world. +#define ACLESESYM_PREFIX "__acle_se_" + +// Look for [acle_se_, ] pairs, as specified in the Cortex-M Security +// Extensions specification. +// 1) : A standard function name. +// 2) acle_se_ : A special symbol that prefixes the standard function name +// with __acle_se_. +// Both these symbols are Thumb function symbols with external linkage. +// may be redefined in .gnu.sgstubs. +static void processArmCmseSymbol() { + if (!config->cmseImplib) + return; + + // Only symbols with external linkage end up in symtab, so no need to do + // linkage checks. Only check symbol type. + for (Symbol *acle_se_sym : symtab.getSymbols()) { + if (!acle_se_sym->getName().startswith(ACLESESYM_PREFIX)) + continue; + + if (!config->armCMSESupport) + error("cmse special symbol '" + acle_se_sym->getName() + + "' only allowed for ARMv8-M architecture or later"); + + // Try to find the associated symbol definition. + // Symbol must have external linkage. + StringRef name = acle_se_sym->getName(); + name.consume_front(ACLESESYM_PREFIX); + Symbol *sym = symtab.find(name); + + if (!sym) { + error(toString(acle_se_sym->file) + ": cmse special symbol '" + + acle_se_sym->getName() + + "' detected, but no associated entry function definition '" + name + + "' with external linkage found"); + continue; + } + + if (!checkCmseSymAttributes(acle_se_sym, sym)) + continue; + + // may be redefined later in the link in .gnu.sgstubs + symtab.addCmseSymPair(acle_se_sym, sym); + } +} + // Do renaming for --wrap and foo@v1 by updating pointers to symbols. // // When this function is executed, only InputFiles and symbol table @@ -2719,6 +2810,9 @@ if (args.hasArg(OPT_exclude_libs)) excludeLibs(args); + // Process CMSE symbols + processArmCmseSymbol(); + // Apply symbol renames for --wrap and combine foo@v1 and foo@@v1. redirectSymbols(wrapped); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -177,6 +177,10 @@ config->armHasMovtMovw = true; break; } + + // Only ARMv8-M or later architectures have CMSE support. + if (arch >= ARMBuildAttrs::CPUArch::v8_M_Base) + config->armCMSESupport = true; } InputFile::InputFile(Kind k, MemoryBufferRef m) @@ -527,7 +531,7 @@ return; } - if (toString(this) == config->in_implib) { + if (toString(this) == config->cmseInputLib) { initializeJustSymbols(); importCmseSymbols(obj); return; @@ -1013,18 +1017,6 @@ return makeThreadLocal(*this, sec, name); } -// Cortex-M Security Extensions. Prefix for functions that should be exported -// for the non-secure world. -#define ACLESESYM_PREFIX "__acle_se_" -#define ACLESESYM_PREFIX_LEN 10 -bool isACLESESymbolName(llvm::StringRef name) { - return name.startswith(ACLESESYM_PREFIX); -} - -llvm::StringRef getNameFromACLESE(llvm::StringRef name) { - return name.drop_front(ACLESESYM_PREFIX_LEN); -} - // Initialize this->Symbols. this->Symbols is a parallel array as // its corresponding ELF symbol table. template @@ -1094,12 +1086,9 @@ template void ObjFile::importCmseSymbols(const object::ELFFile &obj) { ArrayRef eSyms = this->getELFSyms(); - if (numSymbols == 0) { - numSymbols = eSyms.size(); - symbols = std::make_unique(numSymbols); - } + numSymbols = eSyms.size(); + symbols = std::make_unique(numSymbols); - // Some entries have been filled by LazyObjFile. for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; Defined *sym = reinterpret_cast(make()); @@ -1156,61 +1145,12 @@ } } -static void processArmCmseSymbol(Symbol &sym, uint32_t secIdx) { - if (!(config->emachine == EM_ARM)) - return; - - if (!isACLESESymbolName(sym.getName())) - return; - - StringRef ext_name = getNameFromACLESE(sym.getName()); - // Try to find the associated symbol. Search only in global symbols - // because the specification requires to produce output library with - // this symbol having global visibility. - Symbol *ext_sym = symtab.find(ext_name); - if (ext_sym == NULL || !ext_sym->isDefined()) { - error("CMSE symbol " + sym.getName() + " detected in " + - toString(sym.file) + " but no associated global symbol definition " + - ext_name + " found."); - return; - } - auto &d_sym = cast(sym); - auto &d_extsym = cast(*ext_sym); - - if (secIdx == SHN_ABS) { - // It is not possible to create a veneer and point the - // associated symbol to it if the associated symbol is already - // absolute. - if (d_sym.value == d_extsym.value) - error("CMSE symbol " + ext_sym->getName() + " in " + - toString(ext_sym->file) + - " is absolute and cannot be changed to point to a new secure " - "gateway veneer."); - // Values are different, nothing to do. - return; - } - - // Validate both symbols. - if (!(d_sym.isFunc() && (d_sym.value & 0x1))) { - error("CMSE symbol " + sym.getName() + " in " + toString(sym.file) + - " is not a Thumb code symbol."); - return; - } - - if (!(d_extsym.isFunc() && (d_extsym.value & 0x1))) { - error("CMSE symbol " + ext_sym->getName() + " in " + - toString(ext_sym->file) + " is not a Thumb code symbol."); - return; - } - // will be redefined later in the link in .gnu.sgstubs - symtab.addCmseSymPair(&sym, ext_sym); -} // Called after all ObjFile::parse is called for all ObjFiles. This checks // duplicate symbols and may do symbol property merge in the future. template void ObjFile::postParse() { static std::mutex mu; ArrayRef eSyms = this->getELFSyms(); - if (toString(this) == config->in_implib) + if (toString(this) == config->cmseInputLib) return; for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { @@ -1219,8 +1159,6 @@ uint32_t secIdx = eSym.st_shndx; uint8_t binding = eSym.getBinding(); - processArmCmseSymbol(sym, secIdx); - if (LLVM_UNLIKELY(binding != STB_GLOBAL && binding != STB_WEAK && binding != STB_GNU_UNIQUE)) errorOrWarn(toString(this) + ": symbol (" + Twine(i) + diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -315,6 +315,7 @@ void addOrphanSections(); void diagnoseOrphanHandling() const; + void diagnoseMissingSGVeneerAddress() const; void adjustOutputSections(); void adjustSectionsAfterSorting(); diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -900,6 +900,31 @@ } } +void LinkerScript::diagnoseMissingSGVeneerAddress() const { + if (!config->cmseImplib) + return; + + auto inSectionStart = [&]() { + return config->sectionStartMap.find(".gnu.sgstubs") != + config->sectionStartMap.end(); + }; + + auto inLinkerScript = [&]() { + for (const InputSectionBase *sec : orphanSections) + if (getOutputSectionName(sec) == ".gnu.sgstubs") + return false; + return true; + }; + + auto cmdline = inSectionStart(); + auto linkerscript = inLinkerScript(); + if (cmdline && linkerscript) + warn("addresses assigned to the veneers output section .gnu.sgstubs via " + "--section-start and linker script may potentially clash"); + if (!cmdline && !linkerscript) + error("no address assigned to the veneers output section .gnu.sgstubs"); +} + // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned in the first member of the pair. Otherwise, a nullptr is returned. diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -80,12 +80,12 @@ def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">; -def cmse_implib: F<"cmse-implib">, HelpText<"Make import library to be a secure gateway import">; +def cmse_implib: FF<"cmse-implib">, HelpText<"Make import library to be a secure gateway import">; -defm in_implib: Eq<"in-implib", "Import library whose symbols address must remain stable">, +defm in_implib: EEq<"in-implib", "Import library whose symbols address must remain stable">, MetaVarName<"">; -defm out_implib: Eq<"out-implib", "Generate import library">, +defm out_implib: EEq<"out-implib", "Generate import library">, MetaVarName<"">; defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -60,9 +60,11 @@ // is used to uniquify them. llvm::DenseMap comdatGroups; - bool addCmseSymPair(Symbol *acle_sg_sym, Symbol *sym); - llvm::MapVector cmseSymMap; + void addCmseSymPair(Symbol *acle_se_sym, Symbol *sym); + // The list of __acle_se_, pairs found in the input objects. SmallVector, 0> cmseSymVector; + // Map of symbols defined in the CMSE import library. The linker must preserve + // the addresses in the output objects. llvm::StringMap cmseImportLib; private: diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -99,13 +99,8 @@ return sym; } -// Find an existing symbol or create a new one. -bool SymbolTable::addCmseSymPair(Symbol *acle_sg_sym, Symbol *sym) { - auto p = cmseSymMap.insert(std::make_pair(acle_sg_sym, sym)); - if (p.second) { - cmseSymVector.push_back(std::make_pair(acle_sg_sym, sym)); - } - return p.second; +void SymbolTable::addCmseSymPair(Symbol *acle_se_sym, Symbol *sym) { + cmseSymVector.push_back(std::make_pair(acle_se_sym, sym)); } // This variant of addSymbol is used by BinaryFile::parse to check duplicate diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1219,6 +1219,7 @@ void writeTo(uint8_t *buf) override; size_t getSize() const override; }; + class ArmCmseSGVeneer : public SyntheticSection { public: enum Kind { Unknown, FixedAddress, VarAddress }; @@ -1226,25 +1227,23 @@ Kind kind() const { return (Kind)veneerKind; } uint8_t veneerKind; - ArmCmseSGVeneer(Symbol *sym, Symbol *acle_sg_sym, Kind kind) + ArmCmseSGVeneer(Symbol *sym, Symbol *acle_se_sym, Kind kind) : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, llvm::ELF::SHT_PROGBITS, /*alignment=*/32, ".gnu.sgstubs"), - veneerKind(kind), sym(sym), acle_sg_sym(acle_sg_sym) {} + veneerKind(kind), sym(sym), acle_se_sym(acle_se_sym) {} void writeTo(uint8_t *buf) override; Symbol *sym; - Symbol *acle_sg_sym; + Symbol *acle_se_sym; }; // A Secure Gateway Veneer that is positioned on the address // specified in the CMSE import library. -// The symbol table in the import library should have a size -// specified -class ArmCmseFixedAddressSGVeneer : public ArmCmseSGVeneer { +// The symbol from the import library should have a specified size. +class ArmCmseFixedAddressSGVeneer final : public ArmCmseSGVeneer { public: - ArmCmseFixedAddressSGVeneer(Symbol *sym, Symbol *acle_sg_sym, uint64_t addr) - : ArmCmseSGVeneer(sym, acle_sg_sym, ArmCmseSGVeneer::FixedAddress) { - + ArmCmseFixedAddressSGVeneer(Symbol *sym, Symbol *acle_se_sym, uint64_t addr) + : ArmCmseSGVeneer(sym, acle_se_sym, ArmCmseSGVeneer::FixedAddress) { entsize = 8; entaddr = addr; } @@ -1261,10 +1260,10 @@ // A Secure Gateway Veneer that is positioned out of the range // used by the CMSE import library. // The size is fixed at 8. -class ArmCmseVariableAddressSGVeneer : public ArmCmseSGVeneer { +class ArmCmseVariableAddressSGVeneer final : public ArmCmseSGVeneer { public: - ArmCmseVariableAddressSGVeneer(Symbol *sym, Symbol *acle_sg_sym) - : ArmCmseSGVeneer(sym, acle_sg_sym, ArmCmseSGVeneer::VarAddress) { + ArmCmseVariableAddressSGVeneer(Symbol *sym, Symbol *acle_se_sym) + : ArmCmseSGVeneer(sym, acle_se_sym, ArmCmseSGVeneer::VarAddress) { this->entsize = 8; } size_t getSize() const override { return this->entsize; }; @@ -1280,7 +1279,7 @@ size_t getSize() const override; void writeTo(uint8_t *buf) override; void addSGVeneer(Symbol *sym, Symbol *ext_sym); - void addMappingSymbol(); + inline void addMappingSymbol(); void finalizeContents() override; void exportEntries(SymbolTableBaseSection *symTab); uint64_t impLibMinAddr = -1; @@ -1351,6 +1350,7 @@ std::unique_ptr got; std::unique_ptr gotPlt; std::unique_ptr igotPlt; + std::unique_ptr armCmseSGSection; std::unique_ptr ppc64LongBranchTarget; std::unique_ptr mipsAbiFlags; std::unique_ptr mipsGot; @@ -1369,7 +1369,6 @@ std::unique_ptr strTab; std::unique_ptr symTab; std::unique_ptr symTabShndx; - std::unique_ptr armCmseSGSection; void reset(); }; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -2196,54 +2196,56 @@ llvm::ELF::SHT_PROGBITS, /*alignment=*/32, ".gnu.sgstubs") { this->entsize = 8; - // The range of addresses used in in_implib should be fixed. + // The range of addresses used in the CMSE import library should be fixed. for (auto &p : symtab.cmseImportLib) { Defined *sym = p.second; - if (impLibMinAddr > sym->value) + if (sym->value < impLibMinAddr) impLibMinAddr = sym->value; - if (impLibMaxAddr <= sym->value) { - impLibMaxAddr = sym->value + this->entsize; - } + if (impLibMaxAddr <= sym->value) + impLibMaxAddr = sym->value; } - if (config->emachine == EM_ARM && !symtab.cmseSymVector.empty()) { - addMappingSymbol(); + impLibMaxAddr += this->entsize; - for (const auto &[acle_sg_sym, sym] : symtab.cmseSymVector) - addSGVeneer(acle_sg_sym, sym); + if (!symtab.cmseSymVector.empty()) { + std::sort( + symtab.cmseSymVector.begin(), symtab.cmseSymVector.end(), + [](std::pair a, std::pair b) { + return a.second->getName() < b.second->getName(); + }); + addMappingSymbol(); + for (const auto &[acle_se_sym, sym] : symtab.cmseSymVector) + addSGVeneer(acle_se_sym, sym); } } -void ArmCmseSGSection::addSGVeneer(Symbol *acle_sg_sym, Symbol *sym) { - entries.push_back(std::make_pair(acle_sg_sym, sym)); - if (acle_sg_sym->file != sym->file || - cast(*acle_sg_sym).value != cast(*sym).value) { +void ArmCmseSGSection::addSGVeneer(Symbol *acle_se_sym, Symbol *sym) { + entries.push_back(std::make_pair(acle_se_sym, sym)); + if (acle_se_sym->file != sym->file || + cast(*acle_se_sym).value != cast(*sym).value) { // Symbol addresses different, nothing to do. return; } // Only secure symbols with values equal to that of it's non-secure // counterpart needs to be in the .sg.stubs section. + ArmCmseSGVeneer *ss = nullptr; auto SI = symtab.cmseImportLib.find(sym->getName()); if (SI == symtab.cmseImportLib.end()) { - ArmCmseVariableAddressSGVeneer *ss = - make(sym, acle_sg_sym); - ss->parent = this; - this->sgSections.push_back(ss); - ctx.inputSections.push_back(ss); + ss = make(sym, acle_se_sym); newEntries += 1; } else { Defined *impSym = symtab.cmseImportLib[sym->getName()]; - ArmCmseFixedAddressSGVeneer *ss = - make(sym, acle_sg_sym, impSym->value); - ss->parent = this; - this->sgSections.push_back(ss); - ctx.inputSections.push_back(ss); + ss = make(sym, acle_se_sym, impSym->value); } + ss->parent = this; + this->sgSections.push_back(ss); + ctx.inputSections.push_back(ss); } +// Copy the entry function symbols in .gnu.sgstubs to the import library. void ArmCmseSGSection::exportEntries(SymbolTableBaseSection *symTab) { for (const std::pair &p : entries) { Defined *d = cast(p.second); - symTab->addSymbol(makeDefined(nullptr, d->getName(), STB_GLOBAL, + symTab->addSymbol(makeDefined(nullptr, d->getName(), d->computeBinding(), /*stOther=*/0, STT_FUNC, d->getVA(), d->getSize(), nullptr)); } @@ -2256,8 +2258,8 @@ } } -void ArmCmseSGSection::addMappingSymbol() { - addSyntheticLocal("$t", STT_NOTYPE, /* off */ 0, /* size */ 0, *this); +inline void ArmCmseSGSection::addMappingSymbol() { + addSyntheticLocal("$t", STT_NOTYPE, /*off=*/0, /*size=*/0, *this); } size_t ArmCmseSGSection::getSize() const { @@ -2282,20 +2284,19 @@ if (dyn_cast(ss)) break; ArmCmseFixedAddressSGVeneer *s = dyn_cast(ss); - ss->outSecOff = (s->getAddr() & ~1); + ss->outSecOff = s->getAddr() & ~1; Defined(ss->file, StringRef(), ss->sym->binding, ss->sym->stOther, ss->sym->type, (ss->outSecOff - getVA()) | 1, this->entsize, this) .overwrite(*ss->sym); } - size_t off = (impLibMaxAddr < getVA() ? getVA() : impLibMaxAddr); - off += (newEntries * entsize); + size_t off = (impLibMaxAddr < getVA() ? getVA() : impLibMaxAddr) + + newEntries * entsize; for (ArmCmseSGVeneer *ss : llvm::reverse(this->sgSections)) { if (dyn_cast(ss)) break; - ArmCmseVariableAddressSGVeneer *s = - dyn_cast(ss); + auto *s = dyn_cast(ss); off -= s->entsize; - ss->outSecOff = (off & ~1); + ss->outSecOff = off & ~1; Defined(ss->file, StringRef(), ss->sym->binding, ss->sym->stOther, ss->sym->type, (ss->outSecOff - getVA()) | 1, this->entsize, this) .overwrite(*ss->sym); @@ -2309,7 +2310,7 @@ }; memcpy(buf, data, sizeof(data)); - uint64_t s = acle_sg_sym->getVA(); + uint64_t s = acle_se_sym->getVA(); uint64_t p = getVA() + 4; target->relocateNoSym(buf + 4, R_ARM_THM_JUMP24, s - p - 4); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1991,6 +1991,7 @@ removeUnusedSyntheticSections(); script->diagnoseOrphanHandling(); + script->diagnoseMissingSGVeneerAddress(); sortSections(); diff --git a/lld/test/ELF/Inputs/arm-cmse-implib-1.s b/lld/test/ELF/Inputs/arm-cmse-implib-1.s --- a/lld/test/ELF/Inputs/arm-cmse-implib-1.s +++ b/lld/test/ELF/Inputs/arm-cmse-implib-1.s @@ -1,65 +1,38 @@ .syntax unified .text - .align 2 - .global foo - .global __acle_se_foo - .thumb - .thumb_func - .type foo, %function - .type __acle_se_foo, %function -// foo == __acle_se_foo. So veneer generation expected. -foo: -__acle_se_foo: + .macro cmse_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding + .align 2 + .\sym_binding \sym_name + .\acle_sym_binding __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type + \sym_name: + __acle_se_\sym_name: nop - .size foo, .-foo - .size __acle_se_foo, .-__acle_se_foo + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name + .endm - .align 2 - .global bar - .global __acle_se_bar - .thumb - .thumb_func - .type bar, %function - .type __acle_se_bar, %function -// Same as foo. -bar: -__acle_se_bar: - nop - .size bar, .-bar - .size __acle_se_bar, .-__acle_se_bar - - .align 2 - .global no_veneer1 - .global __acle_se_no_veneer1 - .thumb - .thumb_func - .type no_veneer1, %function - .type __acle_se_no_veneer1, %function -// no_veneer1 != __acle_se_no_veneer1. -// So no veneer generation is needed. -// However no_veneer1 will be imported to the import library. -no_veneer1: + .macro cmse_no_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding + .align 2 + .\sym_binding \sym_name + .\acle_sym_binding __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type + \sym_name: sg -__acle_se_no_veneer1: + __acle_se_\sym_name: nop - .size no_veneer1, .-no_veneer1 - .size __acle_se_no_veneer1, .-__acle_se_no_veneer1 + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name + .endm - .align 2 - .global no_veneer2 - .global __acle_se_no_veneer2 - .thumb - .thumb_func - .type no_veneer2, %function - .type __acle_se_no_veneer2, %function -// Same as no_veneer1. -no_veneer2: - sg -__acle_se_no_veneer2: - nop - .size no_veneer2, .-no_veneer2 - .size __acle_se_no_veneer2, .-__acle_se_no_veneer2 + + cmse_veneer foo, function, global, function, global + cmse_veneer bar, function, weak, function, global + cmse_no_veneer no_veneer1, function, weak, function, global + cmse_no_veneer no_veneer2, function, weak, function, weak .align 2 .global normal_sym diff --git a/lld/test/ELF/Inputs/arm-cmse-implib-2.s b/lld/test/ELF/Inputs/arm-cmse-implib-2.s --- a/lld/test/ELF/Inputs/arm-cmse-implib-2.s +++ b/lld/test/ELF/Inputs/arm-cmse-implib-2.s @@ -1,80 +1,38 @@ .syntax unified .text - .align 2 - .global baz - .global __acle_se_baz - .thumb - .thumb_func - .type baz, %function - .type __acle_se_baz, %function -// baz == __acle_se_baz. So veneer generation expected. -baz: -__acle_se_baz: - nop - .size baz, .-baz - .size __acle_se_baz, .-__acle_se_baz - - .align 2 - .global foo - .global __acle_se_foo - .thumb - .thumb_func - .type foo, %function - .type __acle_se_foo, %function -// Same as baz. -foo: -__acle_se_foo: + .macro cmse_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding + .align 2 + .\sym_binding \sym_name + .\acle_sym_binding __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type + \sym_name: + __acle_se_\sym_name: nop - .size foo, .-foo - .size __acle_se_foo, .-__acle_se_foo + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name + .endm - .align 2 - .global qux - .global __acle_se_qux - .thumb - .thumb_func - .type qux, %function - .type __acle_se_qux, %function -// Same as baz. -qux: -__acle_se_qux: - nop - .size qux, .-qux - .size __acle_se_qux, .-__acle_se_qux - - @ Valid setup for entry function without veneer generation - .align 2 - .global no_veneer1 - .global __acle_se_no_veneer1 - .thumb - .thumb_func - .type no_veneer1, %function - .type __acle_se_no_veneer1, %function -// no_veneer1 != __acle_se_no_veneer1. -// So no veneer generation is needed. -// However no_veneer1 will be imported to the import library. -no_veneer1: + .macro cmse_no_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding + .align 2 + .\sym_binding \sym_name + .\acle_sym_binding __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type + \sym_name: sg -__acle_se_no_veneer1: + __acle_se_\sym_name: nop - .size no_veneer1, .-no_veneer1 - .size __acle_se_no_veneer1, .-__acle_se_no_veneer1 + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name + .endm - .align 2 - .global no_veneer2 - .global __acle_se_no_veneer2 - .thumb - .thumb_func - .type no_veneer2, %function - .type __acle_se_no_veneer2, %function -// Same as no_veneer1. -no_veneer2: - sg -__acle_se_no_veneer2: - nop - .size no_veneer2, .-no_veneer2 - .size __acle_se_no_veneer2, .-__acle_se_no_veneer2 + cmse_veneer baz, function, weak, function, global + cmse_veneer foo, function, global, function, global + cmse_veneer qux, function, global, function, global + cmse_no_veneer no_veneer1, function, weak, function, global + cmse_no_veneer no_veneer2, function, weak, function, weak .align 2 .global normal_sym diff --git a/lld/test/ELF/arm-cmse-check-startaddress.s b/lld/test/ELF/arm-cmse-check-startaddress.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-check-startaddress.s @@ -0,0 +1,59 @@ + +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes --triple=thumbv8m.main -filetype=obj %s -o %t.o +// RUN: echo "SECTIONS { \ +// RUN: .text : { *(.text) } \ +// RUN: .gnu.sgstubs 0x40000 : { *(.gnu.sgstubs*) } \ +// RUN: } " > %twithsgstubs.script +// RUN: echo "SECTIONS { \ +// RUN: .text : { *(.text) } \ +// RUN: } " > %twoutsgstubs.script +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script %twithsgstubs.script %t.o -o %t1 2>&1 | FileCheck %s --check-prefix=WARN +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script %twoutsgstubs.script %t.o -o %t2 2>&1 | FileCheck %s --check-prefix=NOWARN --check-prefix=NOERROR +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t3 2>&1 | FileCheck %s --check-prefix=NOWARN --check-prefix=NOERROR +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --script %twithsgstubs.script %t.o -o %t4 2>&1 | FileCheck %s --check-prefix=NOWARN --check-prefix=NOERROR +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --script %twoutsgstubs.script %t.o -o %t5 2>&1 | FileCheck %s --check-prefix=ERROR + +// RUN: llvm-readobj -S %t1 | FileCheck %s --check-prefix=ADDRCMDLINE +// RUN: llvm-readobj -S %t2 | FileCheck %s --check-prefix=ADDRCMDLINE +// RUN: llvm-readobj -S %t3 | FileCheck %s --check-prefix=ADDRCMDLINE +// RUN: llvm-readobj -S %t4 | FileCheck %s --check-prefix=ADDRLNKSCRIPT + + .align 2 + .global __acle_se_foo + .global foo + .thumb + .thumb_func + .type __acle_se_foo, %function + .thumb + .thumb_func + .type foo, %function +__acle_se_foo: +foo: + nop + .size foo, .-foo + .size __acle_se_foo, .-__acle_se_foo + +// WARN: warning: addresses assigned to the veneers output section .gnu.sgstubs via --section-start and linker script may potentially clash +// NOWARN-NOT: warning: addresses assigned to the veneers output section .gnu.sgstubs via --section-start and linker script may potentially clash + +// ERROR: error: no address assigned to the veneers output section .gnu.sgstubs +// NOERROR-NOT: error: no address assigned to the veneers output section .gnu.sgstubs + +// ADDRCMDLINE: Section { +// ADDRCMDLINE: Name: .gnu.sgstubs +// ADDRCMDLINE-NEXT: Type: SHT_PROGBITS +// ADDRCMDLINE-NEXT: Flags [ +// ADDRCMDLINE-NEXT: SHF_ALLOC +// ADDRCMDLINE-NEXT: SHF_EXECINSTR +// ADDRCMDLINE-NEXT: ] +// ADDRCMDLINE-NEXT: Address: 0x20000 + +// ADDRLNKSCRIPT: Section { +// ADDRLNKSCRIPT: Name: .gnu.sgstubs +// ADDRLNKSCRIPT-NEXT: Type: SHT_PROGBITS +// ADDRLNKSCRIPT-NEXT: Flags [ +// ADDRLNKSCRIPT-NEXT: SHF_ALLOC +// ADDRLNKSCRIPT-NEXT: SHF_EXECINSTR +// ADDRLNKSCRIPT-NEXT: ] +// ADDRLNKSCRIPT-NEXT: Address: 0x40000 diff --git a/lld/test/ELF/arm-cmse-check-sym-attrs.s b/lld/test/ELF/arm-cmse-check-sym-attrs.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-check-sym-attrs.s @@ -0,0 +1,111 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=armv9a %s -o %t1.o +// RUN: not ld.lld --cmse-implib %t1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix ERROR + + .text + .thumb + +.macro cmse_veneer sym_name, sym_type, sym_visibility, acle_sym_type, acle_sym_visibility + .align 2 + .\sym_visibility \sym_name + .\acle_sym_visibility __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type +\sym_name: +__acle_se_\sym_name: + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name +.endm + +/// Valid sequences +/// both sym and __acle_se_sym should be global or weak Thumb code symbols. + cmse_veneer valid_1, function, global, function, global + cmse_veneer valid_2, function, weak, function, weak + cmse_veneer valid_3, function, weak, function, global + cmse_veneer valid_4, function, global, function, weak + +/// Invalid sequences +/// __acle_se_sym is an object + cmse_veneer invalid_1, function, global, object, global + cmse_veneer invalid_2, function, global, object, weak +/// sym is an object + cmse_veneer invalid_3, object, global, function, global + cmse_veneer invalid_4, object, global, function, weak +/// sym is local + cmse_veneer invalid_5, function, local, function, global + cmse_veneer invalid_6, function, local, function, weak + +/// __acle_se_invalid_7 undefined. + .global invalid_7 + .type invalid_7, %function + .global __acle_se_invalid_7 + .thumb_func +invalid_7: + +/// invalid_8 undefined. + .global __acle_se_invalid_8 + .thumb_func +__acle_se_invalid_8: + +// Absolute symbols with same values + .global invalid_9 + .global __acle_se_invalid_9 + .type __acle_se_invalid_9, %function + .type invalid_9, %function +__acle_se_invalid_9=0x1001 +invalid_9=0x1001 + .size invalid_9, 0 + .size __acle_se_invalid_9, 0 + +// Absolute symbols with different values + .align 2 + .global __acle_se_invalid_10 + .global invalid_10 + .type __acle_se_invalid_10, %function + .type invalid_10, %function +__acle_se_invalid_10 = 0x10001 +invalid_10 = 0x10005 + .size invalid_10, 0 + .size __acle_se_invalid_10, 0 + + .section nonthumb + .thumb + .align 2 + .global invalid_11 + .global __acle_se_invalid_11 + .type invalid_11, %function + .type __acle_se_invalid_11, %function +invalid_11: + .arm +/// Invalid non-thumb function symbol __acle_se_invalid_11 +__acle_se_invalid_11: + .size invalid_11, .-invalid_11 + .size __acle_se_invalid_11, .-__acle_se_invalid_11 + + .global invalid_12 + .global __acle_se_invalid_12 + .type invalid_12, %function + .type __acle_se_invalid_12, %function +/// Invalid non-thumb function symbol invalid_12 +invalid_12: + .thumb +__acle_se_invalid_12: + .size invalid_12, .-invalid_12 + .size __acle_se_invalid_12, .-__acle_se_invalid_12 + +// ERROR-NOT: __acle_se_valid_1 +// ERROR-NOT: __acle_se_valid_2 +// ERROR-NOT: __acle_se_valid_3 +// ERROR-NOT: __acle_se_valid_4 +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_1'; is not a Thumb function definition +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_2'; is not a Thumb function definition +// ERROR: error: {{.*}}: cmse entry function 'invalid_3'; is not a Thumb function definition +// ERROR: error: {{.*}}: cmse entry function 'invalid_4'; is not a Thumb function definition +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_5' detected, but no associated entry function definition 'invalid_5' with external linkage found +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_6' detected, but no associated entry function definition 'invalid_6' with external linkage found +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_7'; undefined symbol +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_8' detected, but no associated entry function definition 'invalid_8' with external linkage found +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_9'; cannot be an absolute symbol +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_10'; cannot be an absolute symbol +// ERROR: error: {{.*}}: cmse special symbol '__acle_se_invalid_11'; is not a Thumb function definition +// ERROR: error: {{.*}}: cmse entry function 'invalid_12'; is not a Thumb function definition diff --git a/lld/test/ELF/arm-cmse-error.s b/lld/test/ELF/arm-cmse-error.s deleted file mode 100644 --- a/lld/test/ELF/arm-cmse-error.s +++ /dev/null @@ -1,20 +0,0 @@ -// REQUIRES: arm -// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv7m-none-eabi %s -o %t1.o -// RUN: not ld.lld --cmse-implib %t1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix CHECK-MISSING - - .text - .thumb - - # Missing standard symbol. - .global __acle_se_missing_standard_sym - .thumb_func -__acle_se_missing_standard_sym: - - # External symbol absolute. - .global __acle_se_absolute_sym -__acle_se_absolute_sym=0x1001 - .global absolute_sym -absolute_sym=0x1001 - -// CHECK-MISSING: CMSE symbol __acle_se_missing_standard_sym detected in {{.*\.o}} but no associated global symbol definition missing_standard_sym found. -// CHECK-MISSING: CMSE symbol absolute_sym in {{.*\.o}} is absolute and cannot be changed to point to a new secure gateway veneer. diff --git a/lld/test/ELF/arm-cmse-implib.s b/lld/test/ELF/arm-cmse-implib.s --- a/lld/test/ELF/arm-cmse-implib.s +++ b/lld/test/ELF/arm-cmse-implib.s @@ -1,78 +1,48 @@ // REQUIRES: arm // RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %S/Inputs/arm-cmse-implib-1.s -o %t1.o -/// TODO: Test seg faults when using multiple threads. Workaround by running on a single thread. -// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t1 --out-implib=%t1.lib --cmse-implib %t1.o --threads=1 +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t1 --out-implib=%t1.lib --cmse-implib %t1.o // RUN: llvm-readelf -s %t1 %t1.lib | FileCheck %s --check-prefix=CHECK1 // RUN: llvm-objdump -d %t1 | FileCheck %s --check-prefix=DISS1 // RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %S/Inputs/arm-cmse-implib-2.s -o %t2.o -/// TODO: Test seg faults when using multiple threads. Workaround by running on a single thread. -// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t2 --out-implib=%t2.lib --in-implib=%t1.lib --cmse-implib %t2.o --threads=1 +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t2 --out-implib=%t2.lib --in-implib=%t1.lib --cmse-implib %t2.o // RUN: llvm-readelf -s %t2 %t2.lib | FileCheck %s --check-prefix=CHECK2 // RUN: llvm-objdump -d %t2 | FileCheck %s --check-prefix=DISS2 .syntax unified .text - .align 2 - .global foo - .global __acle_se_foo - .thumb - .thumb_func - .type foo, %function - .type __acle_se_foo, %function -// foo == __acle_se_foo. So veneer generation expected. -foo: -__acle_se_foo: - nop - .size foo, .-foo - .size __acle_se_foo, .-__acle_se_foo - - .align 2 - .global bar - .global __acle_se_bar - .thumb - .thumb_func - .type bar, %function - .type __acle_se_bar, %function -// Same as foo. -bar: -__acle_se_bar: + .macro cmse_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding + .align 2 + .\sym_binding \sym_name + .\acle_sym_binding __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type + \sym_name: + __acle_se_\sym_name: nop - .size bar, .-bar - .size __acle_se_bar, .-__acle_se_bar + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name + .endm - .align 2 - .global no_veneer1 - .global __acle_se_no_veneer1 - .thumb - .thumb_func - .type no_veneer1, %function - .type __acle_se_no_veneer1, %function -// no_veneer1 != __acle_se_no_veneer1. -// So no veneer generation is needed. -// However no_veneer1 will be imported to the import library. -no_veneer1: + .macro cmse_no_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding + .align 2 + .\sym_binding \sym_name + .\acle_sym_binding __acle_se_\sym_name + .type \sym_name, %\sym_type + .type __acle_se_\sym_name, %\acle_sym_type + \sym_name: sg -__acle_se_no_veneer1: + __acle_se_\sym_name: nop - .size no_veneer1, .-no_veneer1 - .size __acle_se_no_veneer1, .-__acle_se_no_veneer1 + .size \sym_name, .-\sym_name + .size __acle_se_\sym_name, .-__acle_se_\sym_name + .endm - .align 2 - .global no_veneer2 - .global __acle_se_no_veneer2 - .thumb - .thumb_func - .type no_veneer2, %function - .type __acle_se_no_veneer2, %function -// Same as no_veneer1. -no_veneer2: - sg -__acle_se_no_veneer2: - nop - .size no_veneer2, .-no_veneer2 - .size __acle_se_no_veneer2, .-__acle_se_no_veneer2 + cmse_veneer foo, function, global, function, global + cmse_veneer bar, function, weak, function, global + cmse_no_veneer no_veneer1, function, weak, function, global + cmse_no_veneer no_veneer2, function, weak, function, weak .align 2 .global normal_sym @@ -89,14 +59,14 @@ // CHECK1-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND // CHECK1-NEXT: 1: 00020000 0 NOTYPE LOCAL DEFAULT 2 $t // CHECK1-NEXT: 2: 00008000 0 NOTYPE LOCAL DEFAULT 1 $t.0 -// CHECK1-NEXT: 3: 00020001 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK1-NEXT: 3: 00020009 8 FUNC GLOBAL DEFAULT 2 foo // CHECK1-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo -// CHECK1-NEXT: 5: 00020009 8 FUNC GLOBAL DEFAULT 2 bar +// CHECK1-NEXT: 5: 00020001 8 FUNC WEAK DEFAULT 2 bar // CHECK1-NEXT: 6: 00008005 2 FUNC GLOBAL DEFAULT 1 __acle_se_bar -// CHECK1-NEXT: 7: 00008009 6 FUNC GLOBAL DEFAULT 1 no_veneer1 +// CHECK1-NEXT: 7: 00008009 6 FUNC WEAK DEFAULT 1 no_veneer1 // CHECK1-NEXT: 8: 0000800d 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 -// CHECK1-NEXT: 9: 00008011 6 FUNC GLOBAL DEFAULT 1 no_veneer2 -// CHECK1-NEXT: 10: 00008015 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer2 +// CHECK1-NEXT: 9: 00008011 6 FUNC WEAK DEFAULT 1 no_veneer2 +// CHECK1-NEXT: 10: 00008015 2 FUNC WEAK DEFAULT 1 __acle_se_no_veneer2 // CHECK1-NEXT: 11: 00008019 2 FUNC GLOBAL DEFAULT 1 normal_sym /// Import library 1 @@ -104,10 +74,10 @@ // CHECK1: Symbol table '.symtab' contains 5 entries: // CHECK1-NEXT: Num: Value Size Type Bind Vis Ndx Name // CHECK1-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND -// CHECK1-NEXT: 1: 00020001 8 FUNC GLOBAL DEFAULT ABS foo -// CHECK1-NEXT: 2: 00020009 8 FUNC GLOBAL DEFAULT ABS bar -// CHECK1-NEXT: 3: 00008009 6 FUNC GLOBAL DEFAULT ABS no_veneer1 -// CHECK1-NEXT: 4: 00008011 6 FUNC GLOBAL DEFAULT ABS no_veneer2 +// CHECK1-NEXT: 1: 00020001 8 FUNC WEAK DEFAULT ABS bar +// CHECK1-NEXT: 2: 00020009 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK1-NEXT: 3: 00008009 6 FUNC WEAK DEFAULT ABS no_veneer1 +// CHECK1-NEXT: 4: 00008011 6 FUNC WEAK DEFAULT ABS no_veneer2 // DISS1: Disassembly of section .text: // DISS1: 00008000 <__acle_se_foo>: @@ -130,12 +100,12 @@ // DISS1-NEXT: 8018: bf00 nop // DISS1: Disassembly of section .gnu.sgstubs: -// DISS1: 00020000 : +// DISS1: 00020000 : // DISS1-NEXT: 20000: e97f e97f sg -// DISS1-NEXT: 20004: f7e7 bffc b.w 0x8000 <__acle_se_foo> -// DISS1: 00020008 : +// DISS1-NEXT: 20004: f7e7 bffe b.w 0x8004 <__acle_se_bar> +// DISS1: 00020008 : // DISS1-NEXT: 20008: e97f e97f sg -// DISS1-NEXT: 2000c: f7e7 bffa b.w 0x8004 <__acle_se_bar> +// DISS1-NEXT: 2000c: f7e7 bff8 b.w 0x8000 <__acle_se_foo> /// Executable 2 // CHECK2: File: @@ -144,30 +114,30 @@ // CHECK2-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND // CHECK2-NEXT: 1: 00020000 0 NOTYPE LOCAL DEFAULT 2 $t // CHECK2-NEXT: 2: 00008000 0 NOTYPE LOCAL DEFAULT 1 $t.0 -// CHECK2-NEXT: 3: 00020011 8 FUNC GLOBAL DEFAULT 2 baz +// CHECK2-NEXT: 3: 00020011 8 FUNC WEAK DEFAULT 2 baz // CHECK2-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 __acle_se_baz -// CHECK2-NEXT: 5: 00020001 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK2-NEXT: 5: 00020009 8 FUNC GLOBAL DEFAULT 2 foo // CHECK2-NEXT: 6: 00008005 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo // CHECK2-NEXT: 7: 00020019 8 FUNC GLOBAL DEFAULT 2 qux // CHECK2-NEXT: 8: 00008009 2 FUNC GLOBAL DEFAULT 1 __acle_se_qux -// CHECK2-NEXT: 9: 0000800d 6 FUNC GLOBAL DEFAULT 1 no_veneer1 +// CHECK2-NEXT: 9: 0000800d 6 FUNC WEAK DEFAULT 1 no_veneer1 // CHECK2-NEXT: 10: 00008011 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 -// CHECK2-NEXT: 11: 00008015 6 FUNC GLOBAL DEFAULT 1 no_veneer2 -// CHECK2-NEXT: 12: 00008019 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer2 +// CHECK2-NEXT: 11: 00008015 6 FUNC WEAK DEFAULT 1 no_veneer2 +// CHECK2-NEXT: 12: 00008019 2 FUNC WEAK DEFAULT 1 __acle_se_no_veneer2 // CHECK2-NEXT: 13: 0000801d 2 FUNC GLOBAL DEFAULT 1 normal_sym -/// Note that foo retains its address from Import library 1 (0x00020001) -/// New entry functions, baz and qux, use addresses not used by Import librar 1. +/// Note that foo retains its address from Import library 1 (0x000020009) +/// New entry functions, baz and qux, use addresses not used by Import library 1. /// Import library 2 // CHECK2: File: // CHECK2: Symbol table '.symtab' contains 6 entries: // CHECK2-NEXT: Num: Value Size Type Bind Vis Ndx Name // CHECK2-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND -// CHECK2-NEXT: 1: 00020011 8 FUNC GLOBAL DEFAULT ABS baz -// CHECK2-NEXT: 2: 00020001 8 FUNC GLOBAL DEFAULT ABS foo -// CHECK2-NEXT: 3: 00020019 8 FUNC GLOBAL DEFAULT ABS qux -// CHECK2-NEXT: 4: 0000800d 6 FUNC GLOBAL DEFAULT ABS no_veneer1 -// CHECK2-NEXT: 5: 00008015 6 FUNC GLOBAL DEFAULT ABS no_veneer2 +// CHECK2-NEXT: 1: 00020011 8 FUNC WEAK DEFAULT ABS baz +// CHECK2-NEXT: 2: 00020009 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK2-NEXT: 3: 0000800d 6 FUNC WEAK DEFAULT ABS no_veneer1 +// CHECK2-NEXT: 4: 00008015 6 FUNC WEAK DEFAULT ABS no_veneer2 +// CHECK2-NEXT: 5: 00020019 8 FUNC GLOBAL DEFAULT ABS qux // DISS2: Disassembly of section .text: // DISS2: 00008000 <__acle_se_baz>: @@ -193,9 +163,9 @@ // DISS2-NEXT: 801c: bf00 nop // DISS2: Disassembly of section .gnu.sgstubs: -// DISS2: 00020000 : -// DISS2-NEXT: 20000: e97f e97f sg -// DISS2-NEXT: 20004: f7e7 bffe b.w 0x8004 <__acle_se_foo> +// DISS2: 00020008 : +// DISS2-NEXT: 20008: e97f e97f sg +// DISS2-NEXT: 2000c: f7e7 bffa b.w 0x8004 <__acle_se_foo> // DISS2: 00020010 : // DISS2-NEXT: 20010: e97f e97f sg // DISS2-NEXT: 20014: f7e7 bff4 b.w 0x8000 <__acle_se_baz> diff --git a/lld/test/ELF/arm-cmse-invalid-arch.s b/lld/test/ELF/arm-cmse-invalid-arch.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-invalid-arch.s @@ -0,0 +1,41 @@ +/// CMSE Only supported by ARMv8-M architecture or later. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8m.base %s -o %t1.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t1 %t1.o + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8m.main %s -o %t2.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t2 %t2.o + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8.1m.main %s -o %t3.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t3 %t3.o + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv9a %s -o %t4.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t4 %t4.o + +/// Expect errors for other architectures +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv7-m %s -o %t5.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t5 %t5.o 2>&1 | FileCheck %s --check-prefix=ERROR + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv8-m %s -o %t6.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t6 %t6.o 2>&1 | FileCheck %s --check-prefix=ERROR + +/// Invalid triple defaults to v4T. Error. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumb %s -o %t7.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t7 %t7.o 2>&1 | FileCheck %s --check-prefix=ERROR + +/// No build attributes. Error. +// RUN: llvm-mc -filetype=obj -triple=thumb %s -o %t8.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t8 %t8.o 2>&1 | FileCheck %s --check-prefix=ERROR + + .text + .thumb + .type entry, %function + .global entry + .type __acle_se_entry, %function + .global __acle_se_entry +entry: +__acle_se_entry: + nop + .size entry, .-entry + .size __acle_se_entry, .-__acle_se_entry + +// ERROR: cmse special symbol '__acle_se_entry' only allowed for ARMv8-M architecture or later diff --git a/lld/test/ELF/arm-cmse-veneers.s b/lld/test/ELF/arm-cmse-veneers.s --- a/lld/test/ELF/arm-cmse-veneers.s +++ b/lld/test/ELF/arm-cmse-veneers.s @@ -1,7 +1,7 @@ // REQUIRES: arm -// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv7m-none-eabi %s -o %t.o -// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %s -o %t.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t // RUN: llvm-objdump -d %t | FileCheck %s .align 2 @@ -64,16 +64,6 @@ .size weak_qux, .-weak_qux .size __acle_se_weak_qux, .-__acle_se_weak_qux - .align 2 - .global __acle_se_absolute_foobar - .global absolute_foobar - .type __acle_se_absolute_foobar, %function - .type absolute_foobar, %function -__acle_se_absolute_foobar = 0x10000 -absolute_foobar = 0x10004 - .size absolute_foobar, 0 - .size __acle_se_absolute_foobar, 0 - // CHECK: Disassembly of section .text: // CHECK: 00008000 <__acle_se_global_foo> // CHECK: 00008004 <__acle_se_weak_bar> @@ -82,18 +72,18 @@ // CHECK: Disassembly of section .gnu.sgstubs: -// CHECK: 00020000 -// CHECK: 20000: e97f e97f -// CHECK: 20004: f7e7 bffc b.w 0x8000 <__acle_se_global_foo> +// CHECK: 00020000 +// CHECK-NEXT: 20000: e97f e97f sg +// CHECK-NEXT: 20004: f7e8 b800 b.w 0x8008 <__acle_se_global_baz> -// CHECK: 00020008 -// CHECK: 20008: e97f e97f -// CHECK: 2000c: f7e7 bffa b.w 0x8004 <__acle_se_weak_bar> +// CHECK: 00020008 +// CHECK-NEXT: 20008: e97f e97f sg +// CHECK-NEXT: 2000c: f7e7 bff8 b.w 0x8000 <__acle_se_global_foo> -// CHECK: 00020010 -// CHECK: 20010: e97f e97f -// CHECK: 20014: f7e7 bff8 b.w 0x8008 <__acle_se_global_baz> +// CHECK: 00020010 +// CHECK-NEXT: 20010: e97f e97f sg +// CHECK-NEXT: 20014: f7e7 bff6 b.w 0x8004 <__acle_se_weak_bar> -// CHECK: 00020018 -// CHECK: 20018: e97f e97f -// CHECK: 2001c: f7e7 bff6 b.w 0x800c <__acle_se_weak_qux> +// CHECK: 00020018 +// CHECK-NEXT: 20018: e97f e97f sg +// CHECK-NEXT: 2001c: f7e7 bff6 b.w 0x800c <__acle_se_weak_qux>