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,14 @@ // //===----------------------------------------------------------------------===// +#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" @@ -19,6 +23,7 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; +using namespace llvm::object; namespace { class ARM final : public TargetInfo { @@ -908,6 +913,403 @@ } } +// Initialize this->symbols. this->symbols is a parallel array as +// its corresponding ELF symbol table. +template void ObjFile::importCmseSymbols() { + 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"); + } + + 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; + + 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 != 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) { + Defined *a = dyn_cast_or_null(acleSeSym); + if (!a) + return (toString(acleSeSym->file) + ": cmse special symbol '" + + acleSeSym->getName() + "' is not defined") + .str(); + + if (!a->isFunc() || (a->value & 1) != 1) + return (toString(a->file) + ": cmse special symbol '" + a->getName() + + "'; is not a Thumb function definition") + .str(); + + if (a->section == nullptr) + return (toString(a->file) + ": cmse special symbol '" + a->getName() + + "'; cannot be an absolute symbol") + .str(); + + Defined *s = dyn_cast_or_null(sym); + if (!s) + return (toString(s->file) + ": cmse entry function '" + s->getName() + + "'; is an undefined symbol") + .str(); + + if (!s->isFunc() || (s->value & 1) != 1) + return (toString(s->file) + ": cmse entry function '" + s->getName() + + "'; is not a Thumb function definition") + .str(); + + if (s->section == nullptr) + return (toString(s->file) + ": cmse entry function '" + s->getName() + + "'; cannot be an absolute symbol") + .str(); + + 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::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 *acleSeSym : symtab.getSymbols()) { + if (!acleSeSym->getName().startswith(ACLESESYM_PREFIX)) + continue; + + if (!config->armCMSESupport) + error("cmse special symbol '" + acleSeSym->getName() + + "' only allowed for ARMv8-M architecture or later"); + + // Try to find the associated symbol definition. + // Symbol must have external linkage. + StringRef name = acleSeSym->getName(); + name.consume_front(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.addCmseSymPair(acleSeSym, 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); + } +} + +ArmCmseSGSection::ArmCmseSGSection() + : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, + llvm::ELF::SHT_PROGBITS, + /*alignment=*/32, ".gnu.sgstubs") { + this->entsize = ACLESESYM_SIZE; + // 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 + sym->size; + } + if (symtab.cmseSymVector.empty()) + return; + 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 &[acleSeSym, sym] : symtab.cmseSymVector) + addSGVeneer(acleSeSym, sym); +} + +void ArmCmseSGSection::addSGVeneer(Symbol *acleSeSym, Symbol *sym) { + entries.push_back(std::make_pair(acleSeSym, sym)); + if (acleSeSym->file != sym->file || + cast(*acleSeSym).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; + if (symtab.cmseImportLib.find(sym->getName()) == symtab.cmseImportLib.end()) { + ss = make(sym, acleSeSym); + newEntries += 1; + } else { + Defined *impSym = symtab.cmseImportLib[sym->getName()]; + ss = make(sym, acleSeSym, impSym->value); + } + ss->parent = this; + this->sgSections.push_back(ss); +} + +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 ? impLibMaxAddr - getVA() : 0) + + (newEntries * entsize); + + return entries.size() * entsize; +} + +void ArmCmseSGSection::finalizeContents() { + if (this->sgSections.empty()) + return; + + auto it = std::stable_partition( + this->sgSections.begin(), this->sgSections.end(), + [](auto *i) { return i->getAddr() != std::nullopt; }); + + std::sort(this->sgSections.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 = (*this->sgSections.begin())->getAddr() == std::nullopt + ? getVA() + : (*this->sgSections.begin())->getAddr().value(); + // 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 (auto *s : this->sgSections) { + if (s->getAddr() == std::nullopt) + break; + s->outSecOff = s->getAddr().value() & ~1; + Defined(s->file, StringRef(), s->sym->binding, s->sym->stOther, + s->sym->type, (s->outSecOff - getVA()) | 1, this->entsize, this) + .overwrite(*s->sym); + } + // This is the partition of veneers newly synthesized by the linker. + size_t off = std::max(getVA(), impLibMaxAddr); + for (auto *s : llvm::reverse(this->sgSections)) { + if (s->getAddr() != std::nullopt) + break; + s->outSecOff = off & ~1; + Defined(s->file, StringRef(), s->sym->binding, s->sym->stOther, + s->sym->type, (s->outSecOff - getVA()) | 1, this->entsize, this) + .overwrite(*s->sym); + off += s->entsize; + } +} + +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 = acleSeSym->getVA(); + uint64_t p = getVA() + 4; + target->relocateNoSym(buf + 4, R_ARM_THM_JUMP24, s - p - 4); +} + +// Write the CMSE import library to disk. +// The CMSE import library is a relocatable object with only a symbol table. +// The symbols are copies of the (absolute) symbols of the secure gateways +// in the executable output by this link. +// See ArmĀ® v8-M Security Extensions: Requirements on Development Tools +// https://developer.arm.com/documentation/ecm0359818/latest +template void elf::writeARMCmseImportLib() { + if (config->cmseOutputLib.empty()) + return; + + StringTableSection *shstrtab = + make(".shstrtab", /*dynamic=*/false); + StringTableSection *strtab = + make(".strtab", /*dynamic=*/false); + SymbolTableBaseSection *impSymTab = make>(*strtab); + + SmallVector, 0> osIsPairs; + osIsPairs.push_back( + std::make_pair(make(impSymTab->name, 0, 0), impSymTab)); + 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 to the import library symbol table. + for (const auto &[_, sym] : symtab.cmseSymVector) { + Defined *d = cast(sym); + impSymTab->addSymbol(makeDefined(nullptr, d->getName(), d->computeBinding(), + /*stOther=*/0, STT_FUNC, d->getVA(), + d->getSize(), nullptr)); + } + + // 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; + } + + // Write the ELF Header + std::unique_ptr &buffer = *bufferOrErr; + uint8_t *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->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(); + +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); + 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 @@ -122,6 +122,7 @@ std::unique_ptr lto; std::vector files; + std::vector armCmseImpLib; public: SmallVector, 0> archiveFiles; @@ -166,6 +167,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 +189,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,15 @@ 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 +1123,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 = @@ -1663,6 +1675,12 @@ files.back()->justSymbols = true; } break; + case OPT_in_implib: + if (!armCmseImpLib.empty()) + error("multiple CMSE import libraries not supported"); + else if (std::optional mb = readFile(arg->getValue())) + armCmseImpLib.push_back(createObjFile(*mb)); + break; case OPT_start_group: if (InputFile::isInGroup) error("nested --start-group"); @@ -2539,9 +2557,14 @@ { llvm::TimeTraceScope timeScope("Parse input files"); for (size_t i = 0; i < files.size(); ++i) { - llvm::TimeTraceScope timeScope("Parse input files", files[i]->getName()); + llvm::TimeTraceScope timeScope("Parse input file", files[i]->getName()); parseFile(files[i]); } + if (!armCmseImpLib.empty()) { + llvm::TimeTraceScope timeScope("Parse Arm CMSE input lib", + armCmseImpLib[0]->getName()); + parseArmCMSEImportLib(armCmseImpLib[0]); + } } // Now that we have every file, we can decide if we will need a @@ -2706,6 +2729,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 @@ -48,6 +48,9 @@ // Add symbols in File to the symbol table. void parseFile(InputFile *file); +// Add symbols in File to the symbol table. +void parseArmCMSEImportLib(InputFile *file); + // The root class of input files. class InputFile { protected: @@ -281,6 +284,7 @@ void initSectionsAndLocalSyms(bool ignoreComdats); void postParse(); + void importCmseSymbols(); 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,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) @@ -294,6 +298,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)); @@ -1120,6 +1132,7 @@ template void ObjFile::postParse() { static std::mutex mu; ArrayRef eSyms = this->getELFSyms(); + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; Symbol &sym = *symbols[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 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 @@ -900,6 +900,42 @@ } } +void LinkerScript::diagnoseMissingSGSectionAddress() const { + if (!config->cmseImplib) + return; + + bool sgStubsFound = false; + for (InputSectionBase *isec : ctx.inputSections) { + if (getOutputSectionName(isec) == ".gnu.sgstubs") { + sgStubsFound = true; + break; + } + } + if (!sgStubsFound) + return; + + auto inLinkerScript = [&]() { + // Nothing to clash if there are no section commands. + if (!hasSectionsCommand) + return false; + // Cannot clash if .gnu.sgstubs is an orphan section. + for (const InputSectionBase *isec : orphanSections) + if (getOutputSectionName(isec) == ".gnu.sgstubs") + return false; + return true; + }; + + // --section-start .gnu.sgstubs= specified via command line + auto inCmdLine = config->sectionStartMap.find(".gnu.sgstubs") != + config->sectionStartMap.end(); + auto linkerscript = inLinkerScript(); + if (inCmdLine && linkerscript) + warn("addresses assigned to the veneers output section .gnu.sgstubs via " + "--section-start and linker script may potentially clash"); + if (!inCmdLine && !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 *acleSeSym, 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 *acleSeSym, Symbol *sym) { + cmseSymVector.push_back(std::make_pair(acleSeSym, 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 @@ -1143,6 +1143,51 @@ 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 : public SyntheticSection { +public: + ArmCmseSGVeneer(Symbol *sym, Symbol *acleSeSym, + std::optional addr = std::nullopt) + : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, + llvm::ELF::SHT_PROGBITS, + /*alignment=*/32, ".gnu.sgstubs"), + sym(sym), acleSeSym(acleSeSym), entAddr{addr} { + entsize = ACLESESYM_SIZE; + } + + void writeTo(uint8_t *buf) override; + size_t getSize() const override { return this->entsize; }; + const std::optional getAddr() const { return this->entAddr; }; + + Symbol *sym; + Symbol *acleSeSym; + +private: + const std::optional entAddr; +}; + +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; +}; + // 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 +1324,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 @@ -3831,6 +3831,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 @@ -194,6 +194,8 @@ return getErrorPlace(loc).loc; } +void processArmCmseSymbol(); + void writePPC32GlinkSection(uint8_t *buf, size_t numEntries); unsigned getPPCDFormOp(unsigned secondaryOp); @@ -215,6 +217,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->diagnoseMissingSGSectionAddress(); 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-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-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,45 @@ +/// Test that the address of .gnu.sgstubs can be specified via command line or linker script. +/// Test that the linker warns when the address of .gnu.sgstubs is specified via both command line and linker script. +/// Test that the linker errors when the address of .gnu.sgstubs is not specified using either method. + +// REQUIRES: arm +// RUN: split-file %s %t + +// RUN: llvm-mc -arm-add-build-attributes --triple=thumbv8m.main -filetype=obj -I%S/Inputs/ %t/asm -o %t.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script %t/with_sgstubs.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 %t/wout_sgstubs.script %t.o -o %t2 2>&1 | FileCheck %s --check-prefixes=NOWARN,NOERROR +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t3 2>&1 | FileCheck %s --check-prefixes=NOWARN,NOERROR +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --script %t/with_sgstubs.script %t.o -o %t4 2>&1 | FileCheck %s --check-prefixes=NOWARN,NOERROR +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --script %t/wout_sgstubs.script %t.o -o %t5 2>&1 | FileCheck %s --check-prefix=ERROR + +// RUN: llvm-readelf -S %t1 | FileCheck %s --check-prefix=ADDRCMDLINE +// RUN: llvm-readelf -S %t2 | FileCheck %s --check-prefix=ADDRCMDLINE +// RUN: llvm-readelf -S %t3 | FileCheck %s --check-prefix=ADDRCMDLINE +// RUN: llvm-readelf -S %t4 | FileCheck %s --check-prefix=ADDRLNKSCRIPT + +// 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: + +/// 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 + +//--- asm +.include "arm-cmse-macros.s" + + .align 2 +cmse_veneer foo, function, global, function, global + +//--- with_sgstubs.script +SECTIONS { + .text : { *(.text) } + .gnu.sgstubs 0x40000 : { *(.gnu.sgstubs*) } +} + +//--- wout_sgstubs.script +SECTIONS { + .text : { *(.text) } +} 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 "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: + .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' is not defined +// 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-error.s b/lld/test/ELF/arm-cmse-implib-error.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-implib-error.s @@ -0,0 +1,73 @@ +// REQUIRES: arm +// RUN: split-file %s %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %t/lib -o %t.lib +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %t/app -o %tapp.o +// RUN: llvm-objcopy --redefine-sym=entry7_duplicate=entry6_duplicate %t.lib +// RUN: not ld.lld --cmse-implib --in-implib=%t.lib %tapp.o -o /dev/null 2>&1 | FileCheck %s +// RUN: not ld.lld --cmse-implib --in-implib=%t.lib --in-implib=%t.lib %tapp.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=MULT_INIMPLIB_ERR + +//--- app + .align 2 + .global entry + .thumb + .thumb_func + .type entry, %function +entry: + bx lr + .size entry, .-entry + +//--- lib + .text + .thumb + + /// Symbol not absolute. + .global entry1_not_absolute + .type entry1_not_absolute, STT_FUNC + .thumb_func +entry1_not_absolute: + .size entry1_not_absolute, 8 + + /// Symbol not global or weak. + .local entry2_not_external + .type entry2_not_external, STT_FUNC +entry2_not_external=0x1001 + .size entry2_not_external, 8 + + /// Symbol not the function type. + .global entry3_not_function + .type entry3_not_function, STT_NOTYPE +entry3_not_function=0x1001 + .size entry3_not_function, 8 + + /// Symbol not a Thumb code symbol. + .global entry4_not_thumb + .type entry4_not_thumb, STT_FUNC +entry4_not_thumb=0x1000 + .size entry4_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 + +// CHECK: error: CMSE symbol entry2_not_external in import library {{.*}} is not global +// CHECK: error: CMSE symbol entry1_not_absolute in import library {{.*}} is not absolute +// CHECK: error: CMSE symbol entry3_not_function in import library {{.*}} is not of function type +// CHECK: error: CMSE symbol entry4_not_thumb in import library {{.*}} is not a Thumb code symbol +// CHECK: warning: CMSE symbol entry5_incorrect_size in import library {{.*}} does not have correct size of 8 bytes +// CHECK: error: CMSE symbol entry6_duplicate is multiply defined in import library {{.*}} + +// MULT_INIMPLIB_ERR: error: multiple CMSE import libraries not supported diff --git a/lld/test/ELF/arm-cmse-implib-startaddress-mismatch.s b/lld/test/ELF/arm-cmse-implib-startaddress-mismatch.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-implib-startaddress-mismatch.s @@ -0,0 +1,20 @@ +/// 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. + +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %s -I %S/Inputs -o %t.o +/// Create a CMSE import library with a secure gateway veneer at 0x10000 +// RUN: ld.lld --cmse-implib --section-start .gnu.sgstubs=0x10000 %t.o -o %t --out-implib=%t.lib 2>&1 | FileCheck %s --check-prefix=NOERROR +/// 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 %t.o -o %t2 --out-implib=%t2.lib --in-implib=%t.lib 2>&1 | FileCheck %s --check-prefix=NOERROR +/// 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 %t.o -o %t3 --out-implib=%t3.lib --in-implib=%t.lib 2>&1 | FileCheck %s --check-prefix=ERROR +/// 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 %t.o -o %t4 --out-implib=%t4.lib --in-implib=%t.lib 2>&1 | FileCheck %s --check-prefix=ERROR + + .include "arm-cmse-macros.s" + + cmse_veneer entry, function, global, function, global + +// NOERROR-NOT: error: +// ERROR: error: start address of '.gnu.sgstubs' is different from previous link 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,110 @@ +/// Test that addresses of secure gateways in an old import library are maintained in new import libraries. + +// REQUIRES: arm +// RUN: split-file %s %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %t/app -o %tapp.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %t/implib-v1 -I %S/Inputs -o %t1.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %t/implib-v2 -I %S/Inputs -o %t2.o + +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t1 %tapp.o --out-implib=%t1.lib --cmse-implib %t1.o +// RUN: llvm-readelf -s %t1 %t1.lib | FileCheck %s --check-prefix=CHECK1 + +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t2 %tapp.o --out-implib=%t2.lib --in-implib=%t1.lib --cmse-implib %t2.o +// RUN: llvm-readelf -s %t2 %t2.lib | FileCheck %s --check-prefix=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 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: 00020009 8 FUNC WEAK DEFAULT ABS bar +// CHECK1-NEXT: 2: 00020001 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK1-NEXT: 3: 0000800d 8 FUNC WEAK DEFAULT ABS no_veneer1 +// CHECK1-NEXT: 4: 00008015 8 FUNC WEAK DEFAULT ABS no_veneer2 + +/// Executable 2 +// CHECK2: File: +// CHECK2: Symbol table '.symtab' contains 15 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: 00020019 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: 00020011 8 FUNC GLOBAL DEFAULT 2 qux +// CHECK2-NEXT: 10: 0000800d 2 FUNC GLOBAL DEFAULT 1 __acle_se_qux +// CHECK2-NEXT: 11: 00008011 8 FUNC WEAK DEFAULT 1 no_veneer1 +// CHECK2-NEXT: 12: 00008017 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// CHECK2-NEXT: 13: 00008019 8 FUNC WEAK DEFAULT 1 no_veneer2 +// CHECK2-NEXT: 14: 0000801f 2 FUNC WEAK DEFAULT 1 __acle_se_no_veneer2 + + +/// 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: 00020019 8 FUNC WEAK DEFAULT ABS baz +// CHECK2-NEXT: 2: 00020001 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK2-NEXT: 3: 00008011 8 FUNC WEAK DEFAULT ABS no_veneer1 +// CHECK2-NEXT: 4: 00008019 8 FUNC WEAK DEFAULT ABS no_veneer2 +// CHECK2-NEXT: 5: 00020011 8 FUNC GLOBAL DEFAULT ABS 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 "arm-cmse-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-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 @@ +/// 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. + +// 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 --out-implib=%t.lib +// RUN: llvm-objdump -d %t | FileCheck %s +// RUN: llvm-readelf -S %t | FileCheck %s --check-prefix=SGSTUBSSIZE +// RUN: llvm-readelf -s %t.lib | FileCheck %s --check-prefix=IMPLIBSYMS + +// CHECK: Disassembly of section .text: + +// CHECK: 00008000 : +// CHECK-NEXT: 8000: e97f e97f sg +// CHECK-NEXT: 8004: bf00 nop + +// CHECK: 00008006 <__acle_se_existing_veneer>: +// CHECK-NEXT: 8006: bf00 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,65 @@ +// REQUIRES: arm +// RUN: split-file %s %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %t/cmse-implib.s -o %timplib.o -I%S/Inputs/ +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %t/cmse-secure-app.s -o %tsecureapp.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %t/cmse-non-secure-app.s -o %tnonsecureapp.o +/// Create the secure app and import library. +// RUN: ld.lld --entry=secure_entry --section-start .gnu.sgstubs=0x20000 --cmse-implib %timplib.o %tsecureapp.o --out-implib=%timplib.lib -o %tsecureapp +/// Link the non-secure app against the import library +// RUN: ld.lld --entry=nonsecure_entry -Ttext=0x8000 -o %t1 --in-implib=%timplib.lib --cmse-implib %tnonsecureapp.o -o %tnonsecureapp +// RUN: llvm-readelf -s %timplib.lib | FileCheck %s +// RUN: llvm-objdump -D %tsecureapp | FileCheck %s --check-prefix=SECUREDISS +// RUN: llvm-objdump -D %tnonsecureapp | FileCheck %s --check-prefix=NONSECUREDISS + +// SECUREDISS: 00020000 : +// SECUREDISS-NEXT: 20000: e97f e97f sg +// SECUREDISS-NEXT: 20004: f000 b800 b.w 0x20008 <__acle_se_entry> + +// SECUREDISS: 00020008 <__acle_se_entry>: +// SECUREDISS-NEXT: 20008: bf00 nop + +// SECUREDISS: 0002000c : +// SECUREDISS-NEXT: 2000c: f7ff fff8 bl 0x20000 +// SECUREDISS-NEXT: 20010: 4770 bx lr + +// NONSECUREDISS: 00008000 : +// NONSECUREDISS-NEXT: 8000: b501 push {r0, lr} +// NONSECUREDISS-NEXT: 8002: f017 fffd bl 0x20000 +// NONSECUREDISS-NEXT: 8006: e8bd 4001 pop.w {r0, lr} +// NONSECUREDISS-NEXT: 800a: 4770 bx lr + +//--- 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 + +// 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 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 + +// 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 b802 b.w 0x800c <__acle_se_weak_qux> + +// CHECK: 00020008 : +// CHECK-NEXT: 20008: e97f e97f sg +// CHECK-NEXT: 2000c: f7e7 bffa b.w 0x8004 <__acle_se_weak_bar> + +// CHECK: 00020010 : +// CHECK-NEXT: 20010: e97f e97f sg +// CHECK-NEXT: 20014: f7e7 bff4 b.w 0x8000 <__acle_se_global_foo> + +// CHECK: 00020018 : +// CHECK-NEXT: 20018: e97f e97f sg +// CHECK-NEXT: 2001c: f7e7 bff4 b.w 0x8008 <__acle_se_global_baz> + + .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