Skip to content

Commit c4fea39

Browse files
committed
[LLD][ELF] Cortex-M Security Extensions (CMSE) Support
This commit provides linker support for Cortex-M Security Extensions (CMSE). The specification for this feature can be found in ARM v8-M Security Extensions: Requirements on Development Tools. The linker synthesizes a security gateway veneer in a special section; `.gnu.sgstubs`, when it finds non-local symbols `__acle_se_<entry>` and `<entry>`, defined relative to the same text section and having the same address. The address of `<entry>` is retargeted to the starting address of the linker-synthesized security gateway veneer in section `.gnu.sgstubs`. In summary, the linker translates input: ``` .text entry: __acle_se_entry: [entry_code] ``` into: ``` .section .gnu.sgstubs entry: SG B.W __acle_se_entry .text __acle_se_entry: [entry_code] ``` If addresses of `__acle_se_<entry>` and `<entry>` are not equal, the linker considers that `<entry>` already defines a secure gateway veneer so does not synthesize one. If `--out-implib=<out.lib>` is specified, the linker writes the list of secure gateway veneers into a CMSE import library `<out.lib>`. The CMSE import library will have 3 sections: `.symtab`, `.strtab`, `.shstrtab`. For every secure gateway veneer <entry> at address `<addr>`, `.symtab` contains a `SHN_ABS` symbol `<entry>` with value `<addr>`. If `--in-implib=<in.lib>` is specified, the linker reads the existing CMSE import library `<in.lib>` and preserves the entry function addresses in the resulting executable and new import library. Reviewed By: MaskRay, peter.smith Differential Revision: https://reviews.llvm.org/D139092
1 parent afc5cca commit c4fea39

22 files changed

+1215
-2
lines changed

lld/ELF/Arch/ARM.cpp

Lines changed: 410 additions & 0 deletions
Large diffs are not rendered by default.

lld/ELF/Config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class LinkerDriver {
129129

130130
std::unique_ptr<BitcodeCompiler> lto;
131131
std::vector<InputFile *> files;
132+
std::optional<InputFile *> armCmseImpLib;
132133

133134
public:
134135
SmallVector<std::pair<StringRef, unsigned>, 0> archiveFiles;
@@ -173,6 +174,8 @@ struct Config {
173174
llvm::StringRef thinLTOCacheDir;
174175
llvm::StringRef thinLTOIndexOnlyArg;
175176
llvm::StringRef whyExtract;
177+
llvm::StringRef cmseInputLib;
178+
llvm::StringRef cmseOutputLib;
176179
StringRef zBtiReport = "none";
177180
StringRef zCetReport = "none";
178181
llvm::StringRef ltoBasicBlockSections;
@@ -195,11 +198,13 @@ struct Config {
195198
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
196199
uint64_t>
197200
callGraphProfile;
201+
bool cmseImplib = false;
198202
bool allowMultipleDefinition;
199203
bool androidPackDynRelocs = false;
200204
bool armHasBlx = false;
201205
bool armHasMovtMovw = false;
202206
bool armJ1J2BranchEncoding = false;
207+
bool armCMSESupport = false;
203208
bool asNeeded = false;
204209
BsymbolicKind bsymbolic = BsymbolicKind::None;
205210
bool callGraphProfileSort;

lld/ELF/Driver.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,22 @@ static void checkOptions() {
345345
if (config->emachine == EM_MIPS && config->gnuHash)
346346
error("the .gnu.hash section is not compatible with the MIPS target");
347347

348+
if (config->emachine == EM_ARM) {
349+
if (!config->cmseImplib) {
350+
if (!config->cmseInputLib.empty())
351+
error("--in-implib may not be used without --cmse-implib");
352+
if (!config->cmseOutputLib.empty())
353+
error("--out-implib may not be used without --cmse-implib");
354+
}
355+
} else {
356+
if (config->cmseImplib)
357+
error("--cmse-implib is only supported on ARM targets");
358+
if (!config->cmseInputLib.empty())
359+
error("--in-implib is only supported on ARM targets");
360+
if (!config->cmseOutputLib.empty())
361+
error("--out-implib is only supported on ARM targets");
362+
}
363+
348364
if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
349365
error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
350366

@@ -1161,6 +1177,9 @@ static void readConfigs(opt::InputArgList &args) {
11611177
config->fini = args.getLastArgValue(OPT_fini, "_fini");
11621178
config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) &&
11631179
!args.hasArg(OPT_relocatable);
1180+
config->cmseImplib = args.hasArg(OPT_cmse_implib);
1181+
config->cmseInputLib = args.getLastArgValue(OPT_in_implib);
1182+
config->cmseOutputLib = args.getLastArgValue(OPT_out_implib);
11641183
config->fixCortexA8 =
11651184
args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable);
11661185
config->fortranCommon =
@@ -1738,6 +1757,12 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
17381757
files.back()->justSymbols = true;
17391758
}
17401759
break;
1760+
case OPT_in_implib:
1761+
if (armCmseImpLib)
1762+
error("multiple CMSE import libraries not supported");
1763+
else if (std::optional<MemoryBufferRef> mb = readFile(arg->getValue()))
1764+
armCmseImpLib = createObjFile(*mb);
1765+
break;
17411766
case OPT_start_group:
17421767
if (InputFile::isInGroup)
17431768
error("nested --start-group");
@@ -2617,6 +2642,8 @@ void LinkerDriver::link(opt::InputArgList &args) {
26172642
llvm::TimeTraceScope timeScope("Parse input files", files[i]->getName());
26182643
parseFile(files[i]);
26192644
}
2645+
if (armCmseImpLib)
2646+
parseArmCMSEImportLib(*armCmseImpLib);
26202647
}
26212648

26222649
// Now that we have every file, we can decide if we will need a
@@ -2781,6 +2808,9 @@ void LinkerDriver::link(opt::InputArgList &args) {
27812808
if (args.hasArg(OPT_exclude_libs))
27822809
excludeLibs(args);
27832810

2811+
// Record [__acle_se_<sym>, <sym>] pairs for later processing.
2812+
processArmCmseSymbols();
2813+
27842814
// Apply symbol renames for --wrap and combine foo@v1 and foo@@v1.
27852815
redirectSymbols(wrapped);
27862816

lld/ELF/InputFiles.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
177177
config->armHasMovtMovw = true;
178178
break;
179179
}
180+
181+
// Only ARMv8-M or later architectures have CMSE support.
182+
std::optional<unsigned> profile =
183+
attributes.getAttributeValue(ARMBuildAttrs::CPU_arch_profile);
184+
if (!profile)
185+
return;
186+
if (arch >= ARMBuildAttrs::CPUArch::v8_M_Base &&
187+
profile == ARMBuildAttrs::MicroControllerProfile)
188+
config->armCMSESupport = true;
180189
}
181190

