This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [RFC PATCH, binutils, ARM 8/11, ping2] Add support for ARMv8-M Secure Gateway veneer generation
- From: Thomas Preudhomme <thomas dot preudhomme at foss dot arm dot com>
- To: "Richard Earnshaw (lists)" <Richard dot Earnshaw at arm dot com>
- Cc: binutils at sourceware dot org
- Date: Tue, 26 Jul 2016 12:07:26 +0100
- Subject: Re: [RFC PATCH, binutils, ARM 8/11, ping2] Add support for ARMv8-M Secure Gateway veneer generation
- Authentication-results: sourceware.org; auth=none
- References: <005001d13d57$e3fb95a0$abf2c0e0$@foss.arm.com> <3357274.0RmD5l3GQD@e108577-lin> <12243290.AlEVVf5MU2@e108577-lin>
Hi Richard,
Ping?
Best regards,
Thomas
On Thursday 14 July 2016 10:14:57 Thomas Preudhomme wrote:
> Hi Richard,
>
> On Thursday 07 July 2016 16:27:27 Thomas Preudhomme wrote:
> > On Thursday 07 July 2016 11:52:56 Richard Earnshaw wrote:
> > > + As per "ARMv8-M Security Extensions: Requirements on Development
> > > Tools"
> > > + document, a secure gateway veneer is needed when there exists a
> > > non-local
> > > + function symbol called "normal" symbol (eg. foo) with the same value
> > > as a
> > > + symbol with the same type, binding a name save for a __acle_se_
> > > prefix,
> > > + called a "special" symbol (eg. __acle_se_foo). Entry functions
> > > handling + with secure state transition by themselves have these
> > > symbols with different
> > > + values.
> > >
> > >
> > > This is very unclear as to what is being/needs to be done. Can you try
> > > redrafting it?
> >
> > What do you think of the attached updated patch?
>
> Ping?
>
> > By the way, I realized I did not copy over the explanation for the changes
> > to elf32_arm_get_plt_info from the original patch submission. I'm adding
> > the full cover letter again here to have all the information in one email
> > in the archive.
> >
> >
> >
> > This patch is part of a patch series to add support for ARMv8-M security
> > extension[1] to GNU ld. This specific patch adds support for creating
> > ARMv8-M Secure Gateway veneers.
> >
> > ARM v8-M security extensions require [3] secure gateway veneers to be
> > generated for (secure) entry function in order for code to transition from
> > non-secure state to secure state when calling these entry function. Unlike
> > other veneers, these veneers are generated independently of relocations,
> > ie
> > a veneer can be generated in the absence of relocation. The condition for
> > the generation is that the normal symbol (the one whose name is the same
> > as
> > in C) of an entry function has the same value as the special symbol
> > (normal
> > symbol prefixed with "__acle_se_"). When that happens, the normal symbol
> > is
> > rebound to the veneer generated. When the two symbols have different value
> > it indicates that the entry function already contains an sg instruction to
> > do the secure state transition and the normal symbol points to the sg
> > instruction while the special symbol points after that.
> >
> > This patch also makes use of the infrastructure laid out in previous
> > patches to control the address of these veneers and to avoid the presence
> > of the bit pattern of the SG instruction in non secure callable memory,
> > as required [4] [5].
> >
> > Finally, the patch also contains a small change to elf32_arm_get_plt_info
> > () to return FALSE when there is no PLT, ensuring that a NULL splt is not
> > dereferenced in the block starting with "If the call goes through" in
> > elf32_arm_final_relocate (). This was not necessary before because
> > root_plt-
> >
> > >offset is set to -1 in elf32_arm_adjust_dynamic_symbol called by
> >
> > _bfd_elf_adjust_dynamic_symbol from bfd_elf_size_dynamic_sections when
> > dynobj is not NULL. However, dynobj is set in elf32_arm_check_relocs which
> > is not called when there is no relocation in the input section. Such a
> > situation is possible while still invoking elf32_arm_final_relocate () due
> > to SG veneers being created in the absence of relocation but needing
> > themselves relocation.
> >
> > [1] Software requirements for ARMv8-M security extension are described in
> > document ARM-ECM-0359818 [2]
> > [2] Available on http://infocenter.arm.com in Developer guides and
> > articles
> >
> > > Software development > ARM®v8-M Security Extensions: Requirements on
> >
> > Development Tools
> > [3] See section 3.4.3 and requirement 44 of ARM-ECM-0359818 [2]
> > [4] requirement 14 and following comment of ARM-ECM-0359818 [2]
> > [5] requirement 12 and 13 and following comment of ARM-ECM-0359818 [2]
> >
> >
> > ChangeLog entries remain unchanged:
> >
> > *** bfd/ChangeLog ***
> >
> > 2016-05-04 Thomas Preud'homme <thomas.preudhomme@arm.com>
> >
> > * elf32-arm.c (CMSE_PREFIX): Define macro.
> > (elf32_arm_stub_cmse_branch_thumb_only): Define stub sequence.
> > (cmse_branch_thumb_only): Declare stub.
> > (struct elf32_arm_link_hash_table): Define cmse_stub_sec field.
> > (elf32_arm_get_plt_info): Add globals parameter. Use it to return
> > FALSE if there is no PLT.
> > (arm_type_of_stub): Adapt to new elf32_arm_get_plt_info signature.
> > (elf32_arm_final_link_relocate): Likewise.
> > (elf32_arm_gc_sweep_hook): Likewise.
> > (elf32_arm_gc_mark_extra_sections): Mark sections holding ARMv8-M
> > secure entry functions.
> > (arm_stub_is_thumb): Add case for arm_stub_cmse_branch_thumb_only.
> > (arm_dedicated_stub_output_section_required): Change to a switch
> >
> > case and add a case for arm_stub_cmse_branch_thumb_only.
> >
> > (arm_dedicated_stub_output_section_required_alignment): Likewise.
> > (arm_stub_dedicated_output_section_name): Likewise.
> > (arm_stub_dedicated_input_section_ptr): Likewise and remove
> > ATTRIBUTE_UNUSED for htab parameter.
> > (arm_stub_required_alignment): Likewise.
> > (arm_stub_sym_claimed): Likewise.
> > (arm_dedicated_stub_section_padding): Likewise.
> > (cmse_scan): New function.
> > (elf32_arm_size_stubs): Call cmse_scan for ARM M profile targets.
> > Set stub_changed to TRUE if such veneers were created.
> > (elf32_arm_swap_symbol_in): Add detection code for CMSE special
> > symbols.
> >
> > *** include/elf/ChangeLog ***
> >
> > 2015-12-16 Thomas Preud'homme <thomas.preudhomme@arm.com>
> >
> > * arm.h (ARM_GET_SYM_CMSE_SPCL): Define macro.
> > (ARM_SET_SYM_CMSE_SPCL): Likewise.
> >
> > *** ld/ChangeLog ***
> >
> > 2016-02-17 Thomas Preud'homme <thomas.preudhomme@arm.com>
> >
> > * ld.texinfo (Placement of SG veneers): New concept entry.
> > * testsuite/ld-arm/arm-elf.exp
> > (Secure gateway veneers: no .gnu.sgstubs section): New test.
> > (Secure gateway veneers: wrong entry functions): Likewise.
> > (Secure gateway veneers (ARMv8-M Baseline)): Likewise.
> > (Secure gateway veneers (ARMv8-M Mainline)): Likewise.
> > * testsuite/ld-arm/cmse-veneers.s: New file.
> > * testsuite/ld-arm/cmse-veneers.d: Likewise.
> > * testsuite/ld-arm/cmse-veneers.rd: Likewise.
> > * testsuite/ld-arm/cmse-veneers.sd: Likewise.
> > * testsuite/ld-arm/cmse-veneers-no-gnu_sgstubs.out: Likewise.
> > * testsuite/ld-arm/cmse-veneers-wrong-entryfct.out: Likewise.
> >
> > Best regards,
> >
> > Thomas
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index 8de01b46671cd2ac1cbd94ef4373db439a62a9c4..b5da179d266845c6bf2aa1c9359507a2c1f0e1d1 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -2138,6 +2138,8 @@ typedef unsigned short int insn16;
#define STUB_ENTRY_NAME "__%s_veneer"
+#define CMSE_PREFIX "__acle_se_"
+
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
@@ -2543,6 +2545,13 @@ static const insn_sequence elf32_arm_stub_long_branch_arm_nacl_pic[] =
DATA_WORD (0, R_ARM_NONE, 0), /* .word 0 */
};
+/* Stub used for transition to secure state (aka SG veneer). */
+static const insn_sequence elf32_arm_stub_cmse_branch_thumb_only[] =
+{
+ THUMB32_INSN (0xe97fe97f), /* sg. */
+ THUMB32_B_INSN (0xf000b800, -4), /* b.w original_branch_dest. */
+};
+
/* Cortex-A8 erratum-workaround stubs. */
@@ -2622,6 +2631,7 @@ static const insn_sequence elf32_arm_stub_a8_veneer_blx[] =
DEF_STUB(long_branch_v4t_thumb_tls_pic) \
DEF_STUB(long_branch_arm_nacl) \
DEF_STUB(long_branch_arm_nacl_pic) \
+ DEF_STUB(cmse_branch_thumb_only) \
DEF_STUB(a8_veneer_b_cond) \
DEF_STUB(a8_veneer_b) \
DEF_STUB(a8_veneer_bl) \
@@ -3172,6 +3182,9 @@ struct elf32_arm_link_hash_table
information on stub grouping. */
struct map_stub *stub_group;
+ /* Input stub section holding secure gateway veneers. */
+ asection *cmse_stub_sec;
+
/* Number of elements in stub_group. */
unsigned int top_id;
@@ -3320,12 +3333,16 @@ elf32_arm_create_local_iplt (bfd *abfd, unsigned long r_symndx)
union and *ARM_PLT at the ARM-specific information. */
static bfd_boolean
-elf32_arm_get_plt_info (bfd *abfd, struct elf32_arm_link_hash_entry *h,
+elf32_arm_get_plt_info (bfd *abfd, struct elf32_arm_link_hash_table *globals,
+ struct elf32_arm_link_hash_entry *h,
unsigned long r_symndx, union gotplt_union **root_plt,
struct arm_plt_info **arm_plt)
{
struct arm_local_iplt_info *local_iplt;
+ if (globals->root.splt == NULL && globals->root.iplt == NULL)
+ return FALSE;
+
if (h != NULL)
{
*root_plt = &h->root.plt;
@@ -3786,6 +3803,7 @@ arm_stub_is_thumb (enum elf32_arm_stub_type stub_type)
case arm_stub_long_branch_v4t_thumb_arm_pic:
case arm_stub_long_branch_v4t_thumb_tls_pic:
case arm_stub_long_branch_thumb_only_pic:
+ case arm_stub_cmse_branch_thumb_only:
return TRUE;
case arm_stub_none:
BFD_FAIL ();
@@ -3851,8 +3869,9 @@ arm_type_of_stub (struct bfd_link_info *info,
the address of the appropriate trampoline. */
if (r_type != R_ARM_TLS_CALL
&& r_type != R_ARM_THM_TLS_CALL
- && elf32_arm_get_plt_info (input_bfd, hash, ELF32_R_SYM (rel->r_info),
- &root_plt, &arm_plt)
+ && elf32_arm_get_plt_info (input_bfd, globals, hash,
+ ELF32_R_SYM (rel->r_info), &root_plt,
+ &arm_plt)
&& root_plt->offset != (bfd_vma) -1)
{
asection *splt;
@@ -4163,7 +4182,16 @@ arm_dedicated_stub_output_section_required (enum elf32_arm_stub_type stub_type)
if (stub_type >= max_stub_type)
abort (); /* Should be unreachable. */
- return FALSE;
+ switch (stub_type)
+ {
+ case arm_stub_cmse_branch_thumb_only:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ abort (); /* Should be unreachable. */
}
/* Required alignment (as a power of 2) for the dedicated section holding
@@ -4177,8 +4205,19 @@ arm_dedicated_stub_output_section_required_alignment
if (stub_type >= max_stub_type)
abort (); /* Should be unreachable. */
- BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
- return 0;
+ switch (stub_type)
+ {
+ /* Vectors of Secure Gateway veneers must be aligned on 32byte
+ boundary. */
+ case arm_stub_cmse_branch_thumb_only:
+ return 5;
+
+ default:
+ BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
+ return 0;
+ }
+
+ abort (); /* Should be unreachable. */
}
/* Name of the dedicated output section to put veneers of type STUB_TYPE, or
@@ -4190,8 +4229,17 @@ arm_dedicated_stub_output_section_name (enum elf32_arm_stub_type stub_type)
if (stub_type >= max_stub_type)
abort (); /* Should be unreachable. */
- BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
- return NULL;
+ switch (stub_type)
+ {
+ case arm_stub_cmse_branch_thumb_only:
+ return ".gnu.sgstubs";
+
+ default:
+ BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
+ return NULL;
+ }
+
+ abort (); /* Should be unreachable. */
}
/* If veneers of type STUB_TYPE should go in a dedicated output section,
@@ -4199,15 +4247,23 @@ arm_dedicated_stub_output_section_name (enum elf32_arm_stub_type stub_type)
corresponding input section. Otherwise, returns NULL. */
static asection **
-arm_dedicated_stub_input_section_ptr
- (struct elf32_arm_link_hash_table *htab ATTRIBUTE_UNUSED,
- enum elf32_arm_stub_type stub_type)
+arm_dedicated_stub_input_section_ptr (struct elf32_arm_link_hash_table *htab,
+ enum elf32_arm_stub_type stub_type)
{
if (stub_type >= max_stub_type)
abort (); /* Should be unreachable. */
- BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
- return NULL;
+ switch (stub_type)
+ {
+ case arm_stub_cmse_branch_thumb_only:
+ return &htab->cmse_stub_sec;
+
+ default:
+ BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
+ return NULL;
+ }
+
+ abort (); /* Should be unreachable. */
}
/* Find or create a stub section to contain a stub of type STUB_TYPE. SECTION
@@ -4429,6 +4485,7 @@ arm_stub_required_alignment (enum elf32_arm_stub_type stub_type)
case arm_stub_long_branch_thumb_only_pic:
case arm_stub_long_branch_any_tls_pic:
case arm_stub_long_branch_v4t_thumb_tls_pic:
+ case arm_stub_cmse_branch_thumb_only:
case arm_stub_a8_veneer_blx:
return 4;
@@ -4450,7 +4507,16 @@ arm_stub_sym_claimed (enum elf32_arm_stub_type stub_type)
if (stub_type >= max_stub_type)
abort (); /* Should be unreachable. */
- return FALSE;
+ switch (stub_type)
+ {
+ case arm_stub_cmse_branch_thumb_only:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ abort (); /* Should be unreachable. */
}
/* Returns the padding needed for the dedicated section used stubs of type
@@ -4462,7 +4528,16 @@ arm_dedicated_stub_section_padding (enum elf32_arm_stub_type stub_type)
if (stub_type >= max_stub_type)
abort (); /* Should be unreachable. */
- return 0;
+ switch (stub_type)
+ {
+ case arm_stub_cmse_branch_thumb_only:
+ return 32;
+
+ default:
+ return 0;
+ }
+
+ abort (); /* Should be unreachable. */
}
static bfd_boolean
@@ -5353,6 +5428,204 @@ elf32_arm_create_stub (struct elf32_arm_link_hash_table *htab,
return TRUE;
}
+/* Scan symbols in INPUT_BFD to identify secure entry functions needing a
+ gateway veneer to transition from non secure to secure state and create them
+ accordingly.
+
+ "ARMv8-M Security Extensions: Requirements on Development Tools" document
+ defines the conditions that govern Secure Gateway veneer creation for a
+ given symbol <SYM> as follows:
+ - it has function type
+ - it has non local binding
+ - a symbol named __acle_se_<SYM> (called special symbol) exists with the
+ same type, binding and value as <SYM> (called normal symbol).
+ An entry function can handle secure state transition itself in which case
+ its special symbol would have a different value from the normal symbol.
+
+ OUT_ATTR gives the output attributes, SYM_HASHES the symbol index to hash
+ entry mapping while HTAB gives the name to hash entry mapping.
+
+ If any secure gateway veneer is created, *STUB_CHANGED is set to TRUE. The
+ return value gives whether a stub failed to be allocated. */
+
+static bfd_boolean
+cmse_scan (bfd *input_bfd, struct elf32_arm_link_hash_table *htab,
+ obj_attribute *out_attr, struct elf_link_hash_entry **sym_hashes,
+ bfd_boolean *stub_changed)
+{
+ const struct elf_backend_data *bed;
+ Elf_Internal_Shdr *symtab_hdr;
+ unsigned i, j, sym_count, ext_start;
+ Elf_Internal_Sym *cmse_sym, *local_syms;
+ struct elf32_arm_link_hash_entry *hash, *cmse_hash = NULL;
+ enum arm_st_branch_type branch_type;
+ char *sym_name, *lsym_name;
+ bfd_vma sym_value;
+ asection *section;
+ bfd_boolean is_v8m, new_stub, created_stub, cmse_invalid, ret = TRUE;
+
+ bed = get_elf_backend_data (input_bfd);
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ sym_count = symtab_hdr->sh_size / bed->s->sizeof_sym;
+ ext_start = symtab_hdr->sh_info;
+ is_v8m = (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V8M_BASE
+ && out_attr[Tag_CPU_arch_profile].i == 'M');
+
+ local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (local_syms == NULL)
+ local_syms = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
+ symtab_hdr->sh_info, 0, NULL, NULL,
+ NULL);
+ if (symtab_hdr->sh_info && local_syms == NULL)
+ return FALSE;
+
+ /* Scan symbols. */
+ for (i = 0; i < sym_count; i++)
+ {
+ cmse_invalid = FALSE;
+
+ if (i < ext_start)
+ {
+ cmse_sym = &local_syms[i];
+ /* Not a special symbol. */
+ if (!ARM_GET_SYM_CMSE_SPCL (cmse_sym->st_target_internal))
+ continue;
+ sym_name = bfd_elf_string_from_elf_section (input_bfd,
+ symtab_hdr->sh_link,
+ cmse_sym->st_name);
+ /* Special symbol with local binding. */
+ cmse_invalid = TRUE;
+ }
+ else
+ {
+ cmse_hash = elf32_arm_hash_entry (sym_hashes[i - ext_start]);
+ sym_name = (char *) cmse_hash->root.root.root.string;
+
+ /* Not a special symbol. */
+ if (!ARM_GET_SYM_CMSE_SPCL (cmse_hash->root.target_internal))
+ continue;
+
+ /* Special symbol has incorrect binding or type. */
+ if ((cmse_hash->root.root.type != bfd_link_hash_defined
+ && cmse_hash->root.root.type != bfd_link_hash_defweak)
+ || cmse_hash->root.type != STT_FUNC)
+ cmse_invalid = TRUE;
+ }
+
+ if (!is_v8m)
+ {
+ (*_bfd_error_handler) (_("%B: Special symbol `%s' only allowed for "
+ "ARMv8-M architecture or later."),
+ input_bfd, sym_name);
+ is_v8m = TRUE; /* Avoid multiple warning. */
+ ret = FALSE;
+ }
+
+ if (cmse_invalid)
+ {
+ (*_bfd_error_handler) (_("%B: invalid special symbol `%s'."),
+ input_bfd, sym_name);
+ (*_bfd_error_handler) (_("It must be a global or weak function "
+ "symbol."));
+ ret = FALSE;
+ if (i < ext_start)
+ continue;
+ }
+
+ sym_name += strlen (CMSE_PREFIX);
+ hash = (struct elf32_arm_link_hash_entry *)
+ elf_link_hash_lookup (&(htab)->root, sym_name, FALSE, FALSE, TRUE);
+
+ /* No associated normal symbol or it is neither global nor weak. */
+ if (!hash
+ || (hash->root.root.type != bfd_link_hash_defined
+ && hash->root.root.type != bfd_link_hash_defweak)
+ || hash->root.type != STT_FUNC)
+ {
+ /* Initialize here to avoid warning about use of possibly
+ uninitialized variable. */
+ j = 0;
+
+ if (!hash)
+ {
+ /* Searching for a normal symbol with local binding. */
+ for (; j < ext_start; j++)
+ {
+ lsym_name =
+ bfd_elf_string_from_elf_section (input_bfd,
+ symtab_hdr->sh_link,
+ local_syms[j].st_name);
+ if (!strcmp (sym_name, lsym_name))
+ break;
+ }
+ }
+
+ if (hash || j < ext_start)
+ {
+ (*_bfd_error_handler)
+ (_("%B: invalid standard symbol `%s'."), input_bfd, sym_name);
+ (*_bfd_error_handler)
+ (_("It must be a global or weak function symbol."));
+ }
+ else
+ (*_bfd_error_handler)
+ (_("%B: absent standard symbol `%s'."), input_bfd, sym_name);
+ ret = FALSE;
+ if (!hash)
+ continue;
+ }
+
+ sym_value = hash->root.root.u.def.value;
+ section = hash->root.root.u.def.section;
+
+ if (cmse_hash->root.root.u.def.section != section)
+ {
+ (*_bfd_error_handler)
+ (_("%B: `%s' and its special symbol are in different sections."),
+ input_bfd, sym_name);
+ ret = FALSE;
+ }
+ if (cmse_hash->root.root.u.def.value != sym_value)
+ continue; /* Ignore: could be an entry function starting with SG. */
+
+ /* If this section is a link-once section that will be discarded, then
+ don't create any stubs. */
+ if (section->output_section == NULL)
+ {
+ (*_bfd_error_handler)
+ (_("%B: entry function `%s' not output."), input_bfd, sym_name);
+ continue;
+ }
+
+ if (hash->root.size == 0)
+ {
+ (*_bfd_error_handler)
+ (_("%B: entry function `%s' is empty."), input_bfd, sym_name);
+ ret = FALSE;
+ }
+
+ if (!ret)
+ continue;
+ branch_type = ARM_GET_SYM_BRANCH_TYPE (hash->root.target_internal);
+ created_stub
+ = elf32_arm_create_stub (htab, arm_stub_cmse_branch_thumb_only,
+ NULL, NULL, section, hash, sym_name,
+ sym_value, branch_type, &new_stub);
+
+ if (!created_stub)
+ ret = FALSE;
+ else
+ {
+ BFD_ASSERT (new_stub);
+ *stub_changed = TRUE;
+ }
+ }
+
+ if (!symtab_hdr->contents)
+ free (local_syms);
+ return ret;
+}
+
/* Determine and set the size of the stub section for a final link.
The basic idea here is to examine all the relocations looking for
@@ -5369,8 +5642,9 @@ elf32_arm_size_stubs (bfd *output_bfd,
unsigned int),
void (*layout_sections_again) (void))
{
+ obj_attribute *out_attr;
bfd_size_type stub_group_size;
- bfd_boolean stubs_always_after_branch;
+ bfd_boolean m_profile, stubs_always_after_branch, first_veneer_scan = TRUE;
struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
struct a8_erratum_fix *a8_fixes = NULL;
unsigned int num_a8_fixes = 0, a8_fix_table_size = 10;
@@ -5399,6 +5673,8 @@ elf32_arm_size_stubs (bfd *output_bfd,
htab->layout_sections_again = layout_sections_again;
stubs_always_after_branch = group_size < 0;
+ out_attr = elf_known_obj_attributes_proc (output_bfd);
+ m_profile = out_attr[Tag_CPU_arch_profile].i == 'M';
/* The Cortex-A8 erratum fix depends on stubs not being in the same 4K page
as the first half of a 32-bit branch straddling two 4K pages. This is a
crude way of enforcing that. */
@@ -5464,6 +5740,18 @@ elf32_arm_size_stubs (bfd *output_bfd,
if (symtab_hdr->sh_info == 0)
continue;
+ /* Limit scan of symbols to object file whose profile is
+ Microcontroller to not hinder performance in the general case. */
+ if (m_profile && first_veneer_scan)
+ {
+ struct elf_link_hash_entry **sym_hashes;
+
+ sym_hashes = elf_sym_hashes (input_bfd);
+ if (!cmse_scan (input_bfd, htab, out_attr, sym_hashes,
+ &stub_changed))
+ goto error_ret_free_local;
+ }
+
/* Walk over each section attached to the input bfd. */
for (section = input_bfd->sections;
section != NULL;
@@ -5850,6 +6138,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
/* Ask the linker to do its stuff. */
(*htab->layout_sections_again) ();
+ first_veneer_scan = FALSE;
}
/* Add stubs for Cortex-A8 erratum fixes now. */
@@ -9191,7 +9480,8 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
/* Find out whether the symbol has a PLT. Set ST_VALUE, BRANCH_TYPE and
VALUE appropriately for relocations that we resolve at link time. */
has_iplt_entry = FALSE;
- if (elf32_arm_get_plt_info (input_bfd, eh, r_symndx, &root_plt, &arm_plt)
+ if (elf32_arm_get_plt_info (input_bfd, globals, eh, r_symndx, &root_plt,
+ &arm_plt)
&& root_plt->offset != (bfd_vma) -1)
{
plt_offset = root_plt->offset;
@@ -13603,7 +13893,8 @@ elf32_arm_gc_sweep_hook (bfd * abfd,
}
if (may_need_local_target_p
- && elf32_arm_get_plt_info (abfd, eh, r_symndx, &root_plt, &arm_plt))
+ && elf32_arm_get_plt_info (abfd, globals, eh, r_symndx, &root_plt,
+ &arm_plt))
{
/* If PLT refcount book-keeping is wrong and too low, we'll
see a zero value (going to -1) for the root PLT reference
@@ -14071,7 +14362,11 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
}
/* Unwinding tables are not referenced directly. This pass marks them as
- required if the corresponding code section is marked. */
+ required if the corresponding code section is marked. Similarly, ARMv8-M
+ secure entry functions can only be referenced by SG veneers which are
+ created after the GC process. They need to be marked in case they reside in
+ their own section (as would be the case if code was compiled with
+ -ffunction-sections). */
static bfd_boolean
elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
@@ -14079,10 +14374,21 @@ elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
{
bfd *sub;
Elf_Internal_Shdr **elf_shdrp;
- bfd_boolean again;
+ asection *cmse_sec;
+ obj_attribute *out_attr;
+ Elf_Internal_Shdr *symtab_hdr;
+ unsigned i, sym_count, ext_start;
+ const struct elf_backend_data *bed;
+ struct elf_link_hash_entry **sym_hashes;
+ struct elf32_arm_link_hash_entry *cmse_hash;
+ bfd_boolean again, is_v8m, first_bfd_browse = TRUE;
_bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+ out_attr = elf_known_obj_attributes_proc (info->output_bfd);
+ is_v8m = out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V8M_BASE
+ && out_attr[Tag_CPU_arch_profile].i == 'M';
+
/* Marking EH data may cause additional code sections to be marked,
requiring multiple passes. */
again = TRUE;
@@ -14113,7 +14419,34 @@ elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
return FALSE;
}
}
+
+ /* Mark section holding ARMv8-M secure entry functions. We mark all
+ of them so no need for a second browsing. */
+ if (is_v8m && first_bfd_browse)
+ {
+ sym_hashes = elf_sym_hashes (sub);
+ bed = get_elf_backend_data (sub);
+ symtab_hdr = &elf_tdata (sub)->symtab_hdr;
+ sym_count = symtab_hdr->sh_size / bed->s->sizeof_sym;
+ ext_start = symtab_hdr->sh_info;
+
+ /* Scan symbols. */
+ for (i = ext_start; i < sym_count; i++)
+ {
+ cmse_hash = elf32_arm_hash_entry (sym_hashes[i - ext_start]);
+
+ /* Assume it is a special symbol. If not, cmse_scan will
+ warn about it and user can do something about it. */
+ if (ARM_GET_SYM_CMSE_SPCL (cmse_hash->root.target_internal))
+ {
+ cmse_sec = cmse_hash->root.root.u.def.section;
+ if (!_bfd_elf_gc_mark (info, cmse_sec, gc_mark_hook))
+ return FALSE;
+ }
+ }
+ }
}
+ first_bfd_browse = FALSE;
}
return TRUE;
@@ -17765,6 +18098,9 @@ elf32_arm_swap_symbol_in (bfd * abfd,
const void *pshn,
Elf_Internal_Sym *dst)
{
+ Elf_Internal_Shdr *symtab_hdr;
+ const char *name = NULL;
+
if (!bfd_elf32_swap_symbol_in (abfd, psrc, pshn, dst))
return FALSE;
dst->st_target_internal = 0;
@@ -17793,6 +18129,13 @@ elf32_arm_swap_symbol_in (bfd * abfd,
else
ARM_SET_SYM_BRANCH_TYPE (dst->st_target_internal, ST_BRANCH_UNKNOWN);
+ /* Mark CMSE special symbols. */
+ symtab_hdr = & elf_symtab_hdr (abfd);
+ if (symtab_hdr->sh_size)
+ name = bfd_elf_sym_name (abfd, symtab_hdr, dst, NULL);
+ if (name && CONST_STRNEQ (name, CMSE_PREFIX))
+ ARM_SET_SYM_CMSE_SPCL (dst->st_target_internal);
+
return TRUE;
}
diff --git a/include/elf/arm.h b/include/elf/arm.h
index bafc03c52ee686671e847eff272ab5cc8a79398c..abad4731b335485f34a06b45d7edea475cfb321b 100644
--- a/include/elf/arm.h
+++ b/include/elf/arm.h
@@ -384,4 +384,11 @@ enum arm_st_branch_type {
| ((TYPE) & ENUM_ARM_ST_BRANCH_TYPE_BITMASK))
#endif
+/* Get or set whether a symbol is a special symbol of an entry function of CMSE
+ secure code. */
+#define ARM_GET_SYM_CMSE_SPCL(SYM_TARGET_INTERNAL) \
+ (((SYM_TARGET_INTERNAL) >> 2) & 1)
+#define ARM_SET_SYM_CMSE_SPCL(SYM_TARGET_INTERNAL) \
+ (SYM_TARGET_INTERNAL) |= 4
+
#endif /* _ELF_ARM_H */
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index bc16764b0554851217316c438ada863843915307..171ae6e55bc2bf2708a23e822218b8d4e53f8ee6 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -6853,6 +6853,12 @@ entries which only support 512Mb of code.
The @samp{--no-apply-dynamic-relocs} option makes AArch64 linker do not apply
link-time values for dynamic relocations.
+@cindex Placement of SG veneers
+All SG veneers are placed in the special output section @code{.gnu.sgstubs}.
+Its start address must be set, either with the command line option
+@samp{--section-start} or in a linker script, to indicate where to place these
+veneers in memory.
+
@ifclear GENERIC
@lowersections
@end ifclear
diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp
index db25a8d46c985f89e02ce68c8897923c23101661..84301626fb6ab24a6726315e810249b4375b5fdc 100644
--- a/ld/testsuite/ld-arm/arm-elf.exp
+++ b/ld/testsuite/ld-arm/arm-elf.exp
@@ -633,6 +633,33 @@ set armeabitests_nonacl {
{{objdump -d jump-reloc-veneers-long.d}}
"jump-reloc-veneers-long"}
+ {"Secure gateway veneers: no .gnu.sgstubs section" "" ""
+ "-march=armv8-m.base -mthumb"
+ {cmse-veneers.s}
+ {{ld cmse-veneers-no-gnu_sgstubs.out}}
+ "cmse-veneers-no-gnu_sgstubs"}
+ {"Secure gateway veneers: wrong entry functions" "" ""
+ "-march=armv7-m -mthumb --defsym CHECK_ERRORS=1"
+ {cmse-veneers.s}
+ {{ld cmse-veneers-wrong-entryfct.out}}
+ "cmse-veneers-wrong-entryfct"}
+ {"Secure gateway veneers (ARMv8-M Baseline)"
+ "-Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --gc-sections" ""
+ "-march=armv8-m.base -mthumb"
+ {cmse-veneers.s}
+ {{objdump {-d -j .gnu.sgstubs} cmse-veneers.d}
+ {objdump {-h -j .gnu.sgstubs} cmse-veneers.sd}
+ {nm {} cmse-veneers.rd}}
+ "cmse-veneers-baseline"}
+ {"Secure gateway veneers (ARMv8-M Mainline)"
+ "-Ttext=0x8000 --section-start .gnu.sgstubs=0x20000 --gc-sections" ""
+ "-march=armv8-m.main -mthumb"
+ {cmse-veneers.s}
+ {{objdump {-d -j .gnu.sgstubs} cmse-veneers.d}
+ {objdump {-h -j .gnu.sgstubs} cmse-veneers.sd}
+ {nm {} cmse-veneers.rd}}
+ "cmse-veneers-mainline"}
+
{"R_ARM_THM_JUMP19 Relocation veneers: Short"
"--section-start destsect=0x000108002 --section-start .text=0x8000" ""
"-march=armv7-m -mthumb"
diff --git a/ld/testsuite/ld-arm/cmse-veneers-no-gnu_sgstubs.out b/ld/testsuite/ld-arm/cmse-veneers-no-gnu_sgstubs.out
new file mode 100644
index 0000000000000000000000000000000000000000..9d1e5ba3215ae8a29585fcf04ec1f94314670a29
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers-no-gnu_sgstubs.out
@@ -0,0 +1,3 @@
+.*: No address assigned to the veneers output section .gnu.sgstubs
+.*: cannot size stub section: Invalid operation
+#...
diff --git a/ld/testsuite/ld-arm/cmse-veneers-wrong-entryfct.out b/ld/testsuite/ld-arm/cmse-veneers-wrong-entryfct.out
new file mode 100644
index 0000000000000000000000000000000000000000..fd4766ab877a4abbeb8b2c4c053ab11d3f86773c
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers-wrong-entryfct.out
@@ -0,0 +1,19 @@
+.*: .*: Special symbol `__acle_se_loc_entry_veneer1' only allowed for ARMv8-M architecture or later.
+.*: .*: invalid .* symbol `.*loc_entry_veneer1'.
+.*: It must be a global or weak function symbol.
+.*: .*: invalid special symbol `__acle_se_loc_entry_veneer2'.
+.*: It must be a global or weak function symbol.
+.*: .*: invalid special symbol `__acle_se_loc_entry_veneer4'.
+.*: It must be a global or weak function symbol.
+.*: .*: invalid standard symbol `loc_entry_veneer3'.
+.*: It must be a global or weak function symbol.
+.*: .*: invalid standard symbol `loc_entry_veneer5'.
+.*: It must be a global or weak function symbol.
+.*: .*: absent standard symbol `fake_entry_veneer1'.
+.*: .*: invalid standard symbol `obj_entry_veneer1'.
+.*: It must be a global or weak function symbol.
+.*: .*: invalid special symbol `__acle_se_obj_entry_veneer2'.
+.*: It must be a global or weak function symbol.
+.*: .*: `fake_entry_veneer2' and its special symbol are in different sections.
+.*: cannot size stub section: Invalid operation
+#...
diff --git a/ld/testsuite/ld-arm/cmse-veneers.d b/ld/testsuite/ld-arm/cmse-veneers.d
new file mode 100644
index 0000000000000000000000000000000000000000..6a44a2b5dce9de4f5d64fe644e41f5ab9840806e
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers.d
@@ -0,0 +1,21 @@
+
+.*
+
+
+Disassembly of section \.gnu.sgstubs:
+
+00020000 <glob_entry_veneer2>:
+ 20000: e97f e97f sg
+ 20004: f7e8 b800 b\.w 8008 <__acle_se_glob_entry_veneer2>
+
+00020008 <weak_entry_veneer2>:
+ 20008: e97f e97f sg
+ 2000c: f7e7 bffe b\.w 800c <__acle_se_weak_entry_veneer2>
+
+00020010 <glob_entry_veneer1>:
+ 20010: e97f e97f sg
+ 20014: f7e7 bff4 b\.w 8000 <__acle_se_glob_entry_veneer1>
+
+00020018 <weak_entry_veneer1>:
+ 20018: e97f e97f sg
+ 2001c: f7e7 bff2 b\.w 8004 <__acle_se_weak_entry_veneer1>
diff --git a/ld/testsuite/ld-arm/cmse-veneers.rd b/ld/testsuite/ld-arm/cmse-veneers.rd
new file mode 100644
index 0000000000000000000000000000000000000000..20fad961bd8803109c3cf57ef22e3b36da5b1500
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers.rd
@@ -0,0 +1,9 @@
+#...
+[0-9a-f]+ T glob_entry_fct
+#...
+[0-9a-f]+ T glob_entry_veneer1
+[0-9a-f]+ T glob_entry_veneer2
+#...
+[0-9a-f]+ W weak_entry_veneer1
+[0-9a-f]+ W weak_entry_veneer2
+#...
diff --git a/ld/testsuite/ld-arm/cmse-veneers.s b/ld/testsuite/ld-arm/cmse-veneers.s
new file mode 100644
index 0000000000000000000000000000000000000000..d5c57f646a78c4736e4f084d8ad6aff7efea554f
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers.s
@@ -0,0 +1,97 @@
+ .syntax unified
+ .thumb
+ .file "foo.c"
+ .text
+
+.macro decltype name, type
+.ifc \type,object
+ .data
+.else
+ .thumb
+ .thumb_func
+.endif
+ .type \name, %\type
+.endm
+
+
+.macro entry name, type, vis, typespc, visspc, entry_fct
+ .align 2
+.ifb \visspc
+ .\vis __acle_se_\name
+.else
+ .\visspc __acle_se_\name
+.endif
+ .\vis \name
+ .thumb
+ .thumb_func
+.ifb \typespc
+ decltype __acle_se_\name, \type
+.else
+ decltype __acle_se_\name, \typespc
+.endif
+ decltype \name, \type
+__acle_se_\name:
+ \entry_fct
+\name:
+.ifc \type,object
+ .word 42
+.else
+ nop
+.endif
+ .size \name, .-\name
+ .size __acle_se_\name, .-__acle_se_\name
+.endm
+
+
+.ifndef CHECK_ERRORS
+ @ Valid setups for veneer generation
+ entry glob_entry_veneer1, function, global
+ entry weak_entry_veneer1, function, weak
+ entry glob_entry_veneer2, function, global, visspc=weak
+ entry weak_entry_veneer2, function, weak, visspc=global
+
+ @ Valid setup for entry function without SG veneer
+ entry glob_entry_fct, function, global, entry_fct=nop
+
+.else
+ @ Invalid setups for veneer generation (visibility)
+ entry loc_entry_veneer1, function, local
+ entry loc_entry_veneer2, function, global, visspc=local
+ entry loc_entry_veneer3, function, local, visspc=global
+ entry loc_entry_veneer4, function, weak, visspc=local
+ entry loc_entry_veneer5, function, local, visspc=weak
+
+ @ Invalid setups for veneer generation (absent standard symbol)
+ .align 2
+ .global __acle_se_fake_entry_veneer1
+ .thumb
+ .thumb_func
+ .type __acle_se_fake_entry_veneer1, %function
+__acle_se_fake_entry_veneer1:
+ nop
+ .size __acle_se_fake_entry_veneer1, .-__acle_se_fake_entry_veneer1
+
+ @ Invalid setups for veneer generation (type)
+ entry obj_entry_veneer1, object, global, typespc=function
+ entry obj_entry_veneer2, function, global, typespc=object
+
+ @ Invalid setup for veneer generation (sections)
+ .section .text.sub1
+ .align 2
+ .thumb
+ .thumb_func
+ .global __acle_se_fake_entry_veneer2
+ .type __acle_se_fake_entry_veneer2, %function
+__acle_se_fake_entry_veneer2:
+ nop
+ .size __acle_se_fake_entry_veneer2, .-__acle_se_fake_entry_veneer2
+ .section .text.sub2
+ .align 2
+ .thumb
+ .thumb_func
+ .global fake_entry_veneer2
+ .type fake_entry_veneer2, %function
+fake_entry_veneer2:
+ nop
+ .size fake_entry_veneer2, .-fake_entry_veneer2
+.endif
diff --git a/ld/testsuite/ld-arm/cmse-veneers.sd b/ld/testsuite/ld-arm/cmse-veneers.sd
new file mode 100644
index 0000000000000000000000000000000000000000..99bfc8f37c25b7a57122b330b1145e27a9b087fd
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers.sd
@@ -0,0 +1,7 @@
+
+.*
+
+Sections:
+Idx Name Size VMA LMA File off Algn
+ \d+ \.gnu\.sgstubs 00000020 00020000 00020000 00010000 2\*\*5
+ CONTENTS, ALLOC, LOAD, READONLY, CODE