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,7 +913,358 @@ } } +// Initialize symbols. symbols is a parallel array as its corresponding ELF +// symbol table. +template void ObjFile::importCmseSymbols() { + ArrayRef eSyms = 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) { + errorOrWarn("CMSE symbol '" + CHECK(eSyms[i].getName(stringTable), this) + + "' in import library '" + toString(this) + "' is not global"); + } + + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + Defined *sym = reinterpret_cast(make()); + + // Initialize symbol fields. + memset(sym, 0, sizeof(Symbol)); + sym->setName(CHECK(eSyms[i].getName(stringTable), this)); + sym->value = eSym.st_value; + sym->size = eSym.st_size; + + if (eSym.st_shndx != SHN_ABS) { + error("CMSE symbol '" + sym->getName() + "' in import library '" + + toString(this) + "' is not absolute"); + continue; + } + + if (!(eSym.st_value & 1) || (eSym.getType() != STT_FUNC)) { + error("CMSE symbol '" + sym->getName() + "' in import library '" + + toString(this) + "' is not a Thumb function definition"); + continue; + } + + if (symtab.cmseImportLib.count(sym->getName())) { + error("CMSE symbol '" + sym->getName() + + "' is multiply defined in import library '" + toString(this) + "'"); + continue; + } + + if (eSym.st_size != ACLESESYM_SIZE) { + warn("CMSE symbol '" + sym->getName() + "' in import library '" + + toString(this) + "' does not have correct size of " + + Twine(ACLESESYM_SIZE) + " bytes"); + } + + symtab.cmseImportLib[sym->getName()] = sym; + } +} + +// Check symbol attributes of the acleSeSym, sym pair. +// Both symbols should be global/weak Thumb code symbol definitions. +static std::string checkCmseSymAttributes(Symbol *acleSeSym, Symbol *sym) { + auto check = [](Symbol *s, StringRef type) -> std::optional { + auto d = dyn_cast_or_null(s); + if (!(d && d->isFunc() && (d->value & 1))) + return (Twine(toString(s->file)) + ": cmse " + type + " symbol '" + + s->getName() + "' is not a Thumb function definition") + .str(); + if (!d->section) + return (Twine(toString(s->file)) + ": cmse " + type + " symbol '" + + s->getName() + "' cannot be an absolute symbol") + .str(); + return std::nullopt; + }; + for (auto [sym, type] : + {std::make_pair(acleSeSym, "special"), std::make_pair(sym, "entry")}) + if (auto err = check(sym, type)) + return *err; + return ""; +} + +// Look for [acle_se_, ] pairs, as specified in the Cortex-M Security +// Extensions specification. +// 1) : A standard function name. +// 2) acle_se_ : A special symbol that prefixes the standard function name +// with __acle_se_. +// Both these symbols are Thumb function symbols with external linkage. +// may be redefined in .gnu.sgstubs. +void elf::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 input object build attributes do not support CMSE, error and disable + // further scanning for , acle_se_ pairs + if (!config->armCMSESupport) { + error("CMSE is only supported by ARMv8-M architecture or later"); + config->cmseImplib = false; + break; + } + + // Try to find the associated symbol definition. + // Symbol must have external linkage. + StringRef name = acleSeSym->getName().substr(std::strlen(ACLESESYM_PREFIX)); + Symbol *sym = symtab.find(name); + if (!sym) { + error(toString(acleSeSym->file) + ": cmse special symbol '" + + acleSeSym->getName() + + "' detected, but no associated entry function definition '" + name + + "' with external linkage found"); + continue; + } + + std::string errMsg = checkCmseSymAttributes(acleSeSym, sym); + if (!errMsg.empty()) { + error(errMsg); + continue; + } + + // may be redefined later in the link in .gnu.sgstubs + symtab.cmseSymVector.emplace_back(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") { + 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.emplace_back(std::make_pair(acleSeSym, sym)); + // Symbol addresses different, nothing to do. + if (acleSeSym->file != sym->file || + cast(*acleSeSym).value != cast(*sym).value) + return; + // Only secure symbols with values equal to that of it's non-secure + // counterpart needs to be in the .gnu.sgstubs section. + ArmCmseSGVeneer *ss = nullptr; + if (symtab.cmseImportLib.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; + sgSections.emplace_back(ss); +} + +void ArmCmseSGSection::writeTo(uint8_t *buf) { + for (auto *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 different from previous link"); + return; + } + + for (auto *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 (auto *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) { + const uint8_t data[] = { + 0x7f, 0xe9, 0x7f, 0xe9, // SG + 0x00, 0xf0, 0x00, 0xb0, // B.W S + }; + memcpy(buf, data, sizeof(data)); + 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 void elf::writeARMCmseImportLib() { + 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(strtab->name, 0, 0), strtab)); + osIsPairs.push_back( + std::make_pair(make(impSymTab->name, 0, 0), impSymTab)); + osIsPairs.push_back( + std::make_pair(make(shstrtab->name, 0, 0), shstrtab)); + + // 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)); + } + + size_t idx = 0; + uint64_t off = sizeof(typename ELFT::Ehdr); + for (auto &[osec, isec] : osIsPairs) { + osec->sectionIndex = ++idx; + osec->recordSection(isec); + osec->finalizeInputSections(); + osec->shName = shstrtab->addString(osec->name); + osec->size = isec->getSize(); + isec->finalizeContents(); + osec->offset = alignToPowerOf2(off, osec->addralign); + off = osec->offset + osec->size; + } + + const uint64_t sectionHeaderOff = alignToPowerOf2(off, config->wordsize); + const auto shnum = osIsPairs.size() + 1; + const uint64_t fileSize = + sectionHeaderOff + shnum * sizeof(typename ELFT::Shdr); + const unsigned flags = + config->mmapOutputFile ? 0 : (unsigned)FileOutputBuffer::F_no_mmap; + unlinkAsync(config->cmseOutputLib); + Expected> bufferOrErr = + FileOutputBuffer::create(config->cmseOutputLib, fileSize, flags); + if (!bufferOrErr) { + error("failed to open " + config->cmseOutputLib + ": " + + llvm::toString(bufferOrErr.takeError())); + return; + } + + // Write the ELF Header + std::unique_ptr &buffer = *bufferOrErr; + uint8_t *const buf = buffer->getBufferStart(); + memcpy(buf, "\177ELF", 4); + auto *eHdr = reinterpret_cast(buf); + eHdr->e_type = ET_REL; + eHdr->e_entry = 0; + eHdr->e_shoff = sectionHeaderOff; + eHdr->e_ident[EI_CLASS] = ELFCLASS32; + eHdr->e_ident[EI_DATA] = config->isLE ? ELFDATA2LSB : ELFDATA2MSB; + eHdr->e_ident[EI_VERSION] = EV_CURRENT; + eHdr->e_ident[EI_OSABI] = config->osabi; + eHdr->e_ident[EI_ABIVERSION] = 0; + eHdr->e_machine = EM_ARM; + eHdr->e_version = EV_CURRENT; + eHdr->e_flags = config->eflags; + eHdr->e_ehsize = sizeof(typename ELFT::Ehdr); + eHdr->e_phnum = 0; + eHdr->e_shentsize = sizeof(typename ELFT::Shdr); + eHdr->e_phoff = 0; + eHdr->e_phentsize = 0; + eHdr->e_shnum = shnum; + eHdr->e_shstrndx = shstrtab->getParent()->sectionIndex; + + // Write the section header table. + auto *sHdrs = reinterpret_cast(buf + eHdr->e_shoff); + for (auto &[osec, _] : osIsPairs) + osec->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))); +} + TargetInfo *elf::getARMTargetInfo() { static ARM target; return ⌖ } + +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); + +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); +template void ObjFile::importCmseSymbols(); diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -129,6 +129,7 @@ std::unique_ptr lto; std::vector files; + std::optional armCmseImpLib; public: SmallVector, 0> archiveFiles; @@ -173,6 +174,8 @@ llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; llvm::StringRef whyExtract; + llvm::StringRef cmseInputLib; + llvm::StringRef cmseOutputLib; StringRef zBtiReport = "none"; StringRef zCetReport = "none"; llvm::StringRef ltoBasicBlockSections; @@ -193,11 +196,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,22 @@ if (config->emachine == EM_MIPS && config->gnuHash) error("the .gnu.hash section is not compatible with the MIPS target"); + if (config->emachine == EM_ARM) { + if (!config->cmseImplib) { + if (!config->cmseInputLib.empty()) + error("--in-implib may not be used without --cmse-implib"); + if (!config->cmseOutputLib.empty()) + error("--out-implib may not be used without --cmse-implib"); + } + } else { + if (config->cmseImplib) + error("--cmse-implib is only supported on ARM targets"); + if (!config->cmseInputLib.empty()) + error("--in-implib is only supported on ARM targets"); + if (!config->cmseOutputLib.empty()) + error("--out-implib is only supported on ARM targets"); + } + if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); @@ -1123,6 +1139,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 = @@ -1672,6 +1691,12 @@ files.back()->justSymbols = true; } break; + case OPT_in_implib: + if (armCmseImpLib) + error("multiple CMSE import libraries not supported"); + else if (std::optional mb = readFile(arg->getValue())) + armCmseImpLib = createObjFile(*mb); + break; case OPT_start_group: if (InputFile::isInGroup) error("nested --start-group"); @@ -2548,9 +2573,10 @@ { 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]); } + parseArmCMSEImportLib(armCmseImpLib); } // Now that we have every file, we can decide if we will need a @@ -2715,6 +2741,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(std::optional file); + // The root class of input files. class InputFile { protected: @@ -280,6 +283,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,15 @@ // 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(std::optional file) { + if (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)); 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 @@ -902,6 +902,15 @@ } } +void LinkerScript::diagnoseMissingSGSectionAddress() const { + if (!config->cmseImplib || !in.armCmseSGSection->isNeeded()) + return; + + OutputSection *sec = findByName(sectionCommands, ".gnu.sgstubs"); + if (sec && !sec->addrExpr && !config->sectionStartMap.count(".gnu.sgstubs")) + error("no address assigned to the veneers output section " + sec->name); +} + // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned in the first member of the pair. Otherwise, a nullptr is returned. diff --git a/lld/ELF/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,12 @@ // is used to uniquify them. llvm::DenseMap comdatGroups; + // 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/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1143,6 +1143,52 @@ 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 entsize; }; + const std::optional getAddr() const { return 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); + 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 +1325,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 @@ -3819,6 +3819,7 @@ got.reset(); gotPlt.reset(); igotPlt.reset(); + armCmseSGSection.reset(); ppc64LongBranchTarget.reset(); mipsAbiFlags.reset(); mipsGot.reset(); diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -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 @@ -454,6 +454,11 @@ in.igotPlt = std::make_unique(); add(*in.igotPlt); + if (config->emachine == EM_ARM) { + in.armCmseSGSection = std::make_unique(); + add(*in.armCmseSGSection); + } + // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat // it as a relocation and ensure the referenced section is created. if (ElfSym::globalOffsetTable && config->emachine != EM_MIPS) { @@ -591,6 +596,9 @@ if (auto e = buffer->commit()) fatal("failed to write output '" + buffer->getPath() + "': " + toString(std::move(e))); + + if (!config->cmseOutputLib.empty()) + writeARMCmseImportLib(); } } @@ -2001,6 +2009,7 @@ removeUnusedSyntheticSections(); script->diagnoseOrphanHandling(); + script->diagnoseMissingSGSectionAddress(); sortSections(); @@ -2149,6 +2158,7 @@ // static symbol table. finalizeSynthetic(in.symTab.get()); finalizeSynthetic(in.ppc64LongBranchTarget.get()); + finalizeSynthetic(in.armCmseSGSection.get()); } // Relaxation to delete inter-basic block jumps created by basic block diff --git a/lld/test/ELF/Inputs/arm-cmse-macros.s b/lld/test/ELF/Inputs/arm-cmse-macros.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/arm-cmse-macros.s @@ -0,0 +1,32 @@ +/// Because the addresses of __acle_se_\sym_name and \sym_name are equal, +/// the linker creates a secure gateway in ".gnu.sgstubs". +.macro cmse_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding +.align 2 +.\sym_binding \sym_name +.\acle_sym_binding __acle_se_\sym_name +.type \sym_name, %\sym_type +.type __acle_se_\sym_name, %\acle_sym_type +\sym_name: +__acle_se_\sym_name: + nop +.size \sym_name, .-\sym_name +.size __acle_se_\sym_name, .-__acle_se_\sym_name +.endm + +/// Because the addresses of __acle_se_\sym_name and \sym_name are not equal, +/// the linker considers that an inline secure gateway exists and does not +/// create one. +.macro cmse_no_veneer sym_name, sym_type, sym_binding, acle_sym_type, acle_sym_binding +.align 2 +.\sym_binding \sym_name +.\acle_sym_binding __acle_se_\sym_name +.type \sym_name, %\sym_type +.type __acle_se_\sym_name, %\acle_sym_type +\sym_name: + sg + nop +__acle_se_\sym_name: + nop +.size \sym_name, .-\sym_name +.size __acle_se_\sym_name, .-__acle_se_\sym_name +.endm diff --git a/lld/test/ELF/aarch64-cmse.s b/lld/test/ELF/aarch64-cmse.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/aarch64-cmse.s @@ -0,0 +1,16 @@ +# REQUIRES: aarch64 +# RUN: yaml2obj %s -o %t.o +# RUN: not ld.lld --cmse-implib %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR_CMSE_IMPLIB +# RUN: not ld.lld --in-implib=%t.o %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR_IN_IMPLIB +# RUN: not ld.lld --out-implib=out.lib %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR_OUT_IMPLIB + +# ERR_CMSE_IMPLIB: error: --cmse-implib is only supported on ARM targets +# ERR_IN_IMPLIB: error: --in-implib is only supported on ARM targets +# ERR_OUT_IMPLIB: error: --out-implib is only supported on ARM targets + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 diff --git a/lld/test/ELF/arm-cmse-diagnostics.s b/lld/test/ELF/arm-cmse-diagnostics.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-diagnostics.s @@ -0,0 +1,259 @@ +// 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: + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumbv9a app -I %S/Inputs -o app4.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app4 app4.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=armv7-m app -I %S/Inputs -o app5.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app5 app5.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv8-m app -I %S/Inputs -o app6.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app6 app6.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +/// Invalid triple defaults to v4T. Error. +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=thumb app -I %S/Inputs -o app7.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app7 app7.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +/// No build attributes. Error. +// RUN: llvm-mc -filetype=obj -triple=thumb app -I %S/Inputs -o app8.o +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o app8 app8.o 2>&1 | FileCheck %s --check-prefixes=ERR_ARCH + +// ERR_ARCH: CMSE is only supported by ARMv8-M architecture or later + +/// Test that the linker diagnoses cases where the linker synthesized veneer addresses +/// specified by the CMSE input library cannot be placed at the .gnu.sgstubs section address. + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main app -I %S/Inputs -o 1.o +/// Create a CMSE import library with a secure gateway veneer at 0x10000 +// RUN: ld.lld --cmse-implib --section-start .gnu.sgstubs=0x10000 1.o -o 1 --out-implib=1.lib 2>&1 | FileCheck /dev/null --implicit-check-not=error: +/// Create a new import library with the secure gateway veneer and .gnu.sgstubs specified at the same address +// RUN: ld.lld --cmse-implib --section-start .gnu.sgstubs=0x10000 1.o -o 2 --out-implib=2.lib --in-implib=1.lib 2>&1 | FileCheck /dev/null --implicit-check-not=error: +/// Create a new import library with the secure gateway veneer specified at a same address but .gnu.sgstubs at a higher address. +// RUN: not ld.lld --cmse-implib --section-start .gnu.sgstubs=0x11000 1.o -o 3 --out-implib=3.lib --in-implib=1.lib 2>&1 | FileCheck %s --check-prefixes=ERR_ADDR +/// Create a new import library with the secure gateway veneer specified at a same address but .gnu.sgstubs at a lower address. +// RUN: not ld.lld --cmse-implib --section-start .gnu.sgstubs=0x9000 1.o -o 4 --out-implib=4.lib --in-implib=1.lib 2>&1 | FileCheck %s --check-prefixes=ERR_ADDR + +// ERR_ADDR: error: start address of '.gnu.sgstubs' is different from previous link + +/// Test that the address of .gnu.sgstubs can be specified via command line or linker script. +/// Test that the linker errors when the address of .gnu.sgstubs is not specified using either method. + +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script with_sgstubs.script 1.o -o 1 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --script wout_sgstubs.script 1.o -o 2 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 1.o -o 3 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --script with_sgstubs.script 1.o -o 4 2>&1 | FileCheck /dev/null --implicit-check-not=error: +// RUN: not ld.lld --cmse-implib -Ttext=0x8000 --script wout_sgstubs.script 1.o -o 5 2>&1 | FileCheck %s --check-prefixes=ERR_NOADDR + +// RUN: llvm-readelf -S 1 | FileCheck %s --check-prefixes=ADDRCMDLINE +// RUN: llvm-readelf -S 2 | FileCheck %s --check-prefixes=ADDRCMDLINE +// RUN: llvm-readelf -S 3 | FileCheck %s --check-prefixes=ADDRCMDLINE +// RUN: llvm-readelf -S 4 | FileCheck %s --check-prefixes=ADDRLNKSCRIPT + +// ERR_NOADDR: error: no address assigned to the veneers output section .gnu.sgstubs + +/// Name Type Address Off Size ES Flg Lk Inf Al +// ADDRCMDLINE: .gnu.sgstubs PROGBITS 00020000 020000 000008 08 AX 0 0 32 +// ADDRLNKSCRIPT: .gnu.sgstubs PROGBITS 00040000 040000 000008 08 AX 0 0 32 + +/// Test diagnostics emitted during symbol attribute checks. + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -I %S/Inputs --triple=armv9a 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_1 +// ERR_SYMATTR-NOT: __acle_se_valid_2 +// ERR_SYMATTR-NOT: __acle_se_valid_3 +// ERR_SYMATTR-NOT: __acle_se_valid_4 +// 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 + + +//--- with_sgstubs.script +SECTIONS { + .text : { *(.text) } + .gnu.sgstubs 0x40000 : { *(.gnu.sgstubs*) } +} + +//--- wout_sgstubs.script +SECTIONS { + .text : { *(.text) } +} + +//--- app + .include "arm-cmse-macros.s" + .text + .thumb + +cmse_veneer entry, function, global, function, global + +//--- lib + .text + .thumb + + /// Symbol not absolute. + .global entry_not_absolute + .type entry_not_absolute, STT_FUNC + .thumb_func +entry_not_absolute: + .size entry_not_absolute, 8 + + /// Symbol not global or weak. + .local entry_not_external + .type entry_not_external, STT_FUNC +entry_not_external=0x1001 + .size entry_not_external, 8 + + /// Symbol not the function type. + .global entry_not_function + .type entry_not_function, STT_NOTYPE +entry_not_function=0x1001 + .size entry_not_function, 8 + + /// Symbol not a Thumb code symbol. + .global entry_not_thumb + .type entry_not_thumb, STT_FUNC +entry_not_thumb=0x1000 + .size entry_not_thumb, 8 + + /// Symbol with incorrect size. + .global entry5_incorrect_size + .type entry5_incorrect_size, STT_FUNC +entry5_incorrect_size=0x1009 + .size entry5_incorrect_size, 6 + + /// Duplicate symbols. + .global entry6_duplicate + .type entry6_duplicate, STT_FUNC +entry6_duplicate=0x1001 + .size entry6_duplicate, 8 + + /// entry7_duplicate gets renamed to entry6_duplicate by llvm-objcopy. + .global entry7_duplicate + .type entry7_duplicate, STT_FUNC +entry7_duplicate=0x1009 + .size entry7_duplicate, 8 + +//--- symattr +.include "arm-cmse-macros.s" + + .text + .thumb + +/// Valid sequences +/// both sym and __acle_se_sym should be global or weak Thumb code symbols. + cmse_veneer valid_1, function, global, function, global + cmse_veneer valid_2, function, weak, function, weak + cmse_veneer valid_3, function, weak, function, global + cmse_veneer valid_4, function, global, function, weak + +/// Invalid sequences +/// __acle_se_sym is an object + cmse_veneer invalid_1, function, global, object, global + cmse_veneer invalid_2, function, global, object, weak +/// sym is an object + cmse_veneer invalid_3, object, global, function, global + cmse_veneer invalid_4, object, global, function, weak +/// sym is local + cmse_veneer invalid_5, function, local, function, global + cmse_veneer invalid_6, function, local, function, weak + +/// __acle_se_invalid_7 not defined. + .global invalid_7 + .type invalid_7, %function + .global __acle_se_invalid_7 + .thumb_func +invalid_7: + +/// invalid_8 not defined. + .global __acle_se_invalid_8 + .thumb_func +__acle_se_invalid_8: + +// Absolute symbols with same values + .global invalid_9 + .global __acle_se_invalid_9 + .type __acle_se_invalid_9, %function + .type invalid_9, %function +__acle_se_invalid_9=0x1001 +invalid_9=0x1001 + .size invalid_9, 0 + .size __acle_se_invalid_9, 0 + +// Absolute symbols with different values + .align 2 + .global __acle_se_invalid_10 + .global invalid_10 + .type __acle_se_invalid_10, %function + .type invalid_10, %function +__acle_se_invalid_10 = 0x10001 +invalid_10 = 0x10005 + .size invalid_10, 0 + .size __acle_se_invalid_10, 0 + + .section nonthumb + .thumb + .align 2 + .global invalid_11 + .global __acle_se_invalid_11 + .type invalid_11, %function + .type __acle_se_invalid_11, %function +invalid_11: + .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 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 @@ +// REQUIRES: arm +/// Test that addresses of secure gateways in an old import library are maintained in new import libraries. + +// 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-prefixes=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-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 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-noveneers.s b/lld/test/ELF/arm-cmse-noveneers.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-noveneers.s @@ -0,0 +1,30 @@ +// REQUIRES: arm +/// Test that addresses of existing secure gateway veneers are output in the CMSE import library. +/// Test that .gnu.sgstubs is size 0 when no linker synthesized secure gateway veneers are created. + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %s -I %S/Inputs -o %t.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t --out-implib=%t.lib +// RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s +// RUN: llvm-readelf -S %t | FileCheck %s --check-prefixes=SGSTUBSSIZE +// RUN: llvm-readelf -s %t.lib | FileCheck %s --check-prefixes=IMPLIBSYMS + +// CHECK: Disassembly of section .text: + +// CHECK-LABEL: : +// CHECK-NEXT: 8000: sg +// CHECK-NEXT: 8004: nop + +// CHECK-LABEL: <__acle_se_existing_veneer>: +// CHECK-NEXT: 8006: nop + + .include "arm-cmse-macros.s" + + cmse_no_veneer existing_veneer, function, global, function, global + +/// Name Type Address Off Size ES Flg Lk Inf Al +// SGSTUBSSIZE: .gnu.sgstubs PROGBITS 00020000 020000 000000 08 AX 0 0 32 + +// IMPLIBSYMS: Symbol table '.symtab' contains 2 entries: +// IMPLIBSYMS-NEXT: Num: Value Size Type Bind Vis Ndx Name +// IMPLIBSYMS-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// IMPLIBSYMS-NEXT: 1: 00008001 8 FUNC GLOBAL DEFAULT ABS existing_veneer diff --git a/lld/test/ELF/arm-cmse-secure.s b/lld/test/ELF/arm-cmse-secure.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-secure.s @@ -0,0 +1,68 @@ +// REQUIRES: arm +/// Create a secure app and import library using CMSE. +/// Create a non-secure app that refers symbols in the import library. + +// RUN: rm -rf %t && split-file %s %t && cd %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-implib.s -o implib.o -I%S/Inputs/ +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-secure-app.s -o secureapp.o +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main cmse-non-secure-app.s -o nonsecureapp.o +/// Create the secure app and import library. +// RUN: ld.lld -e secure_entry --section-start .gnu.sgstubs=0x20000 --cmse-implib implib.o secureapp.o --out-implib=implib.lib -o secureapp +/// Link the non-secure app against the import library. +// RUN: ld.lld -e nonsecure_entry -Ttext=0x8000 --in-implib=implib.lib --cmse-implib nonsecureapp.o -o nonsecureapp +// RUN: llvm-readelf -s implib.lib | FileCheck %s +// RUN: llvm-objdump -d --no-show-raw-insn secureapp | FileCheck %s --check-prefixes=SECUREDISS +// RUN: llvm-objdump -d --no-show-raw-insn nonsecureapp | FileCheck %s --check-prefixes=NONSECUREDISS + +// SECUREDISS-LABEL: : +// SECUREDISS-NEXT: 20000: sg +// SECUREDISS-NEXT: b.w {{.*}} <__acle_se_entry> + +// SECUREDISS-LABEL: <__acle_se_entry>: +// SECUREDISS-NEXT: 20008: nop + +// SECUREDISS-LABEL: : +// SECUREDISS-NEXT: 2000c: bl +// SECUREDISS-NEXT: bx lr + +// NONSECUREDISS-LABEL: : +// NONSECUREDISS-NEXT: 8000: push {r0, lr} +// NONSECUREDISS-NEXT: bl 0x20000 +// NONSECUREDISS-NEXT: pop.w {r0, lr} +// NONSECUREDISS-NEXT: bx lr + +// CHECK: Symbol table '.symtab' contains 2 entries: +// CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK-NEXT: 1: 00020001 8 FUNC GLOBAL DEFAULT ABS entry + +//--- cmse-implib.s + .include "arm-cmse-macros.s" + + .syntax unified + .text + + cmse_veneer entry, function, global, function, global + +//--- cmse-secure-app.s + .align 2 + // Main entry point. + .global secure_entry + .thumb_func +secure_entry: + bl entry + bx lr + .size secure_entry, .-secure_entry + +//--- cmse-non-secure-app.s + .align 2 + .global nonsecure_entry + .thumb + .thumb_func + .type nonsecure_entry, %function +nonsecure_entry: + push {r0,lr} + bl entry + pop {r0,lr} + bx lr + .size nonsecure_entry, .-nonsecure_entry diff --git a/lld/test/ELF/arm-cmse-veneers.s b/lld/test/ELF/arm-cmse-veneers.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-veneers.s @@ -0,0 +1,42 @@ +// REQUIRES: arm +/// Test that symbol visibilities of pairs in the objects +/// are preserved in the executable. + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.main %s -I %S/Inputs -o %t.o +// RUN: ld.lld --cmse-implib -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t +// RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s +// RUN: llvm-readelf -s %t | FileCheck %s --check-prefixes=SYM + +// CHECK: Disassembly of section .gnu.sgstubs: + +// CHECK-LABEL: : +// CHECK-NEXT: 20000: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_weak_qux> +// CHECK-EMPTY: +// CHECK-LABEL: : +// CHECK-NEXT: 20008: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_weak_bar> +// CHECK-EMPTY: +// CHECK-LABEL: : +// CHECK-NEXT: 20010: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_global_foo> +// CHECK-EMPTY: +// CHECK-LABEL: : +// CHECK-NEXT: 20018: sg +// CHECK-NEXT: b.w {{.*}} <__acle_se_global_baz> + +// SYM: 00020011 {{.*}} GLOBAL {{.*}} global_foo +// SYM: 00008001 {{.*}} GLOBAL {{.*}} __acle_se_global_foo +// SYM: 00020009 {{.*}} WEAK {{.*}} weak_bar +// SYM: 00008005 {{.*}} WEAK {{.*}} __acle_se_weak_bar +// SYM: 00020019 {{.*}} 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