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 @@ -7,10 +7,13 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "OutputSections.h" +#include "SymbolTable.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" @@ -20,6 +23,7 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; +using namespace llvm::object; namespace { class ARM final : public TargetInfo { @@ -1054,8 +1058,414 @@ } } +// The Arm Cortex-M Security Extensions (CMSE) splits a system into two parts; +// the non-secure and secure states with the secure state inaccessible from the +// non-secure state, apart from an area of memory in secure state called the +// secure gateway which is accessible from non-secure state. The secure gateway +// contains one or more entry points which must start with a landing pad +// instruction SG. Arm recommends that the secure gateway consists only of +// secure gateway veneers, which are made up of a SG instruction followed by a +// branch to the destination in secure state. Full details can be found in Arm +// v8-M Security Extensions Requirements on Development Tools. +// +// The CMSE model of software development requires the non-secure and secure +// states to be developed as two separate programs. The non-secure developer is +// provided with an import library defining symbols describing the entry points +// in the secure gateway. No additional linker support is required for the +// non-secure state. +// +// Development of the secure state requires linker support to manage the secure +// gateway veneers. The management consists of: +// - Creation of new secure gateway veneers based on symbol conventions. +// - Checking the address of existing secure gateway veneers. +// - Warning when existing secure gateway veneers removed. +// +// The secure gateway veneers are created in an import library, which is just an +// ELF object with a symbol table. The import library is controlled by two +// command line options: +// --in-implib (specify an input import library from a previous revision of the +// program). +// --out-implib (specify an output import library to be created by the linker). +// +// The input import library is used to manage consistency of the secure entry +// points. The output import library is for new and updated secure entry points. +// +// The symbol convention that identifies secure entry functions is the prefix +// __acle_se_ for a symbol called name the linker is expected to create a secure +// gateway veneer if symbols __acle_se_name and name have the same address. +// After creating a secure gateway veneer the symbol name labels the secure +// gateway veneer and the __acle_se_name labels the function definition. +// +// The LLD implementation: +// - Reads an existing import library with importCmseSymbols(). +// - Determines which new secure gateway veneers to create and redirects calls +// within the secure state to the __acle_se_ prefixed symbol with +// processArmCmseSymbols(). +// - Models the SG veneers as a synthetic section. + +// Initialize symbols. symbols is a parallel array to the corresponding ELF +// symbol table. +template void ObjFile::importCmseSymbols() { + ArrayRef eSyms = getELFSyms(); + // Error for local symbols. The symbol at index 0 is LOCAL. So skip it. + for (size_t i = 1, end = firstGlobal; i != end; ++i) { + errorOrWarn("CMSE symbol '" + CHECK(eSyms[i].getName(stringTable), this) + + "' in import library '" + toString(this) + "' is not global"); + } + + 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; + sym->size = eSym.st_size; + sym->type = eSym.getType(); + sym->binding = eSym.getBinding(); + sym->stOther = eSym.st_other; + + if (eSym.st_shndx != SHN_ABS) { + error("CMSE symbol '" + sym->getName() + "' in import library '" + + toString(this) + "' is not absolute"); + continue; + } + + if (!(eSym.st_value & 1) || (eSym.getType() != STT_FUNC)) { + error("CMSE symbol '" + sym->getName() + "' in import library '" + + toString(this) + "' is not a Thumb function definition"); + continue; + } + + if (symtab.cmseImportLib.count(sym->getName())) { + error("CMSE symbol '" + sym->getName() + + "' is multiply defined in import library '" + toString(this) + "'"); + continue; + } + + if (eSym.st_size != ACLESESYM_SIZE) { + warn("CMSE symbol '" + sym->getName() + "' in import library '" + + toString(this) + "' does not have correct size of " + + Twine(ACLESESYM_SIZE) + " bytes"); + } + + symtab.cmseImportLib[sym->getName()] = sym; + } +} + +// Check symbol attributes of the acleSeSym, sym pair. +// Both symbols should be global/weak Thumb code symbol definitions. +static std::string checkCmseSymAttributes(Symbol *acleSeSym, Symbol *sym) { + auto check = [](Symbol *s, StringRef type) -> std::optional { + auto d = dyn_cast_or_null(s); + if (!(d && d->isFunc() && (d->value & 1))) + return (Twine(toString(s->file)) + ": cmse " + type + " symbol '" + + s->getName() + "' is not a Thumb function definition") + .str(); + if (!d->section) + return (Twine(toString(s->file)) + ": cmse " + type + " symbol '" + + s->getName() + "' cannot be an absolute symbol") + .str(); + return std::nullopt; + }; + for (auto [sym, type] : + {std::make_pair(acleSeSym, "special"), std::make_pair(sym, "entry")}) + if (auto err = check(sym, type)) + return *err; + return ""; +} + +// 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. +void elf::processArmCmseSymbols() { + 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 *acleSeSym : symtab.getSymbols()) { + if (!acleSeSym->getName().startswith(ACLESESYM_PREFIX)) + continue; + // If input object build attributes do not support CMSE, error and disable + // further scanning for , __acle_se_ pairs. + if (!config->armCMSESupport) { + error("CMSE is only supported by ARMv8-M architecture or later"); + config->cmseImplib = false; + break; + } + + // Try to find the associated symbol definition. + // Symbol must have external linkage. + StringRef name = acleSeSym->getName().substr(std::strlen(ACLESESYM_PREFIX)); + Symbol *sym = symtab.find(name); + if (!sym) { + error(toString(acleSeSym->file) + ": cmse special symbol '" + + acleSeSym->getName() + + "' detected, but no associated entry function definition '" + name + + "' with external linkage found"); + continue; + } + + std::string errMsg = checkCmseSymAttributes(acleSeSym, sym); + if (!errMsg.empty()) { + error(errMsg); + continue; + } + + // may be redefined later in the link in .gnu.sgstubs + symtab.cmseSymMap[name] = {acleSeSym, sym}; + } + + // If this is an Arm CMSE secure app, replace references to entry symbol + // with its corresponding special symbol __acle_se_. + parallelForEach(ctx.objectFiles, [&](InputFile *file) { + MutableArrayRef syms = file->getMutableSymbols(); + for (size_t i = 0, e = syms.size(); i != e; ++i) { + StringRef symName = syms[i]->getName(); + if (symtab.cmseSymMap.count(symName)) + syms[i] = symtab.cmseSymMap[symName].acleSeSym; + } + }); +} + +class elf::ArmCmseSGVeneer { +public: + ArmCmseSGVeneer(Symbol *sym, Symbol *acleSeSym, + std::optional addr = std::nullopt) + : sym(sym), acleSeSym(acleSeSym), entAddr{addr} {} + static const size_t size{ACLESESYM_SIZE}; + const std::optional getAddr() const { return entAddr; }; + + Symbol *sym; + Symbol *acleSeSym; + uint64_t offset = 0; + +private: + const std::optional entAddr; +}; + +ArmCmseSGSection::ArmCmseSGSection() + : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, + llvm::ELF::SHT_PROGBITS, + /*alignment=*/32, ".gnu.sgstubs") { + entsize = ACLESESYM_SIZE; + // The range of addresses used in the CMSE import library should be fixed. + for (auto &[_, sym] : symtab.cmseImportLib) { + if (impLibMaxAddr <= sym->value) + impLibMaxAddr = sym->value + sym->size; + } + if (symtab.cmseSymMap.empty()) + return; + addMappingSymbol(); + for (auto &[_, entryFunc] : symtab.cmseSymMap) + addSGVeneer(cast(entryFunc.acleSeSym), + cast(entryFunc.sym)); + for (auto &[_, sym] : symtab.cmseImportLib) { + if (!symtab.inCMSEOutImpLib.count(sym->getName())) + warn("entry function '" + sym->getName() + + "' from CMSE import library is not present in secure application"); + } + + if (!symtab.cmseImportLib.empty() && config->cmseOutputLib.empty()) { + for (auto &[_, entryFunc] : symtab.cmseSymMap) { + Symbol *sym = entryFunc.sym; + if (!symtab.inCMSEOutImpLib.count(sym->getName())) + warn("new entry function '" + sym->getName() + + "' introduced but no output import library specified"); + } + } +} + +void ArmCmseSGSection::addSGVeneer(Symbol *acleSeSym, Symbol *sym) { + entries.emplace_back(acleSeSym, sym); + if (symtab.cmseImportLib.count(sym->getName())) + symtab.inCMSEOutImpLib[sym->getName()] = true; + // Symbol addresses different, nothing to do. + if (acleSeSym->file != sym->file || + cast(*acleSeSym).value != cast(*sym).value) + return; + // Only secure symbols with values equal to that of it's non-secure + // counterpart needs to be in the .gnu.sgstubs section. + ArmCmseSGVeneer *ss = nullptr; + if (symtab.cmseImportLib.count(sym->getName())) { + Defined *impSym = symtab.cmseImportLib[sym->getName()]; + ss = make(sym, acleSeSym, impSym->value); + } else { + ss = make(sym, acleSeSym); + ++newEntries; + } + sgVeneers.emplace_back(ss); +} + +void ArmCmseSGSection::writeTo(uint8_t *buf) { + for (ArmCmseSGVeneer *s : sgVeneers) { + uint8_t *p = buf + s->offset; + write16(p + 0, 0xe97f); // SG + write16(p + 2, 0xe97f); + write16(p + 4, 0xf000); // B.W S + write16(p + 6, 0xb000); + target->relocateNoSym(p + 4, R_ARM_THM_JUMP24, + s->acleSeSym->getVA() - + (getVA() + s->offset + s->size)); + } +} + +void ArmCmseSGSection::addMappingSymbol() { + addSyntheticLocal("$t", STT_NOTYPE, /*off=*/0, /*size=*/0, *this); +} + +size_t ArmCmseSGSection::getSize() const { + if (sgVeneers.empty()) + return (impLibMaxAddr ? impLibMaxAddr - getVA() : 0) + newEntries * entsize; + + return entries.size() * entsize; +} + +void ArmCmseSGSection::finalizeContents() { + if (sgVeneers.empty()) + return; + + auto it = + std::stable_partition(sgVeneers.begin(), sgVeneers.end(), + [](auto *i) { return i->getAddr().has_value(); }); + std::sort(sgVeneers.begin(), it, [](auto *a, auto *b) { + return a->getAddr().value() < b->getAddr().value(); + }); + // This is the partition of the veneers with fixed addresses. + uint64_t addr = (*sgVeneers.begin())->getAddr().has_value() + ? (*sgVeneers.begin())->getAddr().value() + : getVA(); + // Check if the start address of '.gnu.sgstubs' correspond to the + // linker-synthesized veneer with the lowest address. + if ((getVA() & ~1) != (addr & ~1)) { + error("start address of '.gnu.sgstubs' is different from previous link"); + return; + } + + for (size_t i = 0; i < sgVeneers.size(); ++i) { + ArmCmseSGVeneer *s = sgVeneers[i]; + s->offset = i * s->size; + Defined(file, StringRef(), s->sym->binding, s->sym->stOther, s->sym->type, + s->offset | 1, s->size, this) + .overwrite(*s->sym); + } +} + +// 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() { + StringTableSection *shstrtab = + make(".shstrtab", /*dynamic=*/false); + StringTableSection *strtab = + make(".strtab", /*dynamic=*/false); + SymbolTableBaseSection *impSymTab = make>(*strtab); + + SmallVector, 0> osIsPairs; + osIsPairs.emplace_back(make(strtab->name, 0, 0), strtab); + osIsPairs.emplace_back(make(impSymTab->name, 0, 0), impSymTab); + osIsPairs.emplace_back(make(shstrtab->name, 0, 0), shstrtab); + + std::sort(symtab.cmseSymMap.begin(), symtab.cmseSymMap.end(), + [](const auto &a, const auto &b) -> bool { + return a.second.sym->getVA() < b.second.sym->getVA(); + }); + // Copy the secure gateway entry symbols to the import library symbol table. + for (auto &p : symtab.cmseSymMap) { + Defined *d = cast(p.second.sym); + impSymTab->addSymbol(makeDefined(nullptr, d->getName(), d->computeBinding(), + /*stOther=*/0, STT_FUNC, d->getVA(), + d->getSize(), nullptr)); + } + + size_t idx = 0; + uint64_t off = sizeof(typename ELFT::Ehdr); + for (auto &[osec, isec] : osIsPairs) { + osec->sectionIndex = ++idx; + osec->recordSection(isec); + osec->finalizeInputSections(); + osec->shName = shstrtab->addString(osec->name); + osec->size = isec->getSize(); + isec->finalizeContents(); + osec->offset = alignToPowerOf2(off, osec->addralign); + off = osec->offset + osec->size; + } + + const uint64_t sectionHeaderOff = alignToPowerOf2(off, config->wordsize); + const auto shnum = osIsPairs.size() + 1; + const uint64_t fileSize = + sectionHeaderOff + shnum * sizeof(typename ELFT::Shdr); + const unsigned flags = + config->mmapOutputFile ? 0 : (unsigned)FileOutputBuffer::F_no_mmap; + unlinkAsync(config->cmseOutputLib); + Expected> bufferOrErr = + FileOutputBuffer::create(config->cmseOutputLib, fileSize, flags); + if (!bufferOrErr) { + error("failed to open " + config->cmseOutputLib + ": " + + llvm::toString(bufferOrErr.takeError())); + return; + } + + // Write the ELF Header + std::unique_ptr &buffer = *bufferOrErr; + uint8_t *const buf = buffer->getBufferStart(); + memcpy(buf, "\177ELF", 4); + auto *eHdr = reinterpret_cast(buf); + eHdr->e_type = ET_REL; + eHdr->e_entry = 0; + eHdr->e_shoff = sectionHeaderOff; + eHdr->e_ident[EI_CLASS] = ELFCLASS32; + eHdr->e_ident[EI_DATA] = config->isLE ? ELFDATA2LSB : ELFDATA2MSB; + eHdr->e_ident[EI_VERSION] = EV_CURRENT; + eHdr->e_ident[EI_OSABI] = config->osabi; + eHdr->e_ident[EI_ABIVERSION] = 0; + eHdr->e_machine = EM_ARM; + eHdr->e_version = EV_CURRENT; + eHdr->e_flags = config->eflags; + eHdr->e_ehsize = sizeof(typename ELFT::Ehdr); + eHdr->e_phnum = 0; + eHdr->e_shentsize = sizeof(typename ELFT::Shdr); + eHdr->e_phoff = 0; + eHdr->e_phentsize = 0; + eHdr->e_shnum = shnum; + eHdr->e_shstrndx = shstrtab->getParent()->sectionIndex; + + // Write the section header table. + auto *sHdrs = reinterpret_cast(buf + eHdr->e_shoff); + for (auto &[osec, _] : osIsPairs) + osec->template writeHeaderTo(++sHdrs); + + // Write section contents to a mmap'ed file. + { + parallel::TaskGroup tg; + for (auto &[osec, _] : osIsPairs) + osec->template writeTo(buf + osec->offset, tg); + } + + if (auto e = buffer->commit()) + fatal("failed to write output '" + buffer->getPath() + + "': " + toString(std::move(e))); +} + TargetInfo *elf::getARMTargetInfo() { static ARM target; return ⌖ } +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); + +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -129,6 +129,7 @@ std::unique_ptr lto; std::vector files; + std::optional armCmseImpLib; public: SmallVector, 0> archiveFiles; @@ -173,6 +174,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; @@ -195,11 +198,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; bool armBe8 = false; BsymbolicKind bsymbolic = BsymbolicKind::None; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -345,6 +345,22 @@ 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) { + if (!config->cmseInputLib.empty()) + error("--in-implib may not be used without --cmse-implib"); + if (!config->cmseOutputLib.empty()) + error("--out-implib may not be used without --cmse-implib"); + } + } else { + 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"); @@ -1165,6 +1181,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 = @@ -1742,6 +1761,12 @@ files.back()->justSymbols = true; } break; + case OPT_in_implib: + if (armCmseImpLib) + error("multiple CMSE import libraries not supported"); + else if (std::optional mb = readFile(arg->getValue())) + armCmseImpLib = createObjFile(*mb); + break; case OPT_start_group: if (InputFile::isInGroup) error("nested --start-group"); @@ -2621,6 +2646,8 @@ llvm::TimeTraceScope timeScope("Parse input files", files[i]->getName()); parseFile(files[i]); } + if (armCmseImpLib) + parseArmCMSEImportLib(*armCmseImpLib); } // Now that we have every file, we can decide if we will need a @@ -2785,6 +2812,9 @@ if (args.hasArg(OPT_exclude_libs)) excludeLibs(args); + // Record [__acle_se_, ] pairs for later processing. + processArmCmseSymbols(); + // 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 @@ -48,6 +48,8 @@ // Add symbols in File to the symbol table. void parseFile(InputFile *file); +void parseArmCMSEImportLib(InputFile *file); + // The root class of input files. class InputFile { protected: @@ -88,6 +90,12 @@ return {symbols.get(), numSymbols}; } + MutableArrayRef getMutableSymbols() { + assert(fileKind == BinaryKind || fileKind == ObjKind || + fileKind == BitcodeKind); + return {symbols.get(), numSymbols}; + } + // Get filename to use for linker script processing. StringRef getNameForScript() const; @@ -280,6 +288,8 @@ void initSectionsAndLocalSyms(bool ignoreComdats); void postParse(); + void importCmseSymbols(); + void redirectCmseSymbols(); private: void initializeSections(bool ignoreComdats, 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,15 @@ config->armHasMovtMovw = true; break; } + + // Only ARMv8-M or later architectures have CMSE support. + std::optional profile = + attributes.getAttributeValue(ARMBuildAttrs::CPU_arch_profile); + if (!profile) + return; + if (arch >= ARMBuildAttrs::CPUArch::v8_M_Base && + profile == ARMBuildAttrs::MicroControllerProfile) + config->armCMSESupport = true; } InputFile::InputFile(Kind k, MemoryBufferRef m) @@ -317,6 +326,14 @@ // Add symbols in File to the symbol table. void elf::parseFile(InputFile *file) { invokeELFT(doParseFile, file); } +template static void doParseArmCMSEImportLib(InputFile *file) { + cast>(file)->importCmseSymbols(); +} + +void elf::parseArmCMSEImportLib(InputFile *file) { + invokeELFT(doParseArmCMSEImportLib, file); +} + // Concatenates arguments to construct a string representing an error location. static std::string createFileLineMsg(StringRef path, unsigned line) { std::string filename = std::string(path::filename(path)); @@ -1031,8 +1048,8 @@ return makeThreadLocal(*this, sec, name); } -// Initialize this->Symbols. this->Symbols is a parallel array as -// its corresponding ELF symbol table. +// Initialize symbols. symbols is a parallel array to the corresponding ELF +// symbol table. template void ObjFile::initializeSymbols(const object::ELFFile &obj) { ArrayRef eSyms = this->getELFSyms(); diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -318,6 +318,7 @@ void addOrphanSections(); void diagnoseOrphanHandling() const; + void diagnoseMissingSGSectionAddress() 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 @@ -896,6 +896,15 @@ } } +void LinkerScript::diagnoseMissingSGSectionAddress() const { + if (!config->cmseImplib || !in.armCmseSGSection->isNeeded()) + return; + + OutputSection *sec = findByName(sectionCommands, ".gnu.sgstubs"); + if (sec && !sec->addrExpr && !config->sectionStartMap.count(".gnu.sgstubs")) + error("no address assigned to the veneers output section " + sec->name); +} + // 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/MarkLive.cpp b/lld/ELF/MarkLive.cpp --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -230,6 +230,10 @@ markSymbol(symtab.find(s)); for (StringRef s : script->referencedSymbols) markSymbol(symtab.find(s)); + for (auto [symName, _] : symtab.cmseSymMap) { + markSymbol(symtab.cmseSymMap[symName].sym); + markSymbol(symtab.cmseSymMap[symName].acleSeSym); + } // Mark .eh_frame sections as live because there are usually no relocations // that point to .eh_frames. Otherwise, the garbage collector would drop diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -82,6 +82,18 @@ 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 (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 @@ -19,6 +19,11 @@ class InputFile; class SharedFile; +struct ArmCmseEntryFunction { + Symbol *acleSeSym; + Symbol *sym; +}; + // SymbolTable is a bucket of all known symbols, including defined, // undefined, or lazy symbols (the last one is symbols in archive // files whose archive members are not yet loaded). @@ -60,6 +65,18 @@ // is used to uniquify them. llvm::DenseMap comdatGroups; + // The Map of __acle_se_, pairs found in the input objects. + // Key is the name. + llvm::SmallMapVector cmseSymMap; + + // Map of symbols defined in the Arm CMSE import library. The linker must + // preserve the addresses in the output objects. + llvm::StringMap cmseImportLib; + + // True if from the input Arm CMSE import library is written to the + // output Arm CMSE import library. + llvm::StringMap inCMSEOutImpLib; + private: SmallVector findByVersion(SymbolVersion ver); SmallVector findAllByVersion(SymbolVersion ver, diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1143,6 +1143,31 @@ size_t size = 0; }; +// Cortex-M Security Extensions. Prefix for functions that should be exported +// for the non-secure world. +const char ACLESESYM_PREFIX[] = "__acle_se_"; +const int ACLESESYM_SIZE = 8; + +class ArmCmseSGVeneer; + +class ArmCmseSGSection final : 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); + void addMappingSymbol(); + void finalizeContents() override; + void exportEntries(SymbolTableBaseSection *symTab); + uint64_t impLibMaxAddr = 0; + +private: + SmallVector, 0> entries; + SmallVector sgVeneers; + uint64_t newEntries = 0; +}; + // Used to compute outSecOff of .got2 in each object file. This is needed to // synthesize PLT entries for PPC32 Secure PLT ABI. class PPC32Got2Section final : public SyntheticSection { @@ -1279,6 +1304,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 @@ -3835,6 +3835,7 @@ got.reset(); gotPlt.reset(); igotPlt.reset(); + armCmseSGSection.reset(); ppc64LongBranchTarget.reset(); mipsAbiFlags.reset(); mipsGot.reset(); diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -201,6 +201,8 @@ return getErrorPlace(loc).loc; } +void processArmCmseSymbols(); + void writePPC32GlinkSection(uint8_t *buf, size_t numEntries); unsigned getPPCDFormOp(unsigned secondaryOp); @@ -222,6 +224,7 @@ void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); +template void writeARMCmseImportLib(); void riscvFinalizeRelax(int passes); void mergeRISCVAttributesSections(); void addArmInputSectionMappingSymbols(); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -454,6 +454,11 @@ in.igotPlt = std::make_unique(); add(*in.igotPlt); + if (config->emachine == EM_ARM) { + 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) { @@ -595,6 +600,9 @@ if (auto e = buffer->commit()) fatal("failed to write output '" + buffer->getPath() + "': " + toString(std::move(e))); + + if (!config->cmseOutputLib.empty()) + writeARMCmseImportLib(); } } @@ -1983,6 +1991,7 @@ removeUnusedSyntheticSections(); script->diagnoseOrphanHandling(); + script->diagnoseMissingSGSectionAddress(); sortSections(); @@ -2134,6 +2143,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-macros.s b/lld/test/ELF/Inputs/arm-cmse-macros.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/arm-cmse-macros.s @@ -0,0 +1,32 @@ +/// Because the addresses of __acle_se_\sym_name and \sym_name are equal, +/// the linker creates a secure gateway in ".gnu.sgstubs". +.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 + +/// Because the addresses of __acle_se_\sym_name and \sym_name are not equal, +/// the linker considers that an inline secure gateway exists and does not +/// create one. +.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 + nop +__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-diagnostics.s b/lld/test/ELF/arm-cmse-diagnostics.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-diagnostics.s @@ -0,0 +1,281 @@ +// REQUIRES: arm +/// Test CMSE diagnostics. + +// RUN: rm -rf %t && split-file %s %t && cd %t + +/// Test diagnostics emitted during checks of the CMSE import library +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base lib -o lib.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base app -I %S/Inputs -o app.o +// RUN: llvm-objcopy --redefine-sym=entry7_duplicate=entry6_duplicate lib.o +// RUN: not ld.lld --cmse-implib --in-implib=lib.o app.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR_IMPLIB +// RUN: not ld.lld --cmse-implib --in-implib=lib.o --in-implib=lib.o app.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR_MULT_INIMPLIB +// RUN: not ld.lld --in-implib=lib.o app.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR_IN_IMPLIB +// RUN: not ld.lld --out-implib=out.lib app.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR_OUT_IMPLIB +// RUN: not ld.lld --out-implib=out.lib --in-implib=lib.o app.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR_IN_IMPLIB,ERR_OUT_IMPLIB + +// ERR_IMPLIB: error: CMSE symbol 'entry_not_external' in import library '{{.*}}' is not global +// ERR_IMPLIB: error: CMSE symbol 'entry_not_absolute' in import library '{{.*}}' is not absolute +// ERR_IMPLIB: error: CMSE symbol 'entry_not_function' in import library '{{.*}}' is not a Thumb function definition +// ERR_IMPLIB: error: CMSE symbol 'entry_not_thumb' in import library '{{.*}}' is not a Thumb function definition +// ERR_IMPLIB: warning: CMSE symbol 'entry5_incorrect_size' in import library '{{.*}}' does not have correct size of 8 bytes +// ERR_IMPLIB: error: CMSE symbol 'entry6_duplicate' is multiply defined in import library '{{.*}}' +// ERR_MULT_INIMPLIB: error: multiple CMSE import libraries not supported +// ERR_IN_IMPLIB: error: --in-implib may not be used without --cmse-implib +// ERR_OUT_IMPLIB: error: --out-implib may not be used without --cmse-implib + +/// CMSE Only supported by ARMv8-M architecture or later. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8m.base app -I %S/Inputs -o app1.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app1 app1.o 2>&1 | FileCheck /dev/null --implicit-check-not=error: + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8m.main app -I %S/Inputs -o app2.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app2 app2.o 2>&1 | FileCheck /dev/null --implicit-check-not=error: + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv8.1m.main app -I %S/Inputs -o app3.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app3 app3.o 2>&1 | FileCheck /dev/null --implicit-check-not=error: + +/// Expect errors for other architectures. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv9a app -I %S/Inputs -o app4.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app4 app4.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv7-m app -I %S/Inputs -o app5.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app5 app5.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv8-m app -I %S/Inputs -o app6.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app6 app6.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +/// Invalid triple defaults to v4T. Error. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumb app -I %S/Inputs -o app7.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app7 app7.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +/// No build attributes. Error. +// RUN: llvm-mc -filetype=obj -triple=thumb app -I %S/Inputs -o app8.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app8 app8.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +// ERR_ARCH: CMSE is only supported by ARMv8-M architecture or later + +/// Test that the linker diagnoses cases where the linker synthesized veneer addresses +/// specified by the CMSE input library cannot be placed at the .gnu.sgstubs section address. + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main app -I %S/Inputs -o 1.o +/// Create a CMSE import library with a secure gateway veneer at 0x10000 +// RUN: ld.lld --cmse-implib --section-start .gnu.sgstubs=0x10000 1.o -o 1 --out-implib=1.lib 2>&1 | FileCheck /dev/null --implicit-check-not=error: +/// Create a new import library with the secure gateway veneer and .gnu.sgstubs specified at the same address +// RUN: ld.lld --cmse-implib --section-start .gnu.sgstubs=0x10000 1.o -o 2 --out-implib=2.lib --in-implib=1.lib 2>&1 | FileCheck /dev/null --implicit-check-not=error: +/// Create a new import library with the secure gateway veneer specified at a same address but .gnu.sgstubs at a higher address. +// RUN: not ld.lld --cmse-implib --section-start .gnu.sgstubs=0x11000 1.o -o 3 --out-implib=3.lib --in-implib=1.lib 2>&1 | FileCheck %s --check-prefixes=ERR_ADDR +/// Create a new import library with the secure gateway veneer specified at a same address but .gnu.sgstubs at a lower address. +// RUN: not ld.lld --cmse-implib --section-start .gnu.sgstubs=0x9000 1.o -o 4 --out-implib=4.lib --in-implib=1.lib 2>&1 | FileCheck %s --check-prefixes=ERR_ADDR + +// ERR_ADDR: error: start address of '.gnu.sgstubs' is different from previous link + +/// Test that the address of .gnu.sgstubs can be specified via command line or linker script. +/// Test that the linker errors when the address of .gnu.sgstubs is not specified using either method. + +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script with_sgstubs.script 1.o -o 1 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script wout_sgstubs.script 1.o -o 2 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 1.o -o 3 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --script with_sgstubs.script 1.o -o 4 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --script wout_sgstubs.script 1.o -o 5 2>&1 | FileCheck %s --check-prefixes=ERR_NOADDR + +// RUN: llvm-readelf -S 1 | FileCheck %s --check-prefixes=ADDRCMDLINE +// RUN: llvm-readelf -S 2 | FileCheck %s --check-prefixes=ADDRCMDLINE +// RUN: llvm-readelf -S 3 | FileCheck %s --check-prefixes=ADDRCMDLINE +// RUN: llvm-readelf -S 4 | FileCheck %s --check-prefixes=ADDRLNKSCRIPT + +// ERR_NOADDR: error: no address assigned to the veneers output section .gnu.sgstubs + +/// Name Type Address Off Size ES Flg Lk Inf Al +// ADDRCMDLINE: .gnu.sgstubs PROGBITS 00020000 020000 000008 08 AX 0 0 32 +// ADDRLNKSCRIPT: .gnu.sgstubs PROGBITS 00040000 040000 000008 08 AX 0 0 32 + +/// Test diagnostics emitted during symbol attribute checks. + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -I %S/Inputs --triple=thumbv8m.base symattr -o symattr.o +// RUN: not ld.lld --cmse-implib symattr.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR_SYMATTR + +// ERR_SYMATTR-NOT: __acle_se_valid_{{.*}} +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_1' is not a Thumb function definition +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_2' is not a Thumb function definition +// ERR_SYMATTR: error: {{.*}}: cmse entry symbol 'invalid_3' is not a Thumb function definition +// ERR_SYMATTR: error: {{.*}}: cmse entry symbol 'invalid_4' is not a Thumb function definition +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_5' detected, but no associated entry function definition 'invalid_5' with external linkage found +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_6' detected, but no associated entry function definition 'invalid_6' with external linkage found +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_7' is not a Thumb function definition +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_8' detected, but no associated entry function definition 'invalid_8' with external linkage found +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_9' cannot be an absolute symbol +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_10' cannot be an absolute symbol +// ERR_SYMATTR: error: {{.*}}: cmse special symbol '__acle_se_invalid_11' is not a Thumb function definition +// ERR_SYMATTR: error: {{.*}}: cmse entry symbol 'invalid_12' is not a Thumb function definition + +/// Test diagnostics emitted when a symbol is removed from a later version of the import library. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -I %S/Inputs --triple=thumbv8m.base libv1 -o libv1.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -I %S/Inputs --triple=thumbv8m.base libv2 -o libv2.o +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --cmse-implib libv1.o --out-implib=libv1.lib -o /dev/null +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --cmse-implib libv2.o --in-implib=libv1.lib --out-implib=libv2.lib -o /dev/null 2>&1 | FileCheck %s --check-prefixes=WARN_MISSING +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --cmse-implib libv1.o --in-implib=libv2.lib -o /dev/null 2>&1 | FileCheck %s --check-prefixes=WARN_NEWENTRY + +// WARN_MISSING: warning: entry function 'bar' from CMSE import library is not present in secure application +// WARN_NEWENTRY: warning: new entry function 'bar' introduced but no output import library specified + +//--- with_sgstubs.script +SECTIONS { + .text : { *(.text) } + .gnu.sgstubs 0x40000 : { *(.gnu.sgstubs*) } +} + +//--- wout_sgstubs.script +SECTIONS { + .text : { *(.text) } +} + +//--- app + .include "arm-cmse-macros.s" + .text + .thumb + +cmse_veneer entry, function, global, function, global + +//--- lib + .text + .thumb + +/// Symbol not absolute. + .global entry_not_absolute + .type entry_not_absolute, STT_FUNC + .thumb_func +entry_not_absolute: + .size entry_not_absolute, 8 + +/// Symbol not global or weak. + .local entry_not_external + .type entry_not_external, STT_FUNC +entry_not_external=0x1001 + .size entry_not_external, 8 + +/// Symbol not the function type. + .global entry_not_function + .type entry_not_function, STT_NOTYPE +entry_not_function=0x1001 + .size entry_not_function, 8 + +/// Symbol not a Thumb code symbol. + .global entry_not_thumb + .type entry_not_thumb, STT_FUNC +entry_not_thumb=0x1000 + .size entry_not_thumb, 8 + +/// Symbol with incorrect size. + .global entry5_incorrect_size + .type entry5_incorrect_size, STT_FUNC +entry5_incorrect_size=0x1009 + .size entry5_incorrect_size, 6 + +/// Duplicate symbols. + .global entry6_duplicate + .type entry6_duplicate, STT_FUNC +entry6_duplicate=0x1001 + .size entry6_duplicate, 8 + +/// entry7_duplicate gets renamed to entry6_duplicate by llvm-objcopy. + .global entry7_duplicate + .type entry7_duplicate, STT_FUNC +entry7_duplicate=0x1009 + .size entry7_duplicate, 8 + +//--- symattr +.include "arm-cmse-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 not defined. + .global invalid_7 + .type invalid_7, %function + .global __acle_se_invalid_7 + .thumb_func +invalid_7: + +/// invalid_8 not defined. + .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: + .size invalid_11, .-invalid_11 +/// Invalid non-thumb function symbol __acle_se_invalid_11 +__acle_se_invalid_11=0x1000 + + .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=0x1000 + .thumb +__acle_se_invalid_12: + .size __acle_se_invalid_12, .-__acle_se_invalid_12 + +//--- libv1 +.include "arm-cmse-macros.s" + + .text + .thumb + +/// Import library version 1 with foo and bar + cmse_veneer foo, function, global, function, global + cmse_veneer bar, function, weak, function, weak + +//--- libv2 +.include "arm-cmse-macros.s" + + .text + .thumb + +/// Import library version 2 with bar missing. + cmse_veneer foo, function, global, function, global 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,114 @@ +// REQUIRES: arm +/// Test that addresses of secure gateways in an old import library are maintained in new import libraries. + +// RUN: rm -rf %t && split-file %s %t && cd %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base app -o app.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base implib-v1 -I %S/Inputs -o 1.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base implib-v2 -I %S/Inputs -o 2.o + +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o 1 app.o --out-implib=1.lib --cmse-implib 1.o +// RUN: llvm-readelf -s 1 1.lib | FileCheck %s --check-prefixes=CHECK1 + +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o 2 app.o --out-implib=2.lib --in-implib=1.lib --cmse-implib 2.o +// RUN: llvm-readelf -s 2 2.lib | FileCheck %s --check-prefixes=CHECK2 + +//--- app + + .align 2 + .global secure_entry + .type secure_entry, %function +secure_entry: + nop + .size secure_entry, .-secure_entry + +//--- implib-v1 + + .include "arm-cmse-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 + +//--- implib-v2 + + .include "arm-cmse-macros.s" + + .syntax unified + .text + + cmse_veneer baz, function, weak, function, global + cmse_veneer foo, function, global, function, global + cmse_veneer bar, function, weak, 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 + +/// Executable 1 +// CHECK1: File: +// CHECK1: Symbol table '.symtab' contains 13 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: 00008004 0 NOTYPE LOCAL DEFAULT 1 $t.0 +// CHECK1-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 secure_entry +// CHECK1-NEXT: 5: 00020001 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK1-NEXT: 6: 00008005 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo +// CHECK1-NEXT: 7: 00020009 8 FUNC WEAK DEFAULT 2 bar +// CHECK1-NEXT: 8: 00008009 2 FUNC GLOBAL DEFAULT 1 __acle_se_bar +// CHECK1-NEXT: 9: 0000800d 8 FUNC WEAK DEFAULT 1 no_veneer1 +// CHECK1-NEXT: 10: 00008013 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// CHECK1-NEXT: 11: 00008015 8 FUNC WEAK DEFAULT 1 no_veneer2 +// CHECK1-NEXT: 12: 0000801b 2 FUNC WEAK DEFAULT 1 __acle_se_no_veneer2 + + +/// 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: 0000800d 8 FUNC WEAK DEFAULT ABS no_veneer1 +// CHECK1-NEXT: 2: 00008015 8 FUNC WEAK DEFAULT ABS no_veneer2 +// CHECK1-NEXT: 3: 00020001 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK1-NEXT: 4: 00020009 8 FUNC WEAK DEFAULT ABS bar + +/// Executable 2 +// CHECK2: File: +// CHECK2: Symbol table '.symtab' contains 17 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: 00008004 0 NOTYPE LOCAL DEFAULT 1 $t.0 +// CHECK2-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 secure_entry +// CHECK2-NEXT: 5: 00020011 8 FUNC WEAK DEFAULT 2 baz +// CHECK2-NEXT: 6: 00008005 2 FUNC GLOBAL DEFAULT 1 __acle_se_baz +// CHECK2-NEXT: 7: 00020001 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK2-NEXT: 8: 00008009 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo +// CHECK2-NEXT: 9: 00020009 8 FUNC WEAK DEFAULT 2 bar +// CHECK2-NEXT: 10: 0000800d 2 FUNC GLOBAL DEFAULT 1 __acle_se_bar +// CHECK2-NEXT: 11: 00020019 8 FUNC GLOBAL DEFAULT 2 qux +// CHECK2-NEXT: 12: 00008011 2 FUNC GLOBAL DEFAULT 1 __acle_se_qux +// CHECK2-NEXT: 13: 00008015 8 FUNC WEAK DEFAULT 1 no_veneer1 +// CHECK2-NEXT: 14: 0000801b 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// CHECK2-NEXT: 15: 0000801d 8 FUNC WEAK DEFAULT 1 no_veneer2 +// CHECK2-NEXT: 16: 00008023 2 FUNC WEAK DEFAULT 1 __acle_se_no_veneer2 + + +/// Note that foo retains its address from Import library 1 (0x000020001) +/// New entry functions, baz and qux, use addresses not used by Import library 1. +/// Import library 2 +// CHECK2: File: +// CHECK2: Symbol table '.symtab' contains 7 entries: +// CHECK2-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK2-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK2-NEXT: 1: 00008015 8 FUNC WEAK DEFAULT ABS no_veneer1 +// CHECK2-NEXT: 2: 0000801d 8 FUNC WEAK DEFAULT ABS no_veneer2 +// CHECK2-NEXT: 3: 00020001 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK2-NEXT: 4: 00020009 8 FUNC WEAK DEFAULT ABS bar +// CHECK2-NEXT: 5: 00020011 8 FUNC WEAK DEFAULT ABS baz +// CHECK2-NEXT: 6: 00020019 8 FUNC GLOBAL DEFAULT ABS qux diff --git a/lld/test/ELF/arm-cmse-keep-sections.s b/lld/test/ELF/arm-cmse-keep-sections.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-keep-sections.s @@ -0,0 +1,55 @@ +// REQUIRES: arm +/// Create a secure app and import library using CMSE. +/// Create a non-secure app that refers symbols in the import library. + +// RUN: rm -rf %t && split-file %s %t && cd %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-implib.s -o implib.o -I%S/Inputs/ +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-secure-app.s -o secureapp.o +/// Create the secure app and import library. +// RUN: ld.lld -e secure_entry --section-start .gnu.sgstubs=0x1000000 --section-start SECURE1=0x10 --section-start SECURE2=0x2000000 --cmse-implib implib.o secureapp.o --out-implib=implib.lib -o secureapp --gc-sections +// RUN: llvm-readelf -s implib.lib | FileCheck %s +// RUN: llvm-objdump -d --no-show-raw-insn secureapp | FileCheck %s --check-prefix=DISS + + +// DISS-LABEL: <__acle_se_entry1>: +// DISS-NEXT: 10: nop + +// DISS-LABEL: : +// DISS-NEXT: 1000000: sg +// DISS-LABEL: b.w {{.*}} <__acle_se_entry1> + +// DISS-LABEL: : +// DISS-NEXT: 1000008: sg +// DISS-LABEL: b.w {{.*}} <__acle_se_entry2> + +// DISS-LABEL: <__acle_se_entry2>: +// DISS-NEXT: 2000000: nop + +// CHECK: Symbol table '.symtab' contains {{.*}} entries: +// CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK-NEXT: 1: 01000001 8 FUNC GLOBAL DEFAULT ABS entry1 +// CHECK-NEXT: 2: 01000009 8 FUNC GLOBAL DEFAULT ABS entry2 + +//--- cmse-implib.s + .include "arm-cmse-macros.s" + + .syntax unified + .section SECURE1, "ax" + + cmse_veneer entry1, function, global, function, global + + .syntax unified + .section SECURE2, "ax" + + cmse_veneer entry2, function, global, function, global + +//--- cmse-secure-app.s + .text + .align 2 + // Main entry point. + .global secure_entry + .thumb_func +secure_entry: + bx lr + .size secure_entry, .-secure_entry diff --git a/lld/test/ELF/arm-cmse-noveneers.s b/lld/test/ELF/arm-cmse-noveneers.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-noveneers.s @@ -0,0 +1,30 @@ +// REQUIRES: arm +/// Test that addresses of existing secure gateway veneers are output in the CMSE import library. +/// Test that .gnu.sgstubs is size 0 when no linker synthesized secure gateway veneers are created. + +// 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 --out-implib=%t.lib +// RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s +// RUN: llvm-readelf -S %t | FileCheck %s --check-prefixes=SGSTUBSSIZE +// RUN: llvm-readelf -s %t.lib | FileCheck %s --check-prefixes=IMPLIBSYMS + +// CHECK: Disassembly of section .text: + +// CHECK-LABEL: : +// CHECK-NEXT: 8000: sg +// CHECK-NEXT: 8004: nop + +// CHECK-LABEL: <__acle_se_existing_veneer>: +// CHECK-NEXT: 8006: nop + + .include "arm-cmse-macros.s" + + cmse_no_veneer existing_veneer, function, global, function, global + +/// Name Type Address Off Size ES Flg Lk Inf Al +// SGSTUBSSIZE: .gnu.sgstubs PROGBITS 00020000 020000 000000 08 AX 0 0 32 + +// IMPLIBSYMS: Symbol table '.symtab' contains 2 entries: +// IMPLIBSYMS-NEXT: Num: Value Size Type Bind Vis Ndx Name +// IMPLIBSYMS-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// IMPLIBSYMS-NEXT: 1: 00008001 8 FUNC GLOBAL DEFAULT ABS existing_veneer 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,68 @@ +// REQUIRES: arm +/// Create a secure app and import library using CMSE. +/// Create a non-secure app that refers symbols in the import library. + +// RUN: rm -rf %t && split-file %s %t && cd %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-implib.s -o implib.o -I%S/Inputs/ +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-secure-app.s -o secureapp.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-non-secure-app.s -o nonsecureapp.o +/// Create the secure app and import library. +// RUN: ld.lld -e secure_entry --section-start .gnu.sgstubs=0x20000 --cmse-implib implib.o secureapp.o --out-implib=implib.lib -o secureapp +/// Link the non-secure app against the import library. +// RUN: ld.lld -e nonsecure_entry -Ttext=0x8000 implib.lib nonsecureapp.o -o nonsecureapp +// RUN: llvm-readelf -s implib.lib | FileCheck %s +// RUN: llvm-objdump -d --no-show-raw-insn secureapp | FileCheck %s --check-prefixes=SECUREDISS +// RUN: llvm-objdump -d --no-show-raw-insn nonsecureapp | FileCheck %s --check-prefixes=NONSECUREDISS + +// SECUREDISS-LABEL: : +// SECUREDISS-NEXT: 20000: sg +// SECUREDISS-NEXT: b.w {{.*}} <__acle_se_entry> + +// SECUREDISS-LABEL: <__acle_se_entry>: +// SECUREDISS-NEXT: 20008: nop + +// SECUREDISS-LABEL: : +// SECUREDISS-NEXT: 2000c: bl {{.*}} <__acle_se_entry> +// SECUREDISS-NEXT: bx lr + +// NONSECUREDISS-LABEL: : +// NONSECUREDISS-NEXT: 8000: push {r0, lr} +// NONSECUREDISS-NEXT: bl 0x20000 +// NONSECUREDISS-NEXT: pop.w {r0, lr} +// NONSECUREDISS-NEXT: bx lr + +// CHECK: Symbol table '.symtab' contains 2 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 + +//--- cmse-implib.s + .include "arm-cmse-macros.s" + + .syntax unified + .text + + cmse_veneer entry, function, global, function, global + +//--- cmse-secure-app.s + .align 2 + // Main entry point. + .global secure_entry + .thumb_func +secure_entry: + bl entry + bx lr + .size secure_entry, .-secure_entry + +//--- cmse-non-secure-app.s + .align 2 + .global nonsecure_entry + .thumb + .thumb_func + .type nonsecure_entry, %function +nonsecure_entry: + push {r0,lr} + bl entry + pop {r0,lr} + bx lr + .size nonsecure_entry, .-nonsecure_entry 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,42 @@ +// REQUIRES: arm +/// Test that symbol visibilities of pairs in the objects +/// are preserved in the executable. + +// 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 --no-show-raw-insn %t | FileCheck %s +// RUN: llvm-readelf -s %t | FileCheck %s --check-prefixes=SYM + +// CHECK: Disassembly of section .gnu.sgstubs: + +// CHECK-LABEL: : +// CHECK-NEXT: 20000: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_global_foo> +// CHECK-EMPTY: +// CHECK-LABEL: : +// CHECK-NEXT: 20008: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_weak_bar> +// CHECK-EMPTY: +// CHECK-LABEL: : +// CHECK-NEXT: 20010: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_global_baz> +// CHECK-EMPTY: +// CHECK-LABEL: : +// CHECK-NEXT: 20018: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_weak_qux> + +// SYM: 00020001 {{.*}} GLOBAL {{.*}} global_foo +// SYM: 00008001 {{.*}} GLOBAL {{.*}} __acle_se_global_foo +// SYM: 00020009 {{.*}} WEAK {{.*}} weak_bar +// SYM: 00008005 {{.*}} WEAK {{.*}} __acle_se_weak_bar +// SYM: 00020011 {{.*}} GLOBAL {{.*}} global_baz +// SYM: 00008009 {{.*}} WEAK {{.*}} __acle_se_global_baz +// SYM: 00020019 {{.*}} WEAK {{.*}} weak_qux +// SYM: 0000800d {{.*}} GLOBAL {{.*}} __acle_se_weak_qux + + .include "arm-cmse-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