diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -6,10 +6,12 @@ // //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" @@ -18,6 +20,7 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; +using namespace llvm::object; namespace { class ARM final : public TargetInfo { @@ -910,6 +913,135 @@ } } +template void elf::writeARMCmseImportLib() { + if (config->emachine != EM_ARM) + return; + + if (in.armCmseSGSection->getSize() == 0) + return; + + if (config->out_implib.empty()) + return; + + SmallVector inputSections; + SmallVector outputSections; + + StringTableSection *shStrTab = make(".shstrtab", false); + StringTableSection *strTab = make(".strtab", false); + SymbolTableBaseSection *symTab = make>(*strTab); + SymtabShndxSection *symTabShndx = make(); + + inputSections.push_back(symTab); + inputSections.push_back(symTabShndx); + inputSections.push_back(shStrTab); + inputSections.push_back(strTab); + + OutputSection *elfHeader1 = make(symTab->name, 0, 0); + OutputSection *elfHeader2 = make(strTab->name, 0, 0); + OutputSection *elfHeader3 = make(shStrTab->name, 0, 0); + OutputSection *elfHeader4 = make(symTabShndx->name, 0, 0); + + auto recordInputSections = [](OutputSection *osec, InputSectionBase *isec) { + osec->recordSection(isec); + osec->finalizeInputSections(); + }; + recordInputSections(elfHeader1, symTab); + recordInputSections(elfHeader2, strTab); + recordInputSections(elfHeader3, shStrTab); + recordInputSections(elfHeader4, symTabShndx); + + in.armCmseSGSection->exportEntries(symTab); + + auto addOutputSection = [&](OutputSection &osec) { + outputSections.push_back(&osec); + osec.sectionIndex = outputSections.size(); + osec.shName = shStrTab->addString(osec.name); + }; + addOutputSection(*elfHeader1); + addOutputSection(*elfHeader2); + addOutputSection(*elfHeader3); + addOutputSection(*elfHeader4); + + elfHeader1->size = symTab->getSize(); + elfHeader2->size = strTab->getSize(); + elfHeader3->size = shStrTab->getSize(); + elfHeader4->size = symTabShndx->getSize(); + + for (SyntheticSection *isec : inputSections) + isec->finalizeContents(); + + uint64_t off = sizeof(typename ELFT::Ehdr); + // Assign file offsets to output sections. + for (OutputSection *osec : outputSections) { + osec->offset = alignToPowerOf2(off, osec->alignment); + off = osec->offset + osec->size; + } + + uint64_t sectionHeaderOff = alignToPowerOf2(off, config->wordsize); + uint64_t fileSize = sectionHeaderOff + + (outputSections.size() + 1) * sizeof(typename ELFT::Shdr); + + unlinkAsync(config->out_implib); + unsigned flags = 0; + if (!config->mmapOutputFile) + flags |= FileOutputBuffer::F_no_mmap; + + Expected> bufferOrErr = + FileOutputBuffer::create(config->out_implib, fileSize, flags); + + if (!bufferOrErr) { + error("failed to open " + config->out_implib + ": " + + llvm::toString(bufferOrErr.takeError())); + return; + } + + std::unique_ptr &buffer = *bufferOrErr; + uint8_t *buf = buffer->getBufferStart(); + + alignToPowerOf2(off, config->wordsize); + auto *eHdr = reinterpret_cast(buf); + eHdr->e_type = ET_REL; + eHdr->e_entry = 0; + eHdr->e_shoff = sectionHeaderOff; + + Partition part; + elf::writeEhdr(buf, part); + // Write the section header table. + // + // The ELF header can only store numbers up to SHN_LORESERVE in the e_shnum + // and e_shstrndx fields. When the value of one of these fields exceeds + // SHN_LORESERVE ELF requires us to put sentinel values in the ELF header and + // use fields in the section header at index 0 to store + // the value. The sentinel values and fields are: + // e_shnum = 0, SHdrs[0].sh_size = number of sections. + // e_shstrndx = SHN_XINDEX, SHdrs[0].sh_link = .shstrtab section index. + auto *sHdrs = reinterpret_cast(buf + eHdr->e_shoff); + // TODO: eHdr->e_shnum = outputSections.size() + 1 gives warning; + // unable to get the associated symbol table for SHT_SYMTAB_SHNDX section with + // index 4: sh_link (5) is greater than or equal to the total number of + // sections (5). Find out why. + eHdr->e_shnum = outputSections.size(); + eHdr->e_shstrndx = elfHeader3->sectionIndex; + + for (OutputSection *osec : outputSections) + osec->writeHeaderTo(++sHdrs); + + // Write section contents to a mmap'ed file. + parallel::TaskGroup tg; + + elfHeader1->writeTo(buf + elfHeader1->offset, tg); + elfHeader2->writeTo(buf + elfHeader2->offset, tg); + elfHeader3->writeTo(buf + elfHeader3->offset, tg); + elfHeader4->writeTo(buf + elfHeader4->offset, tg); + + errorToErrorCode(buffer->commit()); +} + +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); +template void elf::writeARMCmseImportLib(); + TargetInfo *elf::getARMTargetInfo() { static ARM target; return ⌖ diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -166,6 +166,8 @@ llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; llvm::StringRef whyExtract; + llvm::StringRef in_implib; + llvm::StringRef out_implib; StringRef zBtiReport = "none"; StringRef zCetReport = "none"; llvm::StringRef ltoBasicBlockSections; @@ -186,6 +188,7 @@ llvm::MapVector, uint64_t> callGraphProfile; + bool cmse_implib = false; bool allowMultipleDefinition; bool androidPackDynRelocs = false; bool armHasBlx = false; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -339,6 +339,17 @@ if (config->emachine == EM_MIPS && config->gnuHash) error("the .gnu.hash section is not compatible with the MIPS target"); + if (config->emachine != EM_ARM) { + if (config->cmse_implib) + error("--cmse-implib is only supported on ARM targets"); + + if (!config->in_implib.empty()) + error("--in-implib is only supported on ARM targets"); + + if (!config->out_implib.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"); @@ -1113,6 +1124,9 @@ config->fini = args.getLastArgValue(OPT_fini, "_fini"); config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) && !args.hasArg(OPT_relocatable); + config->cmse_implib = args.hasArg(OPT_cmse_implib); + config->in_implib = args.getLastArgValue(OPT_in_implib); + config->out_implib = args.getLastArgValue(OPT_out_implib); config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable); config->fortranCommon = @@ -1658,6 +1672,10 @@ files.back()->justSymbols = true; } break; + case OPT_in_implib: + if (std::optional mb = readFile(arg->getValue())) + files.push_back(createObjFile(*mb)); + break; case OPT_start_group: if (InputFile::isInGroup) error("nested --start-group"); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -285,6 +285,7 @@ private: void initializeSections(bool ignoreComdats, const llvm::object::ELFFile &obj); + void importCmseSymbols(const llvm::object::ELFFile &obj); void initializeSymbols(const llvm::object::ELFFile &obj); void initializeJustSymbols(); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -527,6 +527,12 @@ return; } + if (toString(this) == config->in_implib) { + initializeJustSymbols(); + importCmseSymbols(obj); + return; + } + // Handle dependent libraries and selection of section groups as these are not // done in parallel. ArrayRef objSections = getELFShdrs(); @@ -1031,6 +1037,18 @@ return makeThreadLocal(*this, sec, name); } +// Cortex-M Security Extensions. Prefix for functions that should be exported +// for the non-secure world. +#define ACLESESYM_PREFIX "__acle_se_" +#define ACLESESYM_PREFIX_LEN 10 +bool isACLESESymbolName(llvm::StringRef name) { + return name.startswith(ACLESESYM_PREFIX); +} + +llvm::StringRef getNameFromACLESE(llvm::StringRef name) { + return name.drop_front(ACLESESYM_PREFIX_LEN); +} + // Initialize this->Symbols. this->Symbols is a parallel array as // its corresponding ELF symbol table. template @@ -1095,6 +1113,29 @@ } } +// Initialize this->Symbols. this->Symbols is a parallel array as +// its corresponding ELF symbol table. +template +void ObjFile::importCmseSymbols(const object::ELFFile &obj) { + ArrayRef eSyms = this->getELFSyms(); + if (numSymbols == 0) { + numSymbols = eSyms.size(); + symbols = std::make_unique(numSymbols); + } + + // Some entries have been filled by LazyObjFile. + 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; + symtab.cmseImportLib[sym->getName()] = sym; + } +} + template void ObjFile::initSectionsAndLocalSyms(bool ignoreComdats) { if (!justSymbols) @@ -1139,16 +1180,71 @@ } } +static void processArmCmseSymbol(Symbol &sym, uint32_t secIdx) { + if (!(config->emachine == EM_ARM)) + return; + + if (!isACLESESymbolName(sym.getName())) + return; + + StringRef ext_name = getNameFromACLESE(sym.getName()); + // Try to find the associated symbol. Search only in global symbols + // because the specification requires to produce output library with + // this symbol having global visibility. + Symbol *ext_sym = symtab.find(ext_name); + if (ext_sym == NULL || !ext_sym->isDefined()) { + error("CMSE symbol " + sym.getName() + " detected in " + + toString(sym.file) + " but no associated global symbol definition " + + ext_name + " found."); + return; + } + auto &d_sym = cast(sym); + auto &d_extsym = cast(*ext_sym); + + if (secIdx == SHN_ABS) { + // It is not possible to create a veneer and point the + // associated symbol to it if the associated symbol is already + // absolute. + if (d_sym.value == d_extsym.value) + error("CMSE symbol " + ext_sym->getName() + " in " + + toString(ext_sym->file) + + " is absolute and cannot be changed to point to a new secure " + "gateway veneer."); + // Values are different, nothing to do. + return; + } + + // Validate both symbols. + if (!(d_sym.isFunc() && (d_sym.value & 0x1))) { + error("CMSE symbol " + sym.getName() + " in " + toString(sym.file) + + " is not a Thumb code symbol."); + return; + } + + if (!(d_extsym.isFunc() && (d_extsym.value & 0x1))) { + error("CMSE symbol " + ext_sym->getName() + " in " + + toString(ext_sym->file) + " is not a Thumb code symbol."); + return; + } + // will be redefined later in the link in .gnu.sgstubs + symtab.addCmseSymPair(&sym, ext_sym); +} // Called after all ObjFile::parse is called for all ObjFiles. This checks // duplicate symbols and may do symbol property merge in the future. template void ObjFile::postParse() { static std::mutex mu; ArrayRef eSyms = this->getELFSyms(); + if (toString(this) == config->in_implib) + return; + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; Symbol &sym = *symbols[i]; uint32_t secIdx = eSym.st_shndx; uint8_t binding = eSym.getBinding(); + + processArmCmseSymbol(sym, secIdx); + if (LLVM_UNLIKELY(binding != STB_GLOBAL && binding != STB_WEAK && binding != STB_GNU_UNIQUE)) errorOrWarn(toString(this) + ": symbol (" + Twine(i) + 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,14 @@ def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">; +def cmse_implib: F<"cmse-implib">, HelpText<"Make import library to be a secure gateway import">; + +defm in_implib: Eq<"in-implib", "Import library whose symbols address must remain stable">, + MetaVarName<"">; + +defm out_implib: Eq<"out-implib", "Generate import library">, + 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,11 @@ // is used to uniquify them. llvm::DenseMap comdatGroups; + bool addCmseSymPair(Symbol *acle_sg_sym, Symbol *sym); + llvm::MapVector cmseSymMap; + SmallVector, 0> cmseSymVector; + llvm::StringMap cmseImportLib; + private: SmallVector findByVersion(SymbolVersion ver); SmallVector findAllByVersion(SymbolVersion ver, diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -99,6 +99,15 @@ return sym; } +// Find an existing symbol or create a new one. +bool SymbolTable::addCmseSymPair(Symbol *acle_sg_sym, Symbol *sym) { + auto p = cmseSymMap.insert(std::make_pair(acle_sg_sym, sym)); + if (p.second) { + cmseSymVector.push_back(std::make_pair(acle_sg_sym, sym)); + } + return p.second; +} + // This variant of addSymbol is used by BinaryFile::parse to check duplicate // symbol errors. Symbol *SymbolTable::addAndCheckDuplicate(const Defined &newSym) { diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1219,6 +1219,78 @@ void writeTo(uint8_t *buf) override; size_t getSize() const override; }; +class ArmCmseSGVeneer : public SyntheticSection { +public: + enum Kind { Unknown, FixedAddress, VarAddress }; + + Kind kind() const { return (Kind)veneerKind; } + uint8_t veneerKind; + + ArmCmseSGVeneer(Symbol *sym, Symbol *acle_sg_sym, Kind kind) + : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, + llvm::ELF::SHT_PROGBITS, + /*alignment=*/32, ".gnu.sgstubs"), + veneerKind(kind), sym(sym), acle_sg_sym(acle_sg_sym) {} + void writeTo(uint8_t *buf) override; + Symbol *sym; + Symbol *acle_sg_sym; +}; + +// A Secure Gateway Veneer that is positioned on the address +// specified in the CMSE import library. +// The symbol table in the import library should have a size +// specified +class ArmCmseFixedAddressSGVeneer : public ArmCmseSGVeneer { +public: + ArmCmseFixedAddressSGVeneer(Symbol *sym, Symbol *acle_sg_sym, uint64_t addr) + : ArmCmseSGVeneer(sym, acle_sg_sym, ArmCmseSGVeneer::FixedAddress) { + + entsize = 8; + entaddr = addr; + } + size_t getSize() const override { return this->entsize; }; + size_t getAddr() const { return this->entaddr; }; + static bool classof(const ArmCmseSGVeneer *s) { + return s->kind() != VarAddress; + } + +private: + uint64_t entaddr; +}; + +// A Secure Gateway Veneer that is positioned out of the range +// used by the CMSE import library. +// The size is fixed at 8. +class ArmCmseVariableAddressSGVeneer : public ArmCmseSGVeneer { +public: + ArmCmseVariableAddressSGVeneer(Symbol *sym, Symbol *acle_sg_sym) + : ArmCmseSGVeneer(sym, acle_sg_sym, ArmCmseSGVeneer::VarAddress) { + this->entsize = 8; + } + size_t getSize() const override { return this->entsize; }; + static bool classof(const ArmCmseSGVeneer *s) { + return s->kind() != FixedAddress; + } +}; + +class ArmCmseSGSection : public SyntheticSection { +public: + ArmCmseSGSection(); + bool isNeeded() const override { return !entries.empty(); } + size_t getSize() const override; + void writeTo(uint8_t *buf) override; + void addSGVeneer(Symbol *sym, Symbol *ext_sym); + void addMappingSymbol(); + void finalizeContents() override; + void exportEntries(SymbolTableBaseSection *symTab); + uint64_t impLibMinAddr = -1; + uint64_t impLibMaxAddr = 0; + +private: + SmallVector, 0> entries; + SmallVector sgSections; + uint64_t newEntries = 0; +}; InputSection *createInterpSection(); MergeInputSection *createCommentSection(); @@ -1296,6 +1368,7 @@ std::unique_ptr strTab; std::unique_ptr symTab; std::unique_ptr symTabShndx; + std::unique_ptr armCmseSGSection; void reset(); }; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -2183,6 +2183,129 @@ return SHN_ABS; } +ArmCmseSGSection::ArmCmseSGSection() + : SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, + llvm::ELF::SHT_PROGBITS, + /*alignment=*/32, ".gnu.sgstubs") { + this->entsize = 8; + // The range of addresses used in in_implib should be fixed. + for (auto &p : symtab.cmseImportLib) { + Defined *sym = p.second; + if (impLibMinAddr > sym->value) + impLibMinAddr = sym->value; + if (impLibMaxAddr <= sym->value) { + impLibMaxAddr = sym->value + this->entsize; + } + } + if (config->emachine == EM_ARM && !symtab.cmseSymVector.empty()) { + addMappingSymbol(); + + for (const auto &[acle_sg_sym, sym] : symtab.cmseSymVector) + addSGVeneer(acle_sg_sym, sym); + } +} + +void ArmCmseSGSection::addSGVeneer(Symbol *acle_sg_sym, Symbol *sym) { + entries.push_back(std::make_pair(acle_sg_sym, sym)); + if (acle_sg_sym->file != sym->file || + cast(*acle_sg_sym).value != cast(*sym).value) { + // Symbol addresses different, nothing to do. + return; + } + // Only secure symbols with values equal to that of it's non-secure + // counterpart needs to be in the .sg.stubs section. + auto SI = symtab.cmseImportLib.find(sym->getName()); + if (SI == symtab.cmseImportLib.end()) { + ArmCmseVariableAddressSGVeneer *ss = + make(sym, acle_sg_sym); + ss->parent = this; + this->sgSections.push_back(ss); + ctx.inputSections.push_back(ss); + newEntries += 1; + } else { + Defined *impSym = symtab.cmseImportLib[sym->getName()]; + ArmCmseFixedAddressSGVeneer *ss = + make(sym, acle_sg_sym, impSym->value); + ss->parent = this; + this->sgSections.push_back(ss); + ctx.inputSections.push_back(ss); + } +} + +void ArmCmseSGSection::exportEntries(SymbolTableBaseSection *symTab) { + for (const std::pair &p : entries) { + Defined *d = cast(p.second); + symTab->addSymbol(makeDefined(nullptr, d->getName(), STB_GLOBAL, + /*stOther=*/0, STT_FUNC, d->getVA(), + d->getSize(), nullptr)); + } +} + +void ArmCmseSGSection::writeTo(uint8_t *buf) { + for (auto *s : this->sgSections) { + auto off = s->outSecOff - getVA(); + s->writeTo(buf + off); + } +} + +void ArmCmseSGSection::addMappingSymbol() { + addSyntheticLocal("$t", STT_NOTYPE, /* off */ 0, /* size */ 0, *this); +} + +size_t ArmCmseSGSection::getSize() const { + if (sgSections.empty()) + return impLibMaxAddr - getVA() + (newEntries * entsize); + + return entries.size() * entsize; +} + +void ArmCmseSGSection::finalizeContents() { + auto it = std::partition( + this->sgSections.begin(), this->sgSections.end(), [](ArmCmseSGVeneer *i) { + return symtab.cmseImportLib.find(i->sym->getName()) != + symtab.cmseImportLib.end(); + }); + std::sort(this->sgSections.begin(), it, + [](ArmCmseSGVeneer *a, ArmCmseSGVeneer *b) { + return cast(a)->getAddr() < + cast(b)->getAddr(); + }); + for (ArmCmseSGVeneer *ss : this->sgSections) { + if (dyn_cast(ss)) + break; + ArmCmseFixedAddressSGVeneer *s = dyn_cast(ss); + ss->outSecOff = (s->getAddr() & ~1); + Defined(ss->file, StringRef(), ss->sym->binding, ss->sym->stOther, + ss->sym->type, (ss->outSecOff - getVA()) | 1, this->entsize, this) + .overwrite(*ss->sym); + } + size_t off = (impLibMaxAddr < getVA() ? getVA() : impLibMaxAddr); + off += (newEntries * entsize); + for (ArmCmseSGVeneer *ss : llvm::reverse(this->sgSections)) { + if (dyn_cast(ss)) + break; + ArmCmseVariableAddressSGVeneer *s = + dyn_cast(ss); + off -= s->entsize; + ss->outSecOff = (off & ~1); + Defined(ss->file, StringRef(), ss->sym->binding, ss->sym->stOther, + ss->sym->type, (ss->outSecOff - getVA()) | 1, this->entsize, this) + .overwrite(*ss->sym); + } +} + +void ArmCmseSGVeneer::writeTo(uint8_t *buf) { + const uint8_t data[] = { + 0x7f, 0xe9, 0x7f, 0xe9, // SG + 0x00, 0xf0, 0x00, 0xb0, // B.W S + + }; + memcpy(buf, data, sizeof(data)); + uint64_t s = acle_sg_sym->getVA(); + uint64_t p = getVA() + 4; + target->relocateNoSym(buf + 4, R_ARM_THM_JUMP24, s - p - 4); +} + // Write the internal symbol table contents to the output symbol table. template void SymbolTableSection::writeTo(uint8_t *buf) { // The first entry is a null entry as per the ELF spec. @@ -3822,6 +3945,7 @@ strTab.reset(); symTab.reset(); symTabShndx.reset(); + armCmseSGSection.reset(); } constexpr char kMemtagAndroidNoteName[] = "Android"; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -215,6 +215,7 @@ void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); +template void writeARMCmseImportLib(); void riscvFinalizeRelax(int passes); LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -453,6 +453,9 @@ in.igotPlt = std::make_unique(); add(*in.igotPlt); + in.armCmseSGSection = std::make_unique(); + add(*in.armCmseSGSection); + // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat // it as a relocation and ensure the referenced section is created. if (ElfSym::globalOffsetTable && config->emachine != EM_MIPS) { @@ -590,6 +593,9 @@ if (auto e = buffer->commit()) fatal("failed to write output '" + buffer->getPath() + "': " + toString(std::move(e))); + + if (config->emachine == EM_ARM) + writeARMCmseImportLib(); } } @@ -2133,6 +2139,7 @@ // static symbol table. finalizeSynthetic(in.symTab.get()); finalizeSynthetic(in.ppc64LongBranchTarget.get()); + finalizeSynthetic(in.armCmseSGSection.get()); } // Relaxation to delete inter-basic block jumps created by basic block diff --git a/lld/test/ELF/Inputs/arm-cmse-implib-1.s b/lld/test/ELF/Inputs/arm-cmse-implib-1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/arm-cmse-implib-1.s @@ -0,0 +1,70 @@ + .syntax unified + .text + + .align 2 + .global foo + .global __acle_se_foo + .thumb + .thumb_func + .type foo, %function + .type __acle_se_foo, %function +// foo == __acle_se_foo. So veneer generation expected. +foo: +__acle_se_foo: + nop + .size foo, .-foo + .size __acle_se_foo, .-__acle_se_foo + + .align 2 + .global bar + .global __acle_se_bar + .thumb + .thumb_func + .type bar, %function + .type __acle_se_bar, %function +// Same as foo. +bar: +__acle_se_bar: + nop + .size bar, .-bar + .size __acle_se_bar, .-__acle_se_bar + + .align 2 + .global no_veneer1 + .global __acle_se_no_veneer1 + .thumb + .thumb_func + .type no_veneer1, %function + .type __acle_se_no_veneer1, %function +// no_veneer1 != __acle_se_no_veneer1. +// So no veneer generation is needed. +// However no_veneer1 will be imported to the import library. +no_veneer1: + sg +__acle_se_no_veneer1: + nop + .size no_veneer1, .-no_veneer1 + .size __acle_se_no_veneer1, .-__acle_se_no_veneer1 + + .align 2 + .global no_veneer2 + .global __acle_se_no_veneer2 + .thumb + .thumb_func + .type no_veneer2, %function + .type __acle_se_no_veneer2, %function +// Same as no_veneer1. +no_veneer2: + sg +__acle_se_no_veneer2: + nop + .size no_veneer2, .-no_veneer2 + .size __acle_se_no_veneer2, .-__acle_se_no_veneer2 + + .align 2 + .global normal_sym + .type normal_sym, %function +// Not an entry function because there is no corresponding __acle_se_normal_sym. +normal_sym: + nop + .size normal_sym, .-normal_sym diff --git a/lld/test/ELF/Inputs/arm-cmse-implib-2.s b/lld/test/ELF/Inputs/arm-cmse-implib-2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/arm-cmse-implib-2.s @@ -0,0 +1,85 @@ + .syntax unified + .text + + .align 2 + .global baz + .global __acle_se_baz + .thumb + .thumb_func + .type baz, %function + .type __acle_se_baz, %function +// baz == __acle_se_baz. So veneer generation expected. +baz: +__acle_se_baz: + nop + .size baz, .-baz + .size __acle_se_baz, .-__acle_se_baz + + .align 2 + .global foo + .global __acle_se_foo + .thumb + .thumb_func + .type foo, %function + .type __acle_se_foo, %function +// Same as baz. +foo: +__acle_se_foo: + nop + .size foo, .-foo + .size __acle_se_foo, .-__acle_se_foo + + .align 2 + .global qux + .global __acle_se_qux + .thumb + .thumb_func + .type qux, %function + .type __acle_se_qux, %function +// Same as baz. +qux: +__acle_se_qux: + nop + .size qux, .-qux + .size __acle_se_qux, .-__acle_se_qux + + @ Valid setup for entry function without veneer generation + .align 2 + .global no_veneer1 + .global __acle_se_no_veneer1 + .thumb + .thumb_func + .type no_veneer1, %function + .type __acle_se_no_veneer1, %function +// no_veneer1 != __acle_se_no_veneer1. +// So no veneer generation is needed. +// However no_veneer1 will be imported to the import library. +no_veneer1: + sg +__acle_se_no_veneer1: + nop + .size no_veneer1, .-no_veneer1 + .size __acle_se_no_veneer1, .-__acle_se_no_veneer1 + + .align 2 + .global no_veneer2 + .global __acle_se_no_veneer2 + .thumb + .thumb_func + .type no_veneer2, %function + .type __acle_se_no_veneer2, %function +// Same as no_veneer1. +no_veneer2: + sg +__acle_se_no_veneer2: + nop + .size no_veneer2, .-no_veneer2 + .size __acle_se_no_veneer2, .-__acle_se_no_veneer2 + + .align 2 + .global normal_sym + .type normal_sym, %function +// Not an entry function because there is no corresponding __acle_se_normal_sym. +normal_sym: + nop + .size normal_sym, .-normal_sym diff --git a/lld/test/ELF/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-error.s b/lld/test/ELF/arm-cmse-error.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/arm-cmse-error.s @@ -0,0 +1,20 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv7m-none-eabi %s -o %t1.o +// RUN: not ld.lld --cmse-implib %t1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix CHECK-MISSING + + .text + .thumb + + # Missing standard symbol. + .global __acle_se_missing_standard_sym + .thumb_func +__acle_se_missing_standard_sym: + + # External symbol absolute. + .global __acle_se_absolute_sym +__acle_se_absolute_sym=0x1001 + .global absolute_sym +absolute_sym=0x1001 + +// CHECK-MISSING: CMSE symbol __acle_se_missing_standard_sym detected in {{.*\.o}} but no associated global symbol definition missing_standard_sym found. +// CHECK-MISSING: CMSE symbol absolute_sym in {{.*\.o}} is absolute and cannot be changed to point to a new secure gateway veneer. 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,204 @@ +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %S/Inputs/arm-cmse-implib-1.s -o %t1.o +/// TODO: Test seg faults when using multiple threads. Workaround by running on a single thread. +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t1 --out-implib=%t1.lib --cmse-implib %t1.o --threads=1 +// RUN: llvm-readelf -s %t1 %t1.lib | FileCheck %s --check-prefix=CHECK1 +// RUN: llvm-objdump -d %t1 | FileCheck %s --check-prefix=DISS1 + +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv8m.base %S/Inputs/arm-cmse-implib-2.s -o %t2.o +/// TODO: Test seg faults when using multiple threads. Workaround by running on a single thread. +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 -o %t2 --out-implib=%t2.lib --in-implib=%t1.lib --cmse-implib %t2.o --threads=1 +// RUN: llvm-readelf -s %t2 %t2.lib | FileCheck %s --check-prefix=CHECK2 +// RUN: llvm-objdump -d %t2 | FileCheck %s --check-prefix=DISS2 + + .syntax unified + .text + + .align 2 + .global foo + .global __acle_se_foo + .thumb + .thumb_func + .type foo, %function + .type __acle_se_foo, %function +// foo == __acle_se_foo. So veneer generation expected. +foo: +__acle_se_foo: + nop + .size foo, .-foo + .size __acle_se_foo, .-__acle_se_foo + + .align 2 + .global bar + .global __acle_se_bar + .thumb + .thumb_func + .type bar, %function + .type __acle_se_bar, %function +// Same as foo. +bar: +__acle_se_bar: + nop + .size bar, .-bar + .size __acle_se_bar, .-__acle_se_bar + + .align 2 + .global no_veneer1 + .global __acle_se_no_veneer1 + .thumb + .thumb_func + .type no_veneer1, %function + .type __acle_se_no_veneer1, %function +// no_veneer1 != __acle_se_no_veneer1. +// So no veneer generation is needed. +// However no_veneer1 will be imported to the import library. +no_veneer1: + sg +__acle_se_no_veneer1: + nop + .size no_veneer1, .-no_veneer1 + .size __acle_se_no_veneer1, .-__acle_se_no_veneer1 + + .align 2 + .global no_veneer2 + .global __acle_se_no_veneer2 + .thumb + .thumb_func + .type no_veneer2, %function + .type __acle_se_no_veneer2, %function +// Same as no_veneer1. +no_veneer2: + sg +__acle_se_no_veneer2: + nop + .size no_veneer2, .-no_veneer2 + .size __acle_se_no_veneer2, .-__acle_se_no_veneer2 + + .align 2 + .global normal_sym + .type normal_sym, %function +// Not an entry function because there is no corresponding __acle_se_normal_sym. +normal_sym: + nop + .size normal_sym, .-normal_sym + +/// Executable 1 +// CHECK1: File: +// CHECK1: Symbol table '.symtab' contains 12 entries: +// CHECK1-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK1-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK1-NEXT: 1: 00020000 0 NOTYPE LOCAL DEFAULT 2 $t +// CHECK1-NEXT: 2: 00008000 0 NOTYPE LOCAL DEFAULT 1 $t.0 +// CHECK1-NEXT: 3: 00020001 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK1-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo +// CHECK1-NEXT: 5: 00020009 8 FUNC GLOBAL DEFAULT 2 bar +// CHECK1-NEXT: 6: 00008005 2 FUNC GLOBAL DEFAULT 1 __acle_se_bar +// CHECK1-NEXT: 7: 00008009 6 FUNC GLOBAL DEFAULT 1 no_veneer1 +// CHECK1-NEXT: 8: 0000800d 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// CHECK1-NEXT: 9: 00008011 6 FUNC GLOBAL DEFAULT 1 no_veneer2 +// CHECK1-NEXT: 10: 00008015 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer2 +// CHECK1-NEXT: 11: 00008019 2 FUNC GLOBAL DEFAULT 1 normal_sym + +/// Import library 1 +// CHECK1: File: +// CHECK1: Symbol table '.symtab' contains 5 entries: +// CHECK1-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK1-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK1-NEXT: 1: 00020001 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK1-NEXT: 2: 00020009 8 FUNC GLOBAL DEFAULT ABS bar +// CHECK1-NEXT: 3: 00008009 6 FUNC GLOBAL DEFAULT ABS no_veneer1 +// CHECK1-NEXT: 4: 00008011 6 FUNC GLOBAL DEFAULT ABS no_veneer2 + +// DISS1: Disassembly of section .text: +// DISS1: 00008000 <__acle_se_foo>: +// DISS1-NEXT: 8000: bf00 nop +// DISS1-NEXT: 8002: 46c0 mov r8, r8 +// DISS1: 00008004 <__acle_se_bar>: +// DISS1-NEXT: 8004: bf00 nop +// DISS1-NEXT: 8006: 46c0 mov r8, r8 +// DISS1: 00008008 : +// DISS1-NEXT: 8008: e97f e97f sg +// DISS1: 0000800c <__acle_se_no_veneer1>: +// DISS1-NEXT: 800c: bf00 nop +// DISS1-NEXT: 800e: 46c0 mov r8, r8 +// DISS1: 00008010 : +// DISS1-NEXT: 8010: e97f e97f sg +// DISS1: 00008014 <__acle_se_no_veneer2>: +// DISS1-NEXT: 8014: bf00 nop +// DISS1-NEXT: 8016: 46c0 mov r8, r8 +// DISS1: 00008018 : +// DISS1-NEXT: 8018: bf00 nop + +// DISS1: Disassembly of section .gnu.sgstubs: +// DISS1: 00020000 : +// DISS1-NEXT: 20000: e97f e97f sg +// DISS1-NEXT: 20004: f7e7 bffc b.w 0x8000 <__acle_se_foo> +// DISS1: 00020008 : +// DISS1-NEXT: 20008: e97f e97f sg +// DISS1-NEXT: 2000c: f7e7 bffa b.w 0x8004 <__acle_se_bar> + +/// Executable 2 +// CHECK2: File: +// CHECK2: Symbol table '.symtab' contains 14 entries: +// CHECK2-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK2-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK2-NEXT: 1: 00020000 0 NOTYPE LOCAL DEFAULT 2 $t +// CHECK2-NEXT: 2: 00008000 0 NOTYPE LOCAL DEFAULT 1 $t.0 +// CHECK2-NEXT: 3: 00020011 8 FUNC GLOBAL DEFAULT 2 baz +// CHECK2-NEXT: 4: 00008001 2 FUNC GLOBAL DEFAULT 1 __acle_se_baz +// CHECK2-NEXT: 5: 00020001 8 FUNC GLOBAL DEFAULT 2 foo +// CHECK2-NEXT: 6: 00008005 2 FUNC GLOBAL DEFAULT 1 __acle_se_foo +// CHECK2-NEXT: 7: 00020019 8 FUNC GLOBAL DEFAULT 2 qux +// CHECK2-NEXT: 8: 00008009 2 FUNC GLOBAL DEFAULT 1 __acle_se_qux +// CHECK2-NEXT: 9: 0000800d 6 FUNC GLOBAL DEFAULT 1 no_veneer1 +// CHECK2-NEXT: 10: 00008011 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer1 +// CHECK2-NEXT: 11: 00008015 6 FUNC GLOBAL DEFAULT 1 no_veneer2 +// CHECK2-NEXT: 12: 00008019 2 FUNC GLOBAL DEFAULT 1 __acle_se_no_veneer2 +// CHECK2-NEXT: 13: 0000801d 2 FUNC GLOBAL DEFAULT 1 normal_sym + +/// Note that foo retains its address from Import library 1 (0x00020001) +/// New entry functions, baz and qux, use addresses not used by Import librar 1. +/// Import library 2 +// CHECK2: File: +// CHECK2: Symbol table '.symtab' contains 6 entries: +// CHECK2-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK2-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK2-NEXT: 1: 00020011 8 FUNC GLOBAL DEFAULT ABS baz +// CHECK2-NEXT: 2: 00020001 8 FUNC GLOBAL DEFAULT ABS foo +// CHECK2-NEXT: 3: 00020019 8 FUNC GLOBAL DEFAULT ABS qux +// CHECK2-NEXT: 4: 0000800d 6 FUNC GLOBAL DEFAULT ABS no_veneer1 +// CHECK2-NEXT: 5: 00008015 6 FUNC GLOBAL DEFAULT ABS no_veneer2 + +// DISS2: Disassembly of section .text: +// DISS2: 00008000 <__acle_se_baz>: +// DISS2-NEXT: 8000: bf00 nop +// DISS2-NEXT: 8002: 46c0 mov r8, r8 +// DISS2: 00008004 <__acle_se_foo>: +// DISS2-NEXT: 8004: bf00 nop +// DISS2-NEXT: 8006: 46c0 mov r8, r8 +// DISS2: 00008008 <__acle_se_qux>: +// DISS2-NEXT: 8008: bf00 nop +// DISS2-NEXT: 800a: 46c0 mov r8, r8 +// DISS2: 0000800c : +// DISS2-NEXT: 800c: e97f e97f sg +// DISS2: 00008010 <__acle_se_no_veneer1>: +// DISS2-NEXT: 8010: bf00 nop +// DISS2-NEXT: 8012: 46c0 mov r8, r8 +// DISS2: 00008014 : +// DISS2-NEXT: 8014: e97f e97f sg +// DISS2: 00008018 <__acle_se_no_veneer2>: +// DISS2-NEXT: 8018: bf00 nop +// DISS2-NEXT: 801a: 46c0 mov r8, r8 +// DISS2: 0000801c : +// DISS2-NEXT: 801c: bf00 nop + +// DISS2: Disassembly of section .gnu.sgstubs: +// DISS2: 00020000 : +// DISS2-NEXT: 20000: e97f e97f sg +// DISS2-NEXT: 20004: f7e7 bffe b.w 0x8004 <__acle_se_foo> +// DISS2: 00020010 : +// DISS2-NEXT: 20010: e97f e97f sg +// DISS2-NEXT: 20014: f7e7 bff4 b.w 0x8000 <__acle_se_baz> +// DISS2: 00020018 : +// DISS2-NEXT: 20018: e97f e97f sg +// DISS2-NEXT: 2001c: f7e7 bff4 b.w 0x8008 <__acle_se_qux> diff --git a/lld/test/ELF/arm-cmse-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,99 @@ + +// REQUIRES: arm +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj --triple=thumbv7m-none-eabi %s -o %t.o +// RUN: ld.lld -Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 %t.o -o %t +// RUN: llvm-objdump -d %t | FileCheck %s + + .align 2 + .global __acle_se_global_foo + .global global_foo + .thumb + .thumb_func + .type __acle_se_global_foo, %function + .thumb + .thumb_func + .type global_foo, %function +__acle_se_global_foo: +global_foo: + nop + .size global_foo, .-global_foo + .size __acle_se_global_foo, .-__acle_se_global_foo + + .align 2 + .weak __acle_se_weak_bar + .weak weak_bar + .thumb + .thumb_func + .type __acle_se_weak_bar, %function + .thumb + .thumb_func + .type weak_bar, %function +__acle_se_weak_bar: +weak_bar: + nop + .size weak_bar, .-weak_bar + .size __acle_se_weak_bar, .-__acle_se_weak_bar + + .align 2 + .weak __acle_se_global_baz + .global global_baz + .thumb + .thumb_func + .type __acle_se_global_baz, %function + .thumb + .thumb_func + .type global_baz, %function +__acle_se_global_baz: +global_baz: + nop + .size global_baz, .-global_baz + .size __acle_se_global_baz, .-__acle_se_global_baz + + .align 2 + .global __acle_se_weak_qux + .weak weak_qux + .thumb + .thumb_func + .type __acle_se_weak_qux, %function + .thumb + .thumb_func + .type weak_qux, %function +__acle_se_weak_qux: +weak_qux: + nop + .size weak_qux, .-weak_qux + .size __acle_se_weak_qux, .-__acle_se_weak_qux + + .align 2 + .global __acle_se_absolute_foobar + .global absolute_foobar + .type __acle_se_absolute_foobar, %function + .type absolute_foobar, %function +__acle_se_absolute_foobar = 0x10000 +absolute_foobar = 0x10004 + .size absolute_foobar, 0 + .size __acle_se_absolute_foobar, 0 + +// CHECK: Disassembly of section .text: +// CHECK: 00008000 <__acle_se_global_foo> +// CHECK: 00008004 <__acle_se_weak_bar> +// CHECK: 00008008 <__acle_se_global_baz> +// CHECK: 0000800c <__acle_se_weak_qux> + +// CHECK: Disassembly of section .gnu.sgstubs: + +// CHECK: 00020000 +// CHECK: 20000: e97f e97f +// CHECK: 20004: f7e7 bffc b.w 0x8000 <__acle_se_global_foo> + +// CHECK: 00020008 +// CHECK: 20008: e97f e97f +// CHECK: 2000c: f7e7 bffa b.w 0x8004 <__acle_se_weak_bar> + +// CHECK: 00020010 +// CHECK: 20010: e97f e97f +// CHECK: 20014: f7e7 bff8 b.w 0x8008 <__acle_se_global_baz> + +// CHECK: 00020018 +// CHECK: 20018: e97f e97f +// CHECK: 2001c: f7e7 bff6 b.w 0x800c <__acle_se_weak_qux>