182191
InputFile::InputFile(Kind k, MemoryBufferRef m)
@@ -317,6 +326,14 @@ template <class ELFT> static void doParseFile(InputFile *file) {
317326
// Add symbols in File to the symbol table.
318327
void elf::parseFile(InputFile *file) { invokeELFT(doParseFile, file); }
319328

329+
template <class ELFT> static void doParseArmCMSEImportLib(InputFile *file) {
330+
cast<ObjFile<ELFT>>(file)->importCmseSymbols();
331+
}
332+
333+
void elf::parseArmCMSEImportLib(InputFile *file) {
334+
invokeELFT(doParseArmCMSEImportLib, file);
335+
}
336+
320337
// Concatenates arguments to construct a string representing an error location.
321338
static std::string createFileLineMsg(StringRef path, unsigned line) {
322339
std::string filename = std::string(path::filename(path));
@@ -1031,8 +1048,8 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
10311048
return makeThreadLocal<InputSection>(*this, sec, name);
10321049
}
10331050

1034-
// Initialize this->Symbols. this->Symbols is a parallel array as
1035-
// its corresponding ELF symbol table.
1051+
// Initialize symbols. symbols is a parallel array to the corresponding ELF
1052+
// symbol table.
10361053
template <class ELFT>
10371054
void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
10381055
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();

lld/ELF/InputFiles.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ std::optional<MemoryBufferRef> readFile(StringRef path);
4848
// Add symbols in File to the symbol table.
4949
void parseFile(InputFile *file);
5050

51+
void parseArmCMSEImportLib(InputFile *file);
52+
5153
// The root class of input files.
5254
class InputFile {
5355
protected:
@@ -88,6 +90,12 @@ class InputFile {
8890
return {symbols.get(), numSymbols};
8991
}
9092

93+
MutableArrayRef<Symbol *> getMutableSymbols() {
94+
assert(fileKind == BinaryKind || fileKind == ObjKind ||
95+
fileKind == BitcodeKind);
96+
return {symbols.get(), numSymbols};
97+
}
98+
9199
// Get filename to use for linker script processing.
92100
StringRef getNameForScript() const;
93101

@@ -280,6 +288,8 @@ template <class ELFT> class ObjFile : public ELFFileBase {
280288

281289
void initSectionsAndLocalSyms(bool ignoreComdats);
282290
void postParse();
291+
void importCmseSymbols();
292+
void redirectCmseSymbols();
283293

284294
private:
285295
void initializeSections(bool ignoreComdats,

lld/ELF/LinkerScript.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,15 @@ void LinkerScript::diagnoseOrphanHandling() const {
896896
}
897897
}
898898

899+
void LinkerScript::diagnoseMissingSGSectionAddress() const {
900+
if (!config->cmseImplib || !in.armCmseSGSection->isNeeded())
901+
return;
902+
903+
OutputSection *sec = findByName(sectionCommands, ".gnu.sgstubs");
904+
if (sec && !sec->addrExpr && !config->sectionStartMap.count(".gnu.sgstubs"))
905+
error("no address assigned to the veneers output section " + sec->name);
906+
}
907+
899908
// This function searches for a memory region to place the given output
900909
// section in. If found, a pointer to the appropriate memory region is
901910
// returned in the first member of the pair. Otherwise, a nullptr is returned.

lld/ELF/LinkerScript.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ class LinkerScript final {
318318

319319
void addOrphanSections();
320320
void diagnoseOrphanHandling() const;
321+
void diagnoseMissingSGSectionAddress() const;
321322
void adjustOutputSections();
322323
void adjustSectionsAfterSorting();
323324

lld/ELF/MarkLive.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ template <class ELFT> void MarkLive<ELFT>::run() {
230230
markSymbol(symtab.find(s));
231231
for (StringRef s : script->referencedSymbols)
232232
markSymbol(symtab.find(s));
233+
for (auto [symName, _] : symtab.cmseSymMap) {
234+
markSymbol(symtab.cmseSymMap[symName].sym);
235+
markSymbol(symtab.cmseSymMap[symName].acleSeSym);
236+
}
233237

234238
// Mark .eh_frame sections as live because there are usually no relocations
235239
// that point to .eh_frames. Otherwise, the garbage collector would drop

lld/ELF/Options.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ defm split_stack_adjust_size
8080

8181
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
8282

83+
def cmse_implib: FF<"cmse-implib">,
84+
HelpText<"Make the output library to be a CMSE secure code import library">;
85+
86+
defm in_implib: EEq<"in-implib",
87+
"Read an existing CMSE secure code import library and preserve entry function addresses in the "
88+
"resulting new CMSE secure code import library (optional when creating a CMSE secure image)">,
89+
MetaVarName<"<file>">;
90+
91+
defm out_implib: EEq<"out-implib",
92+
"Output the CMSE secure code import library to <file> (required when creating a CMSE secure image)">,
93+
MetaVarName<"<file>">;
94+
8395
defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">;
8496

8597
defm Tdata: Eq<"Tdata", "Same as --section-start with .data as the sectionname">;

lld/ELF/SymbolTable.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ namespace lld::elf {
1919
class InputFile;
2020
class SharedFile;
2121

22+
struct ArmCmseEntryFunction {
23+
Symbol *acleSeSym;
24+
Symbol *sym;
25+
};
26+
2227
// SymbolTable is a bucket of all known symbols, including defined,
2328
// undefined, or lazy symbols (the last one is symbols in archive
2429
// files whose archive members are not yet loaded).
@@ -60,6 +65,18 @@ class SymbolTable {
6065
// is used to uniquify them.
6166
llvm::DenseMap<llvm::CachedHashStringRef, const InputFile *> comdatGroups;
6267

68+
// The Map of __acle_se_<sym>, <sym> pairs found in the input objects.
69+
// Key is the <sym> name.
70+
llvm::SmallMapVector<StringRef, ArmCmseEntryFunction, 1> cmseSymMap;
71+
72+
// Map of symbols defined in the Arm CMSE import library. The linker must
73+
// preserve the addresses in the output objects.
74+
llvm::StringMap<Defined *> cmseImportLib;
75+
76+
// True if <sym> from the input Arm CMSE import library is written to the
77+
// output Arm CMSE import library.
78+
llvm::StringMap<bool> inCMSEOutImpLib;
79+
6380
private:
6481
SmallVector<Symbol *, 0> findByVersion(SymbolVersion ver);
6582
SmallVector<Symbol *, 0> findAllByVersion(SymbolVersion ver,

lld/ELF/SyntheticSections.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3828,6 +3828,7 @@ void InStruct::reset() {
38283828
got.reset();
38293829
gotPlt.reset();
38303830
igotPlt.reset();
3831+
armCmseSGSection.reset();
38313832
ppc64LongBranchTarget.reset();
38323833
mipsAbiFlags.reset();
38333834
mipsGot.reset();

lld/ELF/SyntheticSections.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,51 @@ class ThunkSection final : public SyntheticSection {
11431143
size_t size = 0;
11441144
};
11451145

1146+
// Cortex-M Security Extensions. Prefix for functions that should be exported
1147+
// for the non-secure world.
1148+
const char ACLESESYM_PREFIX[] = "__acle_se_";
1149+
const int ACLESESYM_SIZE = 8;
1150+
1151+
class ArmCmseSGVeneer : public SyntheticSection {
1152+
public:
1153+
ArmCmseSGVeneer(Symbol *sym, Symbol *acleSeSym,
1154+
std::optional<uint64_t> addr = std::nullopt)
1155+
: SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
1156+
llvm::ELF::SHT_PROGBITS,
1157+
/*alignment=*/32, ".gnu.sgstubs"),
1158+
sym(sym), acleSeSym(acleSeSym), entAddr{addr} {
1159+
entsize = ACLESESYM_SIZE;
1160+
}
1161+
1162+
void writeTo(uint8_t *buf) override;
1163+
size_t getSize() const override { return entsize; };
1164+
const std::optional<uint64_t> getAddr() const { return entAddr; };
1165+
1166+
Symbol *sym;
1167+
Symbol *acleSeSym;
1168+
1169+
private:
1170+
const std::optional<uint64_t> entAddr;
1171+
};
1172+
1173+
class ArmCmseSGSection : public SyntheticSection {
1174+
public:
1175+
ArmCmseSGSection();
1176+
bool isNeeded() const override { return !entries.empty(); }
1177+
size_t getSize() const override;
1178+
void writeTo(uint8_t *buf) override;
1179+
void addSGVeneer(Symbol *sym, Symbol *ext_sym);
1180+
void addMappingSymbol();
1181+
void finalizeContents() override;
1182+
void exportEntries(SymbolTableBaseSection *symTab);
1183+
uint64_t impLibMaxAddr = 0;
1184+
1185+
private:
1186+
SmallVector<std::pair<Symbol *, Symbol *>, 0> entries;
1187+
SmallVector<ArmCmseSGVeneer *, 0> sgSections;
1188+
uint64_t newEntries = 0;
1189+
};
1190+
11461191
// Used to compute outSecOff of .got2 in each object file. This is needed to
11471192
// synthesize PLT entries for PPC32 Secure PLT ABI.
11481193
class PPC32Got2Section final : public SyntheticSection {
@@ -1279,6 +1324,7 @@ struct InStruct {
12791324
std::unique_ptr<GotSection> got;
12801325
std::unique_ptr<GotPltSection> gotPlt;
12811326
std::unique_ptr<IgotPltSection> igotPlt;
1327+
std::unique_ptr<SyntheticSection> armCmseSGSection;
12821328
std::unique_ptr<PPC64LongBranchTargetSection> ppc64LongBranchTarget;
12831329
std::unique_ptr<SyntheticSection> mipsAbiFlags;
12841330
std::unique_ptr<MipsGotSection> mipsGot;

lld/ELF/Target.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ static inline std::string getErrorLocation(const uint8_t *loc) {
200200
return getErrorPlace(loc).loc;
201201
}
202202

203+
void processArmCmseSymbols();
204+
203205
void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
204206

205207
unsigned getPPCDFormOp(unsigned secondaryOp);
@@ -221,6 +223,7 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn);
221223
void addPPC64SaveRestore();
222224
uint64_t getPPC64TocBase();
223225
uint64_t getAArch64Page(uint64_t expr);
226+
template <typename ELFT> void writeARMCmseImportLib();
224227
void riscvFinalizeRelax(int passes);
225228
void mergeRISCVAttributesSections();
226229

lld/ELF/Writer.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ template <class ELFT> void elf::createSyntheticSections() {
454454
in.igotPlt = std::make_unique<IgotPltSection>();
455455
add(*in.igotPlt);
456456

457+
if (config->emachine == EM_ARM) {
458+
in.armCmseSGSection = std::make_unique<ArmCmseSGSection>();
459+
add(*in.armCmseSGSection);
460+
}
461+
457462
// _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat
458463
// it as a relocation and ensure the referenced section is created.
459464
if (ElfSym::globalOffsetTable && config->emachine != EM_MIPS) {
@@ -595,6 +600,9 @@ template <class ELFT> void Writer<ELFT>::run() {
595600
if (auto e = buffer->commit())
596601
fatal("failed to write output '" + buffer->getPath() +
597602
"': " + toString(std::move(e)));
603+
604+
if (!config->cmseOutputLib.empty())
605+
writeARMCmseImportLib<ELFT>();
598606
}
599607
}
600608

@@ -1983,6 +1991,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
19831991

19841992
removeUnusedSyntheticSections();
19851993
script->diagnoseOrphanHandling();
1994+
script->diagnoseMissingSGSectionAddress();
19861995

19871996
sortSections();
19881997

@@ -2134,6 +2143,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
21342143
// static symbol table.
21352144
finalizeSynthetic(in.symTab.get());
21362145
finalizeSynthetic(in.ppc64LongBranchTarget.get());
2146+
finalizeSynthetic(in.armCmseSGSection.get());
21372147
}
21382148

21392149
// Relaxation to delete inter-basic block jumps created by basic block

0 commit comments

Comments
 (0)