[lld] 9246df7 - Revert "Revert "[LLD][ELF] Cortex-M Security Extensions (CMSE) Support""

Amilendra Kodithuwakku via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 21 14:31:00 PDT 2023


Author: Amilendra Kodithuwakku
Date: 2023-06-21T22:27:13+01:00
New Revision: 9246df7049b0bb83743f860caff4221413c63de2

URL: https://github.com/llvm/llvm-project/commit/9246df7049b0bb83743f860caff4221413c63de2
DIFF: https://github.com/llvm/llvm-project/commit/9246df7049b0bb83743f860caff4221413c63de2.diff

LOG: Revert "Revert "[LLD][ELF] Cortex-M Security Extensions (CMSE) Support""

This reverts commit a685ddf1d104b3ce9d53cf420521f5aaff429630.

This relands Arm CMSE support (D139092) and fixes the GCC build bot errors.

Added: 
    lld/test/ELF/Inputs/arm-cmse-macros.s
    lld/test/ELF/aarch64-cmse.s
    lld/test/ELF/arm-cmse-diagnostics.s
    lld/test/ELF/arm-cmse-implib.s
    lld/test/ELF/arm-cmse-keep-sections.s
    lld/test/ELF/arm-cmse-noveneers.s
    lld/test/ELF/arm-cmse-secure.s
    lld/test/ELF/arm-cmse-veneers.s

Modified: 
    lld/ELF/Arch/ARM.cpp
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/InputFiles.cpp
    lld/ELF/InputFiles.h
    lld/ELF/LinkerScript.cpp
    lld/ELF/LinkerScript.h
    lld/ELF/MarkLive.cpp
    lld/ELF/Options.td
    lld/ELF/SymbolTable.h
    lld/ELF/SyntheticSections.cpp
    lld/ELF/SyntheticSections.h
    lld/ELF/Target.h
    lld/ELF/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index 79635a22df4ec..2c0e21e01932f 100644
--- 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::support;
 using namespace llvm::ELF;
 using namespace lld;
 using namespace lld::elf;
+using namespace llvm::object;
 
 namespace {
 class ARM final : public TargetInfo {
@@ -910,8 +915,413 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
   }
 }
 
