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 @@ -6,10 +6,12 @@ // //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" @@ -18,6 +20,7 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; +using namespace llvm::object; namespace { class ARM final : public TargetInfo { @@ -912,6 +915,112 @@ } } +// 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 ((in.armCmseSGSection->getSize() == 0) || config->cmseOutputLib.empty()) + return; + + StringTableSection *shstrtab = + make(".shstrtab", /*dynamic=*/false); + StringTableSection *strtab = + make(".strtab", /*dynamic=*/false); + SymbolTableBaseSection *symtab = make>(*strtab); + + SmallVector, 0> osIsPairs; + osIsPairs.push_back( + std::make_pair(make(symtab->name, 0, 0), symtab)); + osIsPairs.push_back( + std::make_pair(make(strtab->name, 0, 0), strtab)); + osIsPairs.push_back( + std::make_pair(make(shstrtab->name, 0, 0), shstrtab)); + + for (auto &[osec, isec] : osIsPairs) { + osec->recordSection(isec); + osec->finalizeInputSections(); + } + + // Copy the secure gateway entry symbols in .gnu.sgstubsto the symbol table. + in.armCmseSGSection->exportEntries(symtab); + + // Assign section indices and record section names in .shstrtab + size_t idx = 0; + for (auto &[osec, _] : osIsPairs) { + osec->sectionIndex = ++idx; + osec->shName = shstrtab->addString(osec->name); + } + + 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 (auto &[osec, _] : osIsPairs) { + osec->offset = alignToPowerOf2(off, osec->addralign); + off = osec->offset + osec->size; + } + + uint64_t sectionHeaderOff = alignToPowerOf2(off, config->wordsize); + auto shnum = osIsPairs.size() + 1; + uint64_t fileSize = sectionHeaderOff + shnum * sizeof(typename ELFT::Shdr); + + unlinkAsync(config->cmseOutputLib); + unsigned flags = 0; + if (!config->mmapOutputFile) + flags |= FileOutputBuffer::F_no_mmap; + + Expected> bufferOrErr = + FileOutputBuffer::create(config->cmseOutputLib, fileSize, flags); + + if (!bufferOrErr) { + error("failed to open " + config->cmseOutputLib + ": " + + llvm::toString(bufferOrErr.takeError())); + return; + } + + std::unique_ptr &buffer = *bufferOrErr; + uint8_t *buf = buffer->getBufferStart(); + + alignToPowerOf2(off, config->wordsize); + auto *eHdr = reinterpret_cast(buf); + + eHdr->e_type = ET_REL; + eHdr->e_entry = 0; + eHdr->e_shoff = sectionHeaderOff; + + Partition part; + elf::writeEhdr(buf, part); + // Write the section header table. + auto *sHdrs = reinterpret_cast(buf + eHdr->e_shoff); + eHdr->e_shnum = shnum; + eHdr->e_shstrndx = shstrtab->getParent()->sectionIndex; + + for (auto &[osec, _] : osIsPairs) + osec->writeHeaderTo(++sHdrs); + + // Write section contents to a mmap'ed file. + { + parallel::TaskGroup tg; + for (auto &[osec, _] : osIsPairs) + osec->writeTo(buf + osec->offset, tg); + } + + if (auto e = buffer->commit()) + fatal("failed to write output '" + buffer->getPath() + + "': " + toString(std::move(e))); +} + +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); + TargetInfo *elf::getARMTargetInfo() { static ARM target; return ⌖ diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -166,6 +166,8 @@ llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; llvm::StringRef whyExtract; + llvm::StringRef cmseInputLib; + llvm::StringRef cmseOutputLib; StringRef zBtiReport = "none"; StringRef zCetReport = "none"; llvm::StringRef ltoBasicBlockSections; @@ -186,11 +188,13 @@ llvm::MapVector, uint64_t> callGraphProfile; + 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,6 +340,17 @@ if (config->emachine == EM_MIPS && config->gnuHash) error("the .gnu.hash section is not compatible with the MIPS target"); + if (config->emachine != EM_ARM) { + if (config->cmseImplib) + error("--cmse-implib is only supported on ARM targets"); + + if (!config->cmseInputLib.empty()) + error("--in-implib is only supported on ARM targets"); + + if (!config->cmseOutputLib.empty()) + error("--out-implib is only supported on ARM targets"); + } + if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); @@ -1114,6 +1125,9 @@ config->fini = args.getLastArgValue(OPT_fini, "_fini"); config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) && !args.hasArg(OPT_relocatable); + 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 = @@ -1657,6 +1671,10 @@ files.back()->justSymbols = true; } break; + case OPT_in_implib: + if (std::optional mb = readFile(arg->getValue())) + files.push_back(createObjFile(*mb)); + break; case OPT_start_group: if (InputFile::isInGroup) error("nested --start-group"); @@ -2332,6 +2350,107 @@ } } +// 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); + } + + // If there is an unresolved definition for sym, resolve it using + // the definition in the import library + for (auto &p : symtab.cmseImportLib) { + Defined *sym = p.second; + Symbol *d = symtab.find(sym->getName()); + if (!d || dyn_cast_or_null(d)) + continue; + d->resolve(*sym); + } +} + // Do renaming for --wrap and foo@v1 by updating pointers to symbols. // // When this function is executed, only InputFiles and symbol table @@ -2700,6 +2819,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.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -285,6 +285,7 @@ private: void initializeSections(bool ignoreComdats, const llvm::object::ELFFile &obj); + void importCmseSymbols(const llvm::object::ELFFile &obj); void initializeSymbols(const llvm::object::ELFFile &obj); void initializeJustSymbols(); 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,6 +531,12 @@ return; } + if (this->getName() == config->cmseInputLib) { + initializeJustSymbols(); + importCmseSymbols(obj); + return; + } + // Handle dependent libraries and selection of section groups as these are not // done in parallel. ArrayRef objSections = getELFShdrs(); @@ -1071,6 +1081,67 @@ } } +// Initialize this->Symbols. this->Symbols is a parallel array as +// its corresponding ELF symbol table. +template +void ObjFile::importCmseSymbols(const object::ELFFile &obj) { + ArrayRef eSyms = this->getELFSyms(); + numSymbols = eSyms.size(); + symbols = std::make_unique(numSymbols); + + // Error for local symbols. The symbol at index 0 is LOCAL. So skip it. + for (size_t i = 1, end = firstGlobal; i != end; ++i) { + error("CMSE symbol " + CHECK(eSyms[i].getName(stringTable), this) + + " in import library " + toString(this) + " is not global"); + } + + if (firstGlobal == eSyms.size()) + warn("CMSE import library " + toString(this) + + " does not define any symbols"); + + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + Defined *sym = reinterpret_cast(make()); + + // Initialize symbol fields. + memset(sym, 0, sizeof(Symbol)); + sym->setName(CHECK(eSyms[i].getName(stringTable), this)); + sym->value = eSym.st_value; + + if (eSym.st_shndx != SHN_ABS) { + error("CMSE symbol " + sym->getName() + " in import library " + + toString(this) + " is not absolute"); + continue; + } + + if (!(eSym.st_value & 0x1)) { + error("CMSE symbol " + sym->getName() + " in import library " + + toString(this) + " is not a Thumb code symbol"); + continue; + } + + if (eSym.getType() != STT_FUNC) { + error("CMSE symbol " + sym->getName() + " in import library " + + toString(this) + " is not of function type"); + continue; + } + + if (symtab.cmseImportLib.find(sym->getName()) != + symtab.cmseImportLib.end()) { + error("CMSE symbol " + sym->getName() + + "is multiply defined in import library " + toString(this)); + continue; + } + + if (eSym.st_size != 8) { + warn("CMSE symbol " + sym->getName() + " in import library " + + toString(this) + " does not have correct size of 8 bytes"); + } + + symtab.cmseImportLib[sym->getName()] = sym; + } +} + template void ObjFile::initSectionsAndLocalSyms(bool ignoreComdats) { if (!justSymbols) @@ -1120,11 +1191,15 @@ template void ObjFile::postParse() { static std::mutex mu; ArrayRef eSyms = this->getELFSyms(); + if (this->getName() == config->cmseInputLib) + return; + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; Symbol &sym = *symbols[i]; uint32_t secIdx = eSym.st_shndx; uint8_t binding = eSym.getBinding(); + 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,6 +80,19 @@ def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">; +def cmse_implib: FF<"cmse-implib">, + HelpText<"Make the output library to be a CMSE secure code import library">; + +defm in_implib: EEq<"in-implib", + "Read an existing CMSE secure code import library and preserve entry function addresses in the " + "resulting new CMSE secure code import library (optional when creating a CMSE secure image)">, + MetaVarName<"">; + +defm out_implib: EEq<"out-implib", + "Output the CMSE secure code import library to the location specified (required when creating " + "a CMSE secure image)">, + MetaVarName<"">; + defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">; defm Tdata: Eq<"Tdata", "Same as --section-start with .data 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,6 +60,13 @@ // is used to uniquify them. llvm::DenseMap comdatGroups; + 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: SmallVector findByVersion(SymbolVersion ver); SmallVector findAllByVersion(SymbolVersion ver, diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -99,6 +99,10 @@ return sym; } +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 // symbol errors. Symbol *SymbolTable::addAndCheckDuplicate(const Defined &newSym) { diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1220,6 +1220,77 @@ size_t getSize() const override; }; +class ArmCmseSGVeneer : public SyntheticSection { +public: + enum Kind { Unknown, FixedAddress, VarAddress }; + + Kind kind() const { return (Kind)veneerKind; } + uint8_t veneerKind; + + 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_se_sym(acle_se_sym) {} + void writeTo(uint8_t *buf) override; + Symbol *sym; + Symbol *acle_se_sym; +}; + +// A Secure Gateway Veneer that is positioned on the address +// specified in the CMSE import library. +// The symbol from the import library should have a specified size. +class ArmCmseFixedAddressSGVeneer final : public ArmCmseSGVeneer { +public: + ArmCmseFixedAddressSGVeneer(Symbol *sym, Symbol *acle_se_sym, uint64_t addr) + : ArmCmseSGVeneer(sym, acle_se_sym, ArmCmseSGVeneer::FixedAddress) { + entsize = 8; + entaddr = addr; + } + size_t getSize() const override { return this->entsize; }; + size_t getAddr() const { return this->entaddr; }; + static bool classof(const ArmCmseSGVeneer *s) { + return s->kind() != VarAddress; + } + +private: + uint64_t entaddr; +}; + +// 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 final : public ArmCmseSGVeneer { +public: + 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; }; + static bool classof(const ArmCmseSGVeneer *s) { + return s->kind() != FixedAddress; + } +}; + +class ArmCmseSGSection : public SyntheticSection { +public: + ArmCmseSGSection(); + bool isNeeded() const override { return !entries.empty(); } + size_t getSize() const override; + void writeTo(uint8_t *buf) override; + void addSGVeneer(Symbol *sym, Symbol *ext_sym); + inline void addMappingSymbol(); + void finalizeContents() override; + void exportEntries(SymbolTableBaseSection *symTab); + uint64_t impLibMinAddr = -1; + uint64_t impLibMaxAddr = 0; + +private: + SmallVector, 0> entries; + SmallVector sgSections; + uint64_t newEntries = 0; +}; + InputSection *createInterpSection(); MergeInputSection *createCommentSection(); template void splitSections(); @@ -1279,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; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -2191,6 +2191,130 @@ return SHN_ABS; } +ArmCmseSGSection::ArmCmseSGSection() + : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, + llvm::ELF::SHT_PROGBITS, + /*alignment=*/32, ".gnu.sgstubs") { + this->entsize = 8; + // The range of addresses used in the CMSE import library should be fixed. + for (auto &p : symtab.cmseImportLib) { + Defined *sym = p.second; + if (sym->value < impLibMinAddr) + impLibMinAddr = sym->value; + if (impLibMaxAddr <= sym->value) + impLibMaxAddr = sym->value; + } + impLibMaxAddr += this->entsize; + + 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_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()) { + ss = make(sym, acle_se_sym); + newEntries += 1; + } else { + Defined *impSym = symtab.cmseImportLib[sym->getName()]; + 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(), d->computeBinding(), + /*stOther=*/0, STT_FUNC, d->getVA(), + d->getSize(), nullptr)); + } +} + +void ArmCmseSGSection::writeTo(uint8_t *buf) { + for (auto *s : this->sgSections) { + auto off = s->outSecOff - getVA(); + s->writeTo(buf + off); + } +} + +inline void ArmCmseSGSection::addMappingSymbol() { + addSyntheticLocal("$t", STT_NOTYPE, /*off=*/0, /*size=*/0, *this); +} + +size_t ArmCmseSGSection::getSize() const { + if (sgSections.empty()) + return impLibMaxAddr - getVA() + (newEntries * entsize); + + return entries.size() * entsize; +} + +void ArmCmseSGSection::finalizeContents() { + auto it = std::partition( + this->sgSections.begin(), this->sgSections.end(), [](ArmCmseSGVeneer *i) { + return symtab.cmseImportLib.find(i->sym->getName()) != + symtab.cmseImportLib.end(); + }); + std::sort(this->sgSections.begin(), it, + [](ArmCmseSGVeneer *a, ArmCmseSGVeneer *b) { + return cast(a)->getAddr() < + cast(b)->getAddr(); + }); + for (ArmCmseSGVeneer *ss : this->sgSections) { + if (dyn_cast(ss)) + break; + ArmCmseFixedAddressSGVeneer *s = dyn_cast(ss); + 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) + + newEntries * entsize; + for (ArmCmseSGVeneer *ss : llvm::reverse(this->sgSections)) { + if (dyn_cast(ss)) + break; + auto *s = dyn_cast(ss); + off -= s->entsize; + 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); + } +} + +void ArmCmseSGVeneer::writeTo(uint8_t *buf) { + const uint8_t data[] = { + 0x7f, 0xe9, 0x7f, 0xe9, // SG + 0x00, 0xf0, 0x00, 0xb0, // B.W S + + }; + memcpy(buf, data, sizeof(data)); + uint64_t s = acle_se_sym->getVA(); + uint64_t p = getVA() + 4; + target->relocateNoSym(buf + 4, R_ARM_THM_JUMP24, s - p - 4); +} + // 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. @@ -3831,6 +3955,7 @@ strTab.reset(); symTab.reset(); symTabShndx.reset(); + armCmseSGSection.reset(); } constexpr char kMemtagAndroidNoteName[] = "Android"; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -215,6 +215,7 @@ void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); +template void writeARMCmseImportLib(); void riscvFinalizeRelax(int passes); void mergeRISCVAttributesSections(); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -453,6 +453,9 @@ in.igotPlt = std::make_unique(); add(*in.igotPlt); + in.armCmseSGSection = std::make_unique(); + add(*in.armCmseSGSection); + // _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) { @@ -590,6 +593,9 @@ if (auto e = buffer->commit()) fatal("failed to write output '" + buffer->getPath() + "': " + toString(std::move(e))); + + if (config->emachine == EM_ARM) + writeARMCmseImportLib(); } } @@ -1985,6 +1991,7 @@ removeUnusedSyntheticSections(); script->diagnoseOrphanHandling(); + script->diagnoseMissingSGVeneerAddress(); sortSections(); @@ -2133,6 +2140,7 @@ // static symbol table. finalizeSynthetic(in.symTab.get()); finalizeSynthetic(in.ppc64LongBranchTarget.get()); + finalizeSynthetic(in.armCmseSGSection.get()); } // Relaxation to delete inter-basic block jumps created by basic block diff --git a/lld/test/ELF/Inputs/arm-cmse-implib-1.s b/lld/test/ELF/Inputs/arm-cmse-implib-1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/arm-cmse-implib-1.s @@ -0,0 +1,16 @@ + .include "cmse-entry-creation-macros.s" + + .syntax unified + .text + + 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 secure_entry + .type secure_entry, %function +secure_entry: + nop + .size secure_entry, .-secure_entry diff --git a/lld/test/ELF/Inputs/arm-cmse-implib-2.s b/lld/test/ELF/Inputs/arm-cmse-implib-2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/arm-cmse-implib-2.s @@ -0,0 +1,25 @@ + .include "cmse-entry-creation-macros.s" + + .syntax unified + .text + + 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 secure_entry + .type secure_entry, %function +secure_entry: + nop + .size secure_entry, .-secure_entry + +// .align 2 +// .global normal_sym +// .type normal_sym, %function +// // Not an entry function because there is no corresponding __acle_se_normal_sym. +// normal_sym: +// nop +// .size normal_sym, .-normal_sym diff --git a/lld/test/ELF/Inputs/cmse-entry-creation-macros.s b/lld/test/ELF/Inputs/cmse-entry-creation-macros.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/cmse-entry-creation-macros.s @@ -0,0 +1,26 @@ +.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 \sym_name, .-\sym_name +.size __acle_se_\sym_name, .-__acle_se_\sym_name +.endm + +.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_\sym_name: + nop +.size \sym_name, .-\sym_name +.size __acle_se_\sym_name, .-__acle_se_\sym_name +.endm diff --git a/lld/test/ELF/aarch64-cmse.s b/lld/test/ELF/aarch64-cmse.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/aarch64-cmse.s @@ -0,0 +1,16 @@ +# REQUIRES: aarch64 +# RUN: yaml2obj %s -o %t.o +# RUN: not ld.lld --cmse-implib %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR_CMSE_IMPLIB +# RUN: not ld.lld --in-implib=%t.o %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR_IN_IMPLIB +# RUN: not ld.lld --out-implib=out.lib %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR_OUT_IMPLIB + +# ERR_CMSE_IMPLIB: error: --cmse-implib is only supported on ARM targets +# ERR_IN_IMPLIB: error: --in-implib is only supported on ARM targets +# ERR_OUT_IMPLIB: error: --out-implib is only supported on ARM targets + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 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,48 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes --triple=thumbv8m.main -filetype=obj -I%S/Inputs/ %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 + +.include "cmse-entry-creation-macros.s" + + .align 2 +cmse_veneer foo, function, global, function, global + +// 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,101 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -I %S/Inputs --triple=armv9a %s -o %t1.o +// RUN: not ld.lld --cmse-implib %t1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix ERROR + +.include "cmse-entry-creation-macros.s" + + .text + .thumb + +/// 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-implib.s b/lld/test/ELF/arm-cmse-implib.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-implib.s @@ -0,0 +1,131 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %S/Inputs/arm-cmse-implib-1.s -I %S/Inputs -o %t1.o +// 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 -I %S/Inputs -o %t2.o +// 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 + +/// Executable 1 +// CHECK1: File: +// CHECK1: Symbol table '.symtab' contains 12 entries: +// CHECK1-NEXT: Num: Value Size Type Bind Vis Ndx Name +// 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: 00020009 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK1-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo +// 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 WEAK DEFAULT 1 no_veneer1 +// CHECK1-NEXT: 8: 0000800d 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// 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 secure_entry + +/// Import library 1 +// CHECK1: File: +// 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 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>: +// DISS1-NEXT: 8000: bf00 nop +// DISS1-NEXT: 8002: 46c0 mov r8, r8 +// DISS1: 00008004 <__acle_se_bar>: +// DISS1-NEXT: 8004: bf00 nop +// DISS1-NEXT: 8006: 46c0 mov r8, r8 +// DISS1: 00008008 : +// DISS1-NEXT: 8008: e97f e97f sg +// DISS1: 0000800c <__acle_se_no_veneer1>: +// DISS1-NEXT: 800c: bf00 nop +// DISS1-NEXT: 800e: 46c0 mov r8, r8 +// DISS1: 00008010 : +// DISS1-NEXT: 8010: e97f e97f sg +// DISS1: 00008014 <__acle_se_no_veneer2>: +// DISS1-NEXT: 8014: bf00 nop +// DISS1-NEXT: 8016: 46c0 mov r8, r8 +// DISS1: 00008018 : +// DISS1-NEXT: 8018: bf00 nop + +// DISS1: Disassembly of section .gnu.sgstubs: +// DISS1: 00020000 : +// DISS1-NEXT: 20000: e97f e97f sg +// DISS1-NEXT: 20004: f7e7 bffe b.w 0x8004 <__acle_se_bar> +// DISS1: 00020008 : +// DISS1-NEXT: 20008: e97f e97f sg +// DISS1-NEXT: 2000c: f7e7 bff8 b.w 0x8000 <__acle_se_foo> + +/// Executable 2 +// CHECK2: File: +// CHECK2: Symbol table '.symtab' contains 14 entries: +// CHECK2-NEXT: Num: Value Size Type Bind Vis Ndx Name +// 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 WEAK DEFAULT 2 baz +// CHECK2-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 __acle_se_baz +// 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 WEAK DEFAULT 1 no_veneer1 +// CHECK2-NEXT: 10: 00008011 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// 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 secure_entry + +/// 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 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>: +// DISS2-NEXT: 8000: bf00 nop +// DISS2-NEXT: 8002: 46c0 mov r8, r8 +// DISS2: 00008004 <__acle_se_foo>: +// DISS2-NEXT: 8004: bf00 nop +// DISS2-NEXT: 8006: 46c0 mov r8, r8 +// DISS2: 00008008 <__acle_se_qux>: +// DISS2-NEXT: 8008: bf00 nop +// DISS2-NEXT: 800a: 46c0 mov r8, r8 +// DISS2: 0000800c : +// DISS2-NEXT: 800c: e97f e97f sg +// DISS2: 00008010 <__acle_se_no_veneer1>: +// DISS2-NEXT: 8010: bf00 nop +// DISS2-NEXT: 8012: 46c0 mov r8, r8 +// DISS2: 00008014 : +// DISS2-NEXT: 8014: e97f e97f sg +// DISS2: 00008018 <__acle_se_no_veneer2>: +// DISS2-NEXT: 8018: bf00 nop +// DISS2-NEXT: 801a: 46c0 mov r8, r8 +// DISS2: 0000801c : +// DISS2-NEXT: 801c: bf00 nop + +// DISS2: Disassembly of section .gnu.sgstubs: +// 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> +// DISS2: 00020018 : +// DISS2-NEXT: 20018: e97f e97f sg +// DISS2-NEXT: 2001c: f7e7 bff4 b.w 0x8008 <__acle_se_qux> 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,37 @@ +// REQUIRES: arm + +/// CMSE Only supported by ARMv8-M architecture or later. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8m.base %s -I %S/Inputs -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 -I %S/Inputs -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 -I %S/Inputs -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 -I %S/Inputs -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 -I %S/Inputs -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 -I %S/Inputs -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 -I %S/Inputs -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 -I %S/Inputs -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 + + .include "cmse-entry-creation-macros.s" + .text + .thumb + +cmse_veneer entry, function, global, function, global + +// ERROR: cmse special symbol '__acle_se_entry' only allowed for ARMv8-M architecture or later diff --git a/lld/test/ELF/arm-cmse-nonsecure.s b/lld/test/ELF/arm-cmse-nonsecure.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-nonsecure.s @@ -0,0 +1,17 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %s -o %t.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base -I %S/Inputs %S/Inputs/arm-cmse-implib-1.s -o %t1.o +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t1 --out-implib=%t1.lib --cmse-implib %t1.o +// RUN: ld.lld %t.o --cmse-implib --in-implib %t1.lib -o %t + + .align 2 + .global nonsecure_entry + .thumb + .thumb_func + .type nonsecure_entry, %function +nonsecure_entry: + push {r0,lr} + bl foo + pop {r0,lr} + bx lr + .size nonsecure_entry, .-nonsecure_entry diff --git a/lld/test/ELF/arm-cmse-secure.s b/lld/test/ELF/arm-cmse-secure.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-secure.s @@ -0,0 +1,40 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main -I%S/Inputs/ %s -o %t1.o +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --entry=secure_entry --cmse-implib --out-implib=%t1.lib %t1.o -o %t1 +/// Reuse the just-generated import library. +// RUN: ld.lld --entry=secure_entry %t1.o --section-start .gnu.sgstubs=0x20000 --cmse-implib --in-implib=%t1.lib --out-implib=%t2.lib -o %t2 +// RUN: llvm-readelf -s %t2.lib | FileCheck %s +// RUN: llvm-objdump -D %t2 | FileCheck %s --check-prefix=DISS + +.include "cmse-entry-creation-macros.s" + + .text + .thumb + .global func + .thumb_func +func: + bx lr + +cmse_veneer entry_1, function, global, function, global +cmse_veneer entry_2, function, global, function, global + + // Main entry point. + .global secure_entry + .thumb_func +secure_entry: + bx lr + +// CHECK: Symbol table '.symtab' contains 3 entries: +// CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK-NEXT: 1: 00020001 8 FUNC GLOBAL DEFAULT ABS entry_1 +// CHECK-NEXT: 2: 00020009 8 FUNC GLOBAL DEFAULT ABS entry_2 + + +// DISS: 00020000 : +// DISS-NEXT: 20000: e97f e97f sg +// DISS-NEXT: 20004: f000 b806 b.w 0x20014 <__acle_se_entry_1> + +// DISS: 00020008 : +// DISS-NEXT: 20008: e97f e97f sg +// DISS-NEXT: 2000c: f000 b804 b.w 0x20018 <__acle_se_entry_2> diff --git a/lld/test/ELF/arm-cmse-veneers.s b/lld/test/ELF/arm-cmse-veneers.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-veneers.s @@ -0,0 +1,35 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %s -I %S/Inputs -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 + + .include "cmse-entry-creation-macros.s" + + cmse_veneer global_foo, function, global, function, global + cmse_veneer weak_bar, function, weak, function, weak + cmse_veneer global_baz, function, global, function, weak + cmse_veneer weak_qux, function, weak, function, global + +// CHECK: Disassembly of section .text: +// CHECK: 00008000 <__acle_se_global_foo> +// CHECK: 00008004 <__acle_se_weak_bar> +// CHECK: 00008008 <__acle_se_global_baz> +// CHECK: 0000800c <__acle_se_weak_qux> + +// CHECK: Disassembly of section .gnu.sgstubs: + +// CHECK: 00020000 +// CHECK-NEXT: 20000: e97f e97f sg +// CHECK-NEXT: 20004: f7e8 b800 b.w 0x8008 <__acle_se_global_baz> + +// CHECK: 00020008 +// CHECK-NEXT: 20008: e97f e97f sg +// CHECK-NEXT: 2000c: f7e7 bff8 b.w 0x8000 <__acle_se_global_foo> + +// CHECK: 00020010 +// CHECK-NEXT: 20010: e97f e97f sg +// CHECK-NEXT: 20014: f7e7 bff6 b.w 0x8004 <__acle_se_weak_bar> + +// CHECK: 00020018 +// CHECK-NEXT: 20018: e97f e97f sg +// CHECK-NEXT: 2001c: f7e7 bff6 b.w 0x800c <__acle_se_weak_qux>