[RFC PATCH, binutils, ARM 6/9] Add support for ARMv8-M Secure Gateway veneer generation

Thomas Preud'homme thomas.preudhomme@foss.arm.com
Wed Dec 23 07:59:00 GMT 2015


Hi,

[Posting patch series as RFC]

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.

It is also required [4] that such veneers must be able to keep their address when the secure code is relinked in order to avoid relinking the non secure code with the secure code. This patch adds support for generating these veneers in a dedicated output section and expect the user to specify the address of that section (either via --section-start parameter or in the linker script). This ensure that they are placed in a memory area with sufficient space for more veneers to be added. Ensuring that existing veneers keep their addresses when the secure executable is relinked is handled by a subsequent patch of this patch serie.

Note: this patch needs the Cortex-A8 errata workaround refactoring patch applied to work (patch 1/9 in the patch serie).

RFC Note: This patch contains some Secure Gateway specific elements that could potentially be made into more generic and independent suitable for separate patch. Namely, these are the padding of stub section and setting a specific output section for a given stub section.


[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]


ChangeLog entries are as follows:

*** bfd/ChangeLog ***

2015-12-16  Thomas Preud'homme  <thomas.preudhomme@arm.com>

        * elf32-arm.c (CMSE_PREFIX): Define macro.
        (CMSE_STUB_OUT_SEC_NAME): Likewise.
        (CMSE_STUB_SEC_PADDING): Likewise.
        (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.
        (arm_stub_is_thumb): Add case for arm_stub_cmse_branch_thumb_only.
        (arm_stub_required_alignment): Likewise.
        (arm_stub_sym_claimed): Likewise.
        (elf32_arm_create_or_find_stub_sec): Add a stub type parameter and
        add support for arm_stub_cmse_branch_thumb_only input and output stub
        sections.
        (elf32_arm_add_stub): Add a stub type parameter and pass it down to
        elf32_arm_create_or_find_stub_sec.
        (elf32_arm_create_stub): Pass stub type down to elf32_arm_add_stub.
        (cmse_scan): New function.
        (elf32_arm_size_stubs): Call cmse_scan for ARM M profile targets to
        generate veneers for entry point functions to ARMv8-M secure code.
        Set stub_changed to TRUE if such veneers were created.  Account for
        32byte padding of secure gateway veneer section when computing its
        size. Pass stub type when calling elf32_arm_create_or_find_stub_sec for
        Cortex-A8 erratum veneers.
        (elf32_arm_build_stubs): Pad the aforementionned stub section to
        32byte boundary.
        (elf32_arm_final_link_relocate): Don't consider a symbol as having a
        PLT if dynobj is NULL.
        (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 ***

2015-12-16  Thomas Preud'homme  <thomas.preudhomme@arm.com>

        * emultempl/armelf.em (arm_elf_before_allocation): Add SEC_KEEP flag
        to .sgstubs output section if it exists.


*** ld/testsuite/ChangeLog ***

2015-12-16  Thomas Preud'homme  <thomas.preudhomme@arm.com>

        * ld-arm/arm-elf.exp (Secure gateway veneers: no .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.
        * ld-arm/cmse-veneers.s: New file.
        * ld-arm/cmse-veneers.d: Likewise.
        * ld-arm/cmse-veneers.rd: Likewise.
        * ld-arm/cmse-veneers.sd: Likewise.
        * ld-arm/cmse-veneers-no-sgstubs.out: Likewise.
        * ld-arm/cmse-veneers-wrong-entryfct.out: Likewise.


diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index b69981ab1259a3415a9974ed2770bf96edfd7eb9..1d9b0135022a6a6a2d9f97564cdd63e267a22e2b 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -2080,6 +2080,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"
@@ -2485,6 +2487,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.  */
 
@@ -2546,6 +2555,14 @@ static const insn_sequence elf32_arm_stub_a8_veneer_blx[] =
    C identifier.  */
 #define STUB_SUFFIX ".__stub"
 
+/* Name of the output section holding secure gateway veneers.  This definition
+   must be kept in sync with name used in ld/emultempl/armelf.em.  */
+#define CMSE_STUB_OUT_SEC_NAME ".sgstubs"
+
+/* Section for secure gateway veneers must be zero padded to a 32 byte
+   boundary.  */
+#define CMSE_STUB_SEC_PADDING 32
+
 /* One entry per long/short branch stub defined above.  */
 #define DEF_STUBS \
   DEF_STUB(long_branch_any_any)	\
@@ -2564,6 +2581,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) \
@@ -3111,6 +3129,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;
 
@@ -3691,6 +3712,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 ();
@@ -4059,68 +4081,94 @@ elf32_arm_get_stub_entry (const asection *input_section,
   return stub_entry;
 }
 
-/* Find or create a stub section.  Returns a pointer to the stub section, and
-   the section to which the stub section will be attached (in *LINK_SEC_P).
-   LINK_SEC_P may be NULL.  */
+/* Find or create a stub section to contain a stub of type STUB_TYPE.  Returns
+   a pointer to the stub section, and the section to which the stub section
+   will be attached (in *LINK_SEC_P).  LINK_SEC_P may be NULL.  */
 
 static asection *
 elf32_arm_create_or_find_stub_sec (asection **link_sec_p, asection *section,
-				   struct elf32_arm_link_hash_table *htab)
+				   struct elf32_arm_link_hash_table *htab,
+				   enum elf32_arm_stub_type stub_type)
 {
-  asection *link_sec;
-  asection *stub_sec;
-  asection *out_sec;
+  asection *link_sec, *out_sec, **stub_sec_p;
+  const char *stub_sec_prefix;
+  int align;
 
-  link_sec = htab->stub_group[section->id].link_sec;
-  BFD_ASSERT (link_sec != NULL);
-  stub_sec = htab->stub_group[section->id].stub_sec;
-
-  if (stub_sec == NULL)
+  if (stub_type == arm_stub_cmse_branch_thumb_only)
     {
-      stub_sec = htab->stub_group[link_sec->id].stub_sec;
-      if (stub_sec == NULL)
+      bfd *output_bfd = htab->obfd;
+      link_sec = NULL;
+      stub_sec_p = &htab->cmse_stub_sec;
+      stub_sec_prefix = CMSE_PREFIX;
+      align = 5;
+      out_sec = bfd_get_section_by_name (output_bfd, CMSE_STUB_OUT_SEC_NAME);
+      if (out_sec == NULL)
 	{
-	  size_t namelen;
-	  bfd_size_type len;
-	  char *s_name;
-
-	  namelen = strlen (link_sec->name);
-	  len = namelen + sizeof (STUB_SUFFIX);
-	  s_name = (char *) bfd_alloc (htab->stub_bfd, len);
-	  if (s_name == NULL)
-	    return NULL;
-
-	  memcpy (s_name, link_sec->name, namelen);
-	  memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
-	  out_sec = link_sec->output_section;
-	  stub_sec = (*htab->add_stub_section) (s_name, out_sec, link_sec,
-						htab->nacl_p ? 4 : 3);
-	  if (stub_sec == NULL)
-	    return NULL;
-	  htab->stub_group[link_sec->id].stub_sec = stub_sec;
+	  (*_bfd_error_handler) (_("No address assigned to the secure gateway "
+				   "veneers output section %s"),
+				 CMSE_STUB_OUT_SEC_NAME);
+	  return NULL;
 	}
-      htab->stub_group[section->id].stub_sec = stub_sec;
+    }
+  else
+    {
+      link_sec = htab->stub_group[section->id].link_sec;
+      BFD_ASSERT (link_sec != NULL);
+      stub_sec_p = &htab->stub_group[section->id].stub_sec;
+      if (*stub_sec_p == NULL)
+	stub_sec_p = &htab->stub_group[link_sec->id].stub_sec;
+      stub_sec_prefix = link_sec->name;
+      out_sec = link_sec->output_section;
+      align = htab->nacl_p ? 4 : 3;
     }
 
+  if (*stub_sec_p == NULL)
+    {
+      size_t namelen;
+      bfd_size_type len;
+      char *s_name;
+
+      namelen = strlen (stub_sec_prefix);
+      len = namelen + sizeof (STUB_SUFFIX);
+      s_name = (char *) bfd_alloc (htab->stub_bfd, len);
+      if (s_name == NULL)
+	return NULL;
+
+      memcpy (s_name, stub_sec_prefix, namelen);
+      memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
+      *stub_sec_p = (*htab->add_stub_section) (s_name, out_sec, link_sec,
+					       align);
+      if (*stub_sec_p == NULL)
+	return NULL;
+
+      out_sec->flags |= SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+			| SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY
+			| SEC_KEEP;
+    }
+
+  if (stub_type != arm_stub_cmse_branch_thumb_only)
+    htab->stub_group[section->id].stub_sec = *stub_sec_p;
+
   if (link_sec_p)
     *link_sec_p = link_sec;
 
-  return stub_sec;
+  return *stub_sec_p;
 }
 
 /* Add a new stub entry to the stub hash.  Not all fields of the new
    stub entry are initialised.  */
 
 static struct elf32_arm_stub_hash_entry *
-elf32_arm_add_stub (const char *stub_name,
-		    asection *section,
-		    struct elf32_arm_link_hash_table *htab)
+elf32_arm_add_stub (const char *stub_name, asection *section,
+		    struct elf32_arm_link_hash_table *htab,
+		    enum elf32_arm_stub_type stub_type)
 {
   asection *link_sec;
   asection *stub_sec;
   struct elf32_arm_stub_hash_entry *stub_entry;
 
-  stub_sec = elf32_arm_create_or_find_stub_sec (&link_sec, section, htab);
+  stub_sec = elf32_arm_create_or_find_stub_sec (&link_sec, section, htab,
+						stub_type);
   if (stub_sec == NULL)
     return NULL;
 
@@ -4247,6 +4295,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;
 
@@ -4289,6 +4338,9 @@ arm_stub_sym_claimed (enum elf32_arm_stub_type stub_type)
     case arm_stub_long_branch_arm_nacl_pic:
       return FALSE;
 
+    case arm_stub_cmse_branch_thumb_only:
+      return TRUE;
+
     default:
       abort ();  /* Should be unreachable.  */
     }
@@ -5132,7 +5184,7 @@ elf32_arm_create_stub (struct elf32_arm_link_hash_table *htab,
       return TRUE;
     }
 
-  stub_entry = elf32_arm_add_stub (stub_name, section, htab);
+  stub_entry = elf32_arm_add_stub (stub_name, section, htab, stub_type);
   if (stub_entry == NULL)
     {
       if (!arm_stub_sym_claimed (stub_type))
@@ -5181,6 +5233,184 @@ elf32_arm_create_stub (struct elf32_arm_link_hash_table *htab,
   return TRUE;
 }
 
+/* Scan symbols in INPUT_BFD for secure gateway veneers to create.  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];
+	  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);
+	  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;
+
+	  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)
+	{
+	  if (!hash)
+	    {
+	      for (j = 0; 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;
+
+      /* Value or input section different.  */
+      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
@@ -5197,8 +5427,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 stubs_always_after_branch, cmse_stub_changed = FALSE;
   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;
@@ -5227,6 +5458,24 @@ 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);
+  /* Limit scan of symbols to object file whose profile is Microcontroller to
+     not hinder performance in the general case.  */
+  if (out_attr[Tag_CPU_arch_profile].i == 'M')
+    {
+      bfd *input_bfd;
+
+      for (input_bfd = info->input_bfds; input_bfd != NULL;
+	   input_bfd = input_bfd->link.next)
+	{
+	  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
+
+	  if (!cmse_scan (input_bfd, htab, out_attr, sym_hashes,
+			  &cmse_stub_changed))
+	    goto error_ret_free_local;
+	}
+    }
+
   /* 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.  */
@@ -5269,10 +5518,11 @@ elf32_arm_size_stubs (bfd *output_bfd,
       bfd *input_bfd;
       unsigned int bfd_indx;
       asection *stub_sec;
-      bfd_boolean stub_changed = FALSE;
+      bfd_boolean stub_changed = cmse_stub_changed;
       unsigned prev_num_a8_fixes = num_a8_fixes;
 
       num_a8_fixes = 0;
+      cmse_stub_changed = FALSE;
       for (input_bfd = info->input_bfds, bfd_indx = 0;
 	   input_bfd != NULL;
 	   input_bfd = input_bfd->link.next, bfd_indx++)
@@ -5280,6 +5530,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
 	  Elf_Internal_Shdr *symtab_hdr;
 	  asection *section;
 	  Elf_Internal_Sym *local_syms = NULL;
+	  struct elf_link_hash_entry **sym_hashes;
 
 	  if (!is_arm_elf (input_bfd))
 	    continue;
@@ -5291,6 +5542,8 @@ elf32_arm_size_stubs (bfd *output_bfd,
 	  if (symtab_hdr->sh_info == 0)
 	    continue;
 
+	  sym_hashes = elf_sym_hashes (input_bfd);
+
 	  /* Walk over each section attached to the input bfd.  */
 	  for (section = input_bfd->sections;
 	       section != NULL;
@@ -5349,8 +5602,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
 		  hash = NULL;
 		  if (r_indx >= symtab_hdr->sh_info)
 		    hash = elf32_arm_hash_entry
-		      (elf_sym_hashes (input_bfd)
-		       [r_indx - symtab_hdr->sh_info]);
+		      (sym_hashes[r_indx - symtab_hdr->sh_info]);
 
 		  /* Only look for stubs on branch instructions, or
 		     non-relaxed TLSCALL  */
@@ -5625,13 +5877,20 @@ elf32_arm_size_stubs (bfd *output_bfd,
 	}
 
       bfd_hash_traverse (&htab->stub_hash_table, arm_size_one_stub, htab);
+      if (htab->cmse_stub_sec)
+	{
+	  int size = htab->cmse_stub_sec->size;
+	  size = (size + CMSE_STUB_SEC_PADDING - 1)
+		 & ~(CMSE_STUB_SEC_PADDING - 1);
+	  htab->cmse_stub_sec->size = size;
+	}
 
       /* Add Cortex-A8 erratum veneers to stub section sizes too.  */
       if (htab->fix_cortex_a8)
 	for (i = 0; i < num_a8_fixes; i++)
 	  {
 	    stub_sec = elf32_arm_create_or_find_stub_sec (NULL,
-			 a8_fixes[i].section, htab);
+			 a8_fixes[i].section, htab, a8_fixes[i].stub_type);
 
 	    if (stub_sec == NULL)
 	      goto error_ret_free_local;
@@ -5743,6 +6002,18 @@ elf32_arm_build_stubs (struct bfd_link_info *info)
   /* Build the stubs as directed by the stub hash table.  */
   table = &htab->stub_hash_table;
   bfd_hash_traverse (table, arm_build_one_stub, info);
+  if (htab->cmse_stub_sec)
+    {
+      bfd_byte *loc;
+      int size, padded_size;
+
+      loc = htab->cmse_stub_sec->contents + htab->cmse_stub_sec->size;
+      size = htab->cmse_stub_sec->size;
+      padded_size = (size + CMSE_STUB_SEC_PADDING - 1)
+		    & ~(CMSE_STUB_SEC_PADDING - 1);
+      memset (loc, 0, padded_size - size);
+      htab->cmse_stub_sec->size = padded_size;
+    }
   if (htab->fix_cortex_a8)
     {
       /* Place the cortex a8 stubs last.  */
@@ -8954,6 +9225,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
      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)
+      && globals->root.dynobj != NULL
       && root_plt->offset != (bfd_vma) -1)
     {
       plt_offset = root_plt->offset;
@@ -17402,6 +17674,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;
@@ -17430,6 +17705,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 1a7fe1cf947bfb890884f77b3b30c035046b3fd5..c7e4c478dd8168c7cb9cd83eb3d9dfbf77730d39 100644
--- a/include/elf/arm.h
+++ b/include/elf/arm.h
@@ -361,4 +361,11 @@ enum arm_st_branch_type {
 #define ARM_SET_SYM_BRANCH_TYPE(SYM_TARGET_INTERNAL,TYPE) \
   ((SYM_TARGET_INTERNAL) = ((SYM_TARGET_INTERNAL) & ~3) | ((TYPE) & 3))
 
+/* 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/emultempl/armelf.em b/ld/emultempl/armelf.em
index f20820733e37b78e45eb6c5f466f6ad2ccea9a14..f297f4574348c0c3705f53638cc9225dfb1978bb 100644
--- a/ld/emultempl/armelf.em
+++ b/ld/emultempl/armelf.em
@@ -70,6 +70,8 @@ gld${EMULATION_NAME}_set_symbols (void)
 static void
 arm_elf_before_allocation (void)
 {
+  asection *cmse_stub_out_sec;
+
   bfd_elf32_arm_set_byteswap_code (&link_info, byteswap_code);
 
   /* Choose type of VFP11 erratum fix, or warn if specified fix is unnecessary
@@ -83,6 +85,15 @@ arm_elf_before_allocation (void)
   /* Auto-select Cortex-A8 erratum fix if it wasn't explicitly specified.  */
   bfd_elf32_arm_set_cortex_a8_fix (link_info.output_bfd, &link_info);
 
+  /* Mark output section for SG veneers with SEC_KEEP to ensure it's not marked
+     for deletion by strip_excluded_output_sections () when veneers are going
+     to be created later.  Not doing so would trigger assert on empty section
+     size in lang_size_sections_1 ().  */
+  cmse_stub_out_sec = bfd_get_section_by_name (link_info.output_bfd,
+					       ".sgstubs");
+  if (cmse_stub_out_sec != NULL)
+    cmse_stub_out_sec->flags |= SEC_KEEP;
+
   /* We should be able to set the size of the interworking stub section.  We
      can't do it until later if we have dynamic sections, though.  */
   if (elf_hash_table (&link_info)->dynobj == NULL)
diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp
index a991c39717791837691f5b01e6a642aec9941e5e..037198fe0f17eef66c10f755ee56ad4dac2d5c8e 100644
--- a/ld/testsuite/ld-arm/arm-elf.exp
+++ b/ld/testsuite/ld-arm/arm-elf.exp
@@ -597,6 +597,33 @@ set armeabitests_nonacl {
      {{objdump -d jump-reloc-veneers-long.d}}
      "jump-reloc-veneers-long"}
 
+    {"Secure gateway veneers: no .sgstubs section" "" ""
+     "-march=armv8-m.base -mthumb"
+     {cmse-veneers.s}
+     {{ld cmse-veneers-no-sgstubs.out}}
+     "cmse-veneers-no-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 .sgstubs=0x20000" ""
+     "-march=armv8-m.base -mthumb"
+     {cmse-veneers.s}
+     {{objdump {-d -j .sgstubs} cmse-veneers.d}
+      {objdump {-h -j .sgstubs} cmse-veneers.sd}
+      {nm {} cmse-veneers.rd}}
+     "cmse-veneers-baseline"}
+    {"Secure gateway veneers (ARMv8-M Mainline)"
+     "-Ttext=0x8000 --section-start .sgstubs=0x20000" ""
+     "-march=armv8-m.main -mthumb"
+     {cmse-veneers.s}
+     {{objdump {-d -j .sgstubs} cmse-veneers.d}
+      {objdump {-h -j .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-sgstubs.out b/ld/testsuite/ld-arm/cmse-veneers-no-sgstubs.out
new file mode 100644
index 0000000000000000000000000000000000000000..6a29a6cc1cb3822d397e5812937ffc659b7b47a7
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers-no-sgstubs.out
@@ -0,0 +1,3 @@
+.*: No address assigned to the secure gateway veneers output section .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..868e5605e9cfc72fef567f896af1e0aff6875296
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers.d
@@ -0,0 +1,21 @@
+
+.*
+
+
+Disassembly of section \.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..a31f284dd125d900e37e5c517edc2487a0964b76
--- /dev/null
+++ b/ld/testsuite/ld-arm/cmse-veneers.sd
@@ -0,0 +1,7 @@
+
+.*
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  1 \.sgstubs      00000020  00020000  00020000  00010000  2\*\*5
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE


The patch doesn't show any regression when running the binutils-gdb testsuite for the arm-none-eabi target.

Any comments?

Best regards,

Thomas



More information about the Binutils mailing list