+// 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 <class ELFT> void ObjFile<ELFT>::importCmseSymbols() {
+  ArrayRef<Elf_Sym> eSyms = getELFSyms<ELFT>();
+  // 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<Defined *>(make<SymbolUnion>());
+
+    // 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<std::string> {
+    auto d = dyn_cast_or_null<Defined>(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_<sym>, <sym>] pairs, as specified in the Cortex-M
+// Security Extensions specification.
+// 1) <sym> : A standard function name.
+// 2) __acle_se_<sym> : A special symbol that prefixes the standard function
+// name with __acle_se_.
+// Both these symbols are Thumb function symbols with external linkage.
+// <sym> 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 <sym>, __acle_se_<sym> 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;
+    }
+
+    // <sym> 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 <sym>
+  // with its corresponding special symbol __acle_se_<sym>.
+  parallelForEach(ctx.objectFiles, [&](InputFile *file) {
+    MutableArrayRef<Symbol *> 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;
+    }
+  });
+}
+
+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<Defined>(entryFunc.acleSeSym),
+                cast<Defined>(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 
diff erent, nothing to do.
+  if (acleSeSym->file != sym->file ||
+      cast<Defined>(*acleSeSym).value != cast<Defined>(*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<ArmCmseSGVeneer>(sym, acleSeSym, impSym->value);
+  } else {
+    ss = make<ArmCmseSGVeneer>(sym, acleSeSym);
+    ++newEntries;
+  }
+  ss->parent = this;
+  sgSections.emplace_back(ss);
+}
+
+void ArmCmseSGSection::writeTo(uint8_t *buf) {
+  for (ArmCmseSGVeneer *s : sgSections)
+    s->writeTo(buf + s->outSecOff - getVA());
+}
+
+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 (sgSections.empty())
+    return;
+
+  auto it =
+      std::stable_partition(sgSections.begin(), sgSections.end(),
+                            [](auto *i) { return i->getAddr().has_value(); });
+  std::sort(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 = (*sgSections.begin())->getAddr().has_value()
+                      ? (*sgSections.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 
diff erent from previous link");
+    return;
+  }
+
+  for (ArmCmseSGVeneer *s : sgSections) {
+    if (!s->getAddr().has_value())
+      break;
+    s->outSecOff = s->getAddr().value() & ~1;
+    Defined(s->file, StringRef(), s->sym->binding, s->sym->stOther,
+            s->sym->type, (s->outSecOff - getVA()) | 1, entsize, this)
+        .overwrite(*s->sym);
+  }
+  // This is the partition of veneers newly synthesized by the linker.
+  size_t off = std::max(getVA(), impLibMaxAddr);
+  for (ArmCmseSGVeneer *s : llvm::reverse(sgSections)) {
+    if (s->getAddr().has_value())
+      break;
+    s->outSecOff = off & ~1;
+    Defined(s->file, StringRef(), s->sym->binding, s->sym->stOther,
+            s->sym->type, (s->outSecOff - getVA()) | 1, entsize, this)
+        .overwrite(*s->sym);
+    off += s->entsize;
+  }
+}
+
+void ArmCmseSGVeneer::writeTo(uint8_t *buf) {
+  write16(buf + 0, 0xe97f); // SG
+  write16(buf + 2, 0xe97f);
+  write16(buf + 4, 0xf000); // B.W S
+  write16(buf + 6, 0xb000);
+  target->relocateNoSym(buf + 4, R_ARM_THM_JUMP24,
+                        acleSeSym->getVA() - getVA() - 8);
+}
+
+// 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 <typename ELFT> void elf::writeARMCmseImportLib() {
+  StringTableSection *shstrtab =
+      make<StringTableSection>(".shstrtab", /*dynamic=*/false);
+  StringTableSection *strtab =
+      make<StringTableSection>(".strtab", /*dynamic=*/false);
+  SymbolTableBaseSection *impSymTab = make<SymbolTableSection<ELFT>>(*strtab);
+
+  SmallVector<std::pair<OutputSection *, SyntheticSection *>, 0> osIsPairs;
+  osIsPairs.emplace_back(make<OutputSection>(strtab->name, 0, 0), strtab);
+  osIsPairs.emplace_back(make<OutputSection>(impSymTab->name, 0, 0), impSymTab);
+  osIsPairs.emplace_back(make<OutputSection>(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<Defined>(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<std::unique_ptr<FileOutputBuffer>> 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<FileOutputBuffer> &buffer = *bufferOrErr;
+  uint8_t *const buf = buffer->getBufferStart();
+  memcpy(buf, "\177ELF", 4);
+  auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(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<typename ELFT::Shdr *>(buf + eHdr->e_shoff);
+  for (auto &[osec, _] : osIsPairs)
+    osec->template writeHeaderTo<ELFT>(++sHdrs);
+
+  // Write section contents to a mmap'ed file.
+  {
+    parallel::TaskGroup tg;
+    for (auto &[osec, _] : osIsPairs)
+      osec->template writeTo<ELFT>(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<ELF32LE>();
+template void elf::writeARMCmseImportLib<ELF32BE>();
+template void elf::writeARMCmseImportLib<ELF64LE>();
+template void elf::writeARMCmseImportLib<ELF64BE>();
+
+template void ObjFile<ELF32LE>::importCmseSymbols();
+template void ObjFile<ELF32BE>::importCmseSymbols();
+template void ObjFile<ELF64LE>::importCmseSymbols();
+template void ObjFile<ELF64BE>::importCmseSymbols();

diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 956530098c0f1..699c9d31e9433 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -129,6 +129,7 @@ class LinkerDriver {
 
   std::unique_ptr<BitcodeCompiler> lto;
   std::vector<InputFile *> files;
+  std::optional<InputFile *> armCmseImpLib;
 
 public:
   SmallVector<std::pair<StringRef, unsigned>, 0> archiveFiles;
@@ -173,6 +174,8 @@ struct Config {
   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 @@ struct Config {
   llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
                   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
index 4eb10fc3c91e1..2dbc2f24bc179 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -345,6 +345,22 @@ static void checkOptions() {
   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");
 
@@ -1161,6 +1177,9 @@ static void readConfigs(opt::InputArgList &args) {
   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 =
@@ -1738,6 +1757,12 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
         files.back()->justSymbols = true;
       }
       break;
+    case OPT_in_implib:
+      if (armCmseImpLib)
+        error("multiple CMSE import libraries not supported");
+      else if (std::optional<MemoryBufferRef> mb = readFile(arg->getValue()))
+        armCmseImpLib = createObjFile(*mb);
+      break;
     case OPT_start_group:
       if (InputFile::isInGroup)
         error("nested --start-group");
@@ -2617,6 +2642,8 @@ void LinkerDriver::link(opt::InputArgList &args) {
       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
@@ -2781,6 +2808,9 @@ void LinkerDriver::link(opt::InputArgList &args) {
   if (args.hasArg(OPT_exclude_libs))
     excludeLibs(args);
 
+  // Record [__acle_se_<sym>, <sym>] pairs for later processing.
+  processArmCmseSymbols();
+
   // Apply symbol renames for --wrap and combine foo at v1 and foo@@v1.
   redirectSymbols(wrapped);
 

diff  --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 5f24af5a9c6f5..7c50291352190 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -177,6 +177,15 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
       config->armHasMovtMovw = true;
     break;
   }
+
+  // Only ARMv8-M or later architectures have CMSE support.
+  std::optional<unsigned> 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 @@ template <class ELFT> static void doParseFile(InputFile *file) {
 // Add symbols in File to the symbol table.
 void elf::parseFile(InputFile *file) { invokeELFT(doParseFile, file); }
 
+template <class ELFT> static void doParseArmCMSEImportLib(InputFile *file) {
+  cast<ObjFile<ELFT>>(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 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
   return makeThreadLocal<InputSection>(*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 <class ELFT>
 void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
   ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();

diff  --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 2ee15e89347ab..cc658bdc23198 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -48,6 +48,8 @@ std::optional<MemoryBufferRef> readFile(StringRef path);
 // 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 @@ class InputFile {
     return {symbols.get(), numSymbols};
   }
 
+  MutableArrayRef<Symbol *> 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 @@ template <class ELFT> class ObjFile : public ELFFileBase {
 
   void initSectionsAndLocalSyms(bool ignoreComdats);
   void postParse();
+  void importCmseSymbols();
+  void redirectCmseSymbols();
 
 private:
   void initializeSections(bool ignoreComdats,

diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 777f1f2f85f95..28e9f0461b2d6 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -896,6 +896,15 @@ void LinkerScript::diagnoseOrphanHandling() const {
   }
 }
 
+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/LinkerScript.h b/lld/ELF/LinkerScript.h
index ee0985ae94382..8b8320f9f18e0 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -318,6 +318,7 @@ class LinkerScript final {
 
   void addOrphanSections();
   void diagnoseOrphanHandling() const;
+  void diagnoseMissingSGSectionAddress() const;
   void adjustOutputSections();
   void adjustSectionsAfterSorting();
 

diff  --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 7db7d1c257d46..353c7ead55dfb 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -230,6 +230,10 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     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
index 0c6884afe52d4..e9fbe232d8462 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -80,6 +80,18 @@ defm split_stack_adjust_size
 
 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<"<file>">;
+
+defm out_implib: EEq<"out-implib",
+    "Output the CMSE secure code import library to <file> (required when creating a CMSE secure image)">,
+  MetaVarName<"<file>">;
+
 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
index 9c93ae1876af0..37e31d2372963 100644
--- a/lld/ELF/SymbolTable.h
+++ b/lld/ELF/SymbolTable.h
@@ -19,6 +19,11 @@ namespace lld::elf {
 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 @@ class SymbolTable {
   // is used to uniquify them.
   llvm::DenseMap<llvm::CachedHashStringRef, const InputFile *> comdatGroups;
 
+  // The Map of __acle_se_<sym>, <sym> pairs found in the input objects.
+  // Key is the <sym> name.
+  llvm::SmallMapVector<StringRef, ArmCmseEntryFunction, 1> cmseSymMap;
+
+  // Map of symbols defined in the Arm CMSE import library. The linker must
+  // preserve the addresses in the output objects.
+  llvm::StringMap<Defined *> cmseImportLib;
+
+  // True if <sym> from the input Arm CMSE import library is written to the
+  // output Arm CMSE import library.
+  llvm::StringMap<bool> inCMSEOutImpLib;
+
 private:
   SmallVector<Symbol *, 0> findByVersion(SymbolVersion ver);
   SmallVector<Symbol *, 0> findAllByVersion(SymbolVersion ver,

diff  --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 48f30123dcc00..678b130e9ef63 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -3828,6 +3828,7 @@ void InStruct::reset() {
   got.reset();
   gotPlt.reset();
   igotPlt.reset();
+  armCmseSGSection.reset();
   ppc64LongBranchTarget.reset();
   mipsAbiFlags.reset();
   mipsGot.reset();

diff  --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index a2554c7fd4a73..de07aee9c98cc 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -1143,6 +1143,51 @@ class ThunkSection final : public SyntheticSection {
   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<uint64_t> 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 entsize; };
+  const std::optional<uint64_t> getAddr() const { return entAddr; };
+
+  Symbol *sym;
+  Symbol *acleSeSym;
+
+private:
+  const std::optional<uint64_t> 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);
+  void addMappingSymbol();
+  void finalizeContents() override;
+  void exportEntries(SymbolTableBaseSection *symTab);
+  uint64_t impLibMaxAddr = 0;
+
+private:
+  SmallVector<std::pair<Symbol *, Symbol *>, 0> entries;
+  SmallVector<ArmCmseSGVeneer *, 0> 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 @@ struct InStruct {
   std::unique_ptr<GotSection> got;
   std::unique_ptr<GotPltSection> gotPlt;
   std::unique_ptr<IgotPltSection> igotPlt;
+  std::unique_ptr<SyntheticSection> armCmseSGSection;
   std::unique_ptr<PPC64LongBranchTargetSection> ppc64LongBranchTarget;
   std::unique_ptr<SyntheticSection> mipsAbiFlags;
   std::unique_ptr<MipsGotSection> mipsGot;

diff  --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 59534ab3256df..cec105f967a90 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -200,6 +200,8 @@ static inline std::string getErrorLocation(const uint8_t *loc) {
   return getErrorPlace(loc).loc;
 }
 
+void processArmCmseSymbols();
+
 void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
 
 unsigned getPPCDFormOp(unsigned secondaryOp);
@@ -221,6 +223,7 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn);
 void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);
+template <typename ELFT> void writeARMCmseImportLib();
 void riscvFinalizeRelax(int passes);
 void mergeRISCVAttributesSections();
 

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 3630b199714da..361e0ab33f7cf 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -454,6 +454,11 @@ template <class ELFT> void elf::createSyntheticSections() {
   in.igotPlt = std::make_unique<IgotPltSection>();
   add(*in.igotPlt);
 
+  if (config->emachine == EM_ARM) {
+    in.armCmseSGSection = std::make_unique<ArmCmseSGSection>();
+    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 @@ template <class ELFT> void Writer<ELFT>::run() {
     if (auto e = buffer->commit())
       fatal("failed to write output '" + buffer->getPath() +
             "': " + toString(std::move(e)));
+
+    if (!config->cmseOutputLib.empty())
+      writeARMCmseImportLib<ELFT>();
   }
 }
 
@@ -1983,6 +1991,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
 
   removeUnusedSyntheticSections();
   script->diagnoseOrphanHandling();
+  script->diagnoseMissingSGSectionAddress();
 
   sortSections();
 
@@ -2134,6 +2143,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
     // 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
index 0000000000000..bfa3dcf33258e
--- /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
index 0000000000000..a360d482272d9
--- /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
index 0000000000000..579cc409ad1e2
--- /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 
diff erent 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 'foo' from CMSE import library is not present in secure application
+// WARN_NEWENTRY: warning: new entry function 'foo' 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 
diff erent 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 bar, function,   weak, function,   weak

diff  --git a/lld/test/ELF/arm-cmse-implib.s b/lld/test/ELF/arm-cmse-implib.s
new file mode 100644
index 0000000000000..3f643a132f6cd
--- /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: 00020009     8 FUNC    GLOBAL DEFAULT     2 foo
+// CHECK1-NEXT:      6: 00008005     2 FUNC    GLOBAL DEFAULT     1 __acle_se_foo
+// CHECK1-NEXT:      7: 00020001     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    WEAK   DEFAULT   ABS bar
+// CHECK1-NEXT:      4: 00020009     8 FUNC    GLOBAL DEFAULT   ABS foo
+
+/// 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: 00020019     8 FUNC    WEAK   DEFAULT     2 baz
+// CHECK2-NEXT:      6: 00008005     2 FUNC    GLOBAL DEFAULT     1 __acle_se_baz
+// CHECK2-NEXT:      7: 00020009     8 FUNC    GLOBAL DEFAULT     2 foo
+// CHECK2-NEXT:      8: 00008009     2 FUNC    GLOBAL DEFAULT     1 __acle_se_foo
+// CHECK2-NEXT:      9: 00020001     8 FUNC    WEAK   DEFAULT     2 bar
+// CHECK2-NEXT:     10: 0000800d     2 FUNC    GLOBAL DEFAULT     1 __acle_se_bar
+// CHECK2-NEXT:     11: 00020011     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 (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 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    WEAK   DEFAULT   ABS bar
+// CHECK2-NEXT:      4: 00020009     8 FUNC    GLOBAL DEFAULT   ABS foo
+// CHECK2-NEXT:      5: 00020011     8 FUNC    GLOBAL DEFAULT   ABS qux
+// CHECK2-NEXT:      6: 00020019     8 FUNC    WEAK   DEFAULT   ABS baz

diff  --git a/lld/test/ELF/arm-cmse-keep-sections.s b/lld/test/ELF/arm-cmse-keep-sections.s
new file mode 100644
index 0000000000000..5157da77cd5e8
--- /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: <entry2>:
+// DISS-NEXT: 1000000: sg
+// DISS-LABEL:         b.w {{.*}} <__acle_se_entry2>
+
+// DISS-LABEL: <entry1>:
+// DISS-NEXT: 1000008: sg
+// DISS-LABEL:         b.w {{.*}} <__acle_se_entry1>
+
+// 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 entry2
+// CHECK-NEXT:    2: 01000009   8 FUNC  GLOBAL DEFAULT   ABS entry1
+
+//--- 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
index 0000000000000..19e7a45e356b9
--- /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: <existing_veneer>:
+// 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
index 0000000000000..d6fb661c46493
--- /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: <entry>:
+// SECUREDISS-NEXT:    20000: sg
+// SECUREDISS-NEXT:           b.w {{.*}} <__acle_se_entry>
+
+// SECUREDISS-LABEL: <__acle_se_entry>:
+// SECUREDISS-NEXT:    20008: nop
+
+// SECUREDISS-LABEL: <secure_entry>:
+// SECUREDISS-NEXT:    2000c: bl {{.*}} <__acle_se_entry>
+// SECUREDISS-NEXT:           bx lr
+
+// NONSECUREDISS-LABEL: <nonsecure_entry>:
+// 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
index 0000000000000..e9b4e6d28a956
--- /dev/null
+++ b/lld/test/ELF/arm-cmse-veneers.s
@@ -0,0 +1,42 @@
+// REQUIRES: arm
+/// Test that symbol visibilities of <sym, __acle_se_sym> 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: <weak_qux>:
+// CHECK-NEXT:    20000: sg
+// CHECK-NEXT:           b.w {{.*}} <__acle_se_weak_qux>
+// CHECK-EMPTY:
+// CHECK-LABEL: <global_baz>:
+// CHECK-NEXT:    20008: sg
+// CHECK-NEXT:           b.w {{.*}} <__acle_se_global_baz>
+// CHECK-EMPTY:
+// CHECK-LABEL: <weak_bar>:
+// CHECK-NEXT:    20010: sg
+// CHECK-NEXT:           b.w {{.*}} <__acle_se_weak_bar>
+// CHECK-EMPTY:
+// CHECK-LABEL: <global_foo>:
+// CHECK-NEXT:    20018: sg
+// CHECK-NEXT:           b.w {{.*}} <__acle_se_global_foo>
+
+// SYM: 00020019 {{.*}} GLOBAL {{.*}}           global_foo
+// SYM: 00008001 {{.*}} GLOBAL {{.*}} __acle_se_global_foo
+// SYM: 00020011 {{.*}} WEAK   {{.*}}           weak_bar
+// SYM: 00008005 {{.*}} WEAK   {{.*}} __acle_se_weak_bar
+// SYM: 00020009 {{.*}} GLOBAL {{.*}}           global_baz
+// SYM: 00008009 {{.*}} WEAK   {{.*}} __acle_se_global_baz
+// SYM: 00020001 {{.*}} 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


        


More information about the llvm-commits mailing list