+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name, FALSE,
+ FALSE);
+ /* The proper stub has already been created, just update its value. */
+ if (stub_entry != NULL)
+ {
+ if (!sym_claimed)
+ free (stub_name);
+ stub_entry->target_value = sym_value;
+ return stub_entry;
+ }
+
+ stub_entry = elf32_arm_add_stub (stub_name, section, htab, stub_type);
+ if (stub_entry == NULL)
+ {
+ if (!sym_claimed)
+ free (stub_name);
+ return NULL;
+ }
+
+ stub_entry->target_value = sym_value;
+ stub_entry->target_section = sym_sec;
+ stub_entry->stub_type = stub_type;
+ stub_entry->h = hash;
+ stub_entry->branch_type = branch_type;
+
+ if (sym_claimed)
+ stub_entry->output_name = sym_name;
+ else
+ {
+ if (sym_name == NULL)
+ sym_name = "unnamed";
+ stub_entry->output_name = (char *)
+ bfd_alloc (htab->stub_bfd, sizeof (THUMB2ARM_GLUE_ENTRY_NAME)
+ + strlen (sym_name));
+ if (stub_entry->output_name == NULL)
+ {
+ free (stub_name);
+ return NULL;
+ }
+
+ /* For historical reasons, use the existing names for ARM-to-Thumb and
+ Thumb-to-ARM stubs. */
+ r_type = ELF32_R_TYPE (irela->r_info);
+ if ((r_type == (unsigned int) R_ARM_THM_CALL
+ || r_type == (unsigned int) R_ARM_THM_JUMP24
+ || r_type == (unsigned int) R_ARM_THM_JUMP19)
+ && branch_type == ST_BRANCH_TO_ARM)
+ sprintf (stub_entry->output_name, THUMB2ARM_GLUE_ENTRY_NAME, sym_name);
+ else if ((r_type == (unsigned int) R_ARM_CALL
+ || r_type == (unsigned int) R_ARM_JUMP24)
+ && branch_type == ST_BRANCH_TO_THUMB)
+ sprintf (stub_entry->output_name, ARM2THUMB_GLUE_ENTRY_NAME, sym_name);
+ else
+ sprintf (stub_entry->output_name, STUB_ENTRY_NAME, sym_name);
+ }
+
+ *new_stub = TRUE;
+ return stub_entry;
+}
+
+/* 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.
+ *CMSE_STUB_CREATED is increased by the number of secure gateway veneer
+ created.
+
+ 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,
+ int *cmse_stub_created)
+{
+ 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;
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ bfd_boolean is_v8m, new_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 (_("%pB: 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 (_("%pB: invalid special symbol `%s'; it must be"
+ " a global or weak function symbol"),
+ input_bfd, sym_name);
+ 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
+ (_("%pB: invalid standard symbol `%s'; it must be "
+ "a global or weak function symbol"),
+ input_bfd, sym_name);
+ }
+ else
+ _bfd_error_handler
+ (_("%pB: 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
+ (_("%pB: `%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
+ (_("%pB: entry function `%s' not output"), input_bfd, sym_name);
+ continue;
+ }
+
+ if (hash->root.size == 0)
+ {
+ _bfd_error_handler
+ (_("%pB: 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);
+ stub_entry
+ = elf32_arm_create_stub (htab, arm_stub_cmse_branch_thumb_only,
+ NULL, NULL, section, hash, sym_name,
+ sym_value, branch_type, &new_stub);
+
+ if (stub_entry == NULL)
+ ret = FALSE;
+ else
+ {
+ BFD_ASSERT (new_stub);
+ (*cmse_stub_created)++;
+ }
+ }
+
+ if (!symtab_hdr->contents)
+ free (local_syms);
+ return ret;
+}
+
+/* Return TRUE iff a symbol identified by its linker HASH entry is a secure
+ code entry function, ie can be called from non secure code without using a
+ veneer. */
+
+static bfd_boolean
+cmse_entry_fct_p (struct elf32_arm_link_hash_entry *hash)
+{
+ bfd_byte contents[4];
+ uint32_t first_insn;
+ asection *section;
+ file_ptr offset;
+ bfd *abfd;
+
+ /* Defined symbol of function type. */
+ if (hash->root.root.type != bfd_link_hash_defined
+ && hash->root.root.type != bfd_link_hash_defweak)
+ return FALSE;
+ if (hash->root.type != STT_FUNC)
+ return FALSE;
+
+ /* Read first instruction. */
+ section = hash->root.root.u.def.section;
+ abfd = section->owner;
+ offset = hash->root.root.u.def.value - section->vma;
+ if (!bfd_get_section_contents (abfd, section, contents, offset,
+ sizeof (contents)))
+ return FALSE;
+
+ first_insn = bfd_get_32 (abfd, contents);
+
+ /* Starts by SG instruction. */
+ return first_insn == 0xe97fe97f;
+}
+
+/* Output the name (in symbol table) of the veneer GEN_ENTRY if it is a new
+ secure gateway veneers (ie. the veneers was not in the input import library)
+ and there is no output import library (GEN_INFO->out_implib_bfd is NULL. */
+
+static bfd_boolean
+arm_list_new_cmse_stub (struct bfd_hash_entry *gen_entry, void *gen_info)
+{
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ struct bfd_link_info *info;
+
+ /* Massage our args to the form they really have. */
+ stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+ info = (struct bfd_link_info *) gen_info;
+
+ if (info->out_implib_bfd)
+ return TRUE;
+
+ if (stub_entry->stub_type != arm_stub_cmse_branch_thumb_only)
+ return TRUE;
+
+ if (stub_entry->stub_offset == (bfd_vma) -1)
+ _bfd_error_handler (" %s", stub_entry->output_name);
+
+ return TRUE;
+}
+
+/* Set offset of each secure gateway veneers so that its address remain
+ identical to the one in the input import library referred by
+ HTAB->in_implib_bfd. A warning is issued for veneers that disappeared
+ (present in input import library but absent from the executable being
+ linked) or if new veneers appeared and there is no output import library
+ (INFO->out_implib_bfd is NULL and *CMSE_STUB_CREATED is bigger than the
+ number of secure gateway veneers found in the input import library.
+
+ The function returns whether an error occurred. If no error occurred,
+ *CMSE_STUB_CREATED gives the number of SG veneers created by both cmse_scan
+ and this function and HTAB->new_cmse_stub_offset is set to the biggest
+ veneer observed set for new veneers to be layed out after. */
+
+static bfd_boolean
+set_cmse_veneer_addr_from_implib (struct bfd_link_info *info,
+ struct elf32_arm_link_hash_table *htab,
+ int *cmse_stub_created)
+{
+ long symsize;
+ char *sym_name;
+ flagword flags;
+ long i, symcount;
+ bfd *in_implib_bfd;
+ asection *stub_out_sec;
+ bfd_boolean ret = TRUE;
+ Elf_Internal_Sym *intsym;
+ const char *out_sec_name;
+ bfd_size_type cmse_stub_size;
+ asymbol **sympp = NULL, *sym;
+ struct elf32_arm_link_hash_entry *hash;
+ const insn_sequence *cmse_stub_template;
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ int cmse_stub_template_size, new_cmse_stubs_created = *cmse_stub_created;
+ bfd_vma veneer_value, stub_offset, next_cmse_stub_offset;
+ bfd_vma cmse_stub_array_start = (bfd_vma) -1, cmse_stub_sec_vma = 0;
+
+ /* No input secure gateway import library. */
+ if (!htab->in_implib_bfd)
+ return TRUE;
+
+ in_implib_bfd = htab->in_implib_bfd;
+ if (!htab->cmse_implib)
+ {
+ _bfd_error_handler (_("%pB: --in-implib only supported for Secure "
+ "Gateway import libraries"), in_implib_bfd);
+ return FALSE;
+ }
+
+ /* Get symbol table size. */
+ symsize = bfd_get_symtab_upper_bound (in_implib_bfd);
+ if (symsize < 0)
+ return FALSE;
+
+ /* Read in the input secure gateway import library's symbol table. */
+ sympp = (asymbol **) xmalloc (symsize);
+ symcount = bfd_canonicalize_symtab (in_implib_bfd, sympp);
+ if (symcount < 0)
+ {
+ ret = FALSE;
+ goto free_sym_buf;
+ }
+
+ htab->new_cmse_stub_offset = 0;
+ cmse_stub_size =
+ find_stub_size_and_template (arm_stub_cmse_branch_thumb_only,
+ &cmse_stub_template,
+ &cmse_stub_template_size);
+ out_sec_name =
+ arm_dedicated_stub_output_section_name (arm_stub_cmse_branch_thumb_only);
+ stub_out_sec =
+ bfd_get_section_by_name (htab->obfd, out_sec_name);
+ if (stub_out_sec != NULL)
+ cmse_stub_sec_vma = stub_out_sec->vma;
+
+ /* Set addresses of veneers mentionned in input secure gateway import
+ library's symbol table. */
+ for (i = 0; i < symcount; i++)
+ {
+ sym = sympp[i];
+ flags = sym->flags;
+ sym_name = (char *) bfd_asymbol_name (sym);
+ intsym = &((elf_symbol_type *) sym)->internal_elf_sym;
+
+ if (sym->section != bfd_abs_section_ptr
+ || !(flags & (BSF_GLOBAL | BSF_WEAK))
+ || (flags & BSF_FUNCTION) != BSF_FUNCTION
+ || (ARM_GET_SYM_BRANCH_TYPE (intsym->st_target_internal)
+ != ST_BRANCH_TO_THUMB))
+ {
+ _bfd_error_handler (_("%pB: invalid import library entry: `%s'; "
+ "symbol should be absolute, global and "
+ "refer to Thumb functions"),
+ in_implib_bfd, sym_name);
+ ret = FALSE;
+ continue;
+ }
+
+ veneer_value = bfd_asymbol_value (sym);
+ stub_offset = veneer_value - cmse_stub_sec_vma;
+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, sym_name,
+ FALSE, FALSE);
+ hash = (struct elf32_arm_link_hash_entry *)
+ elf_link_hash_lookup (&(htab)->root, sym_name, FALSE, FALSE, TRUE);
+
+ /* Stub entry should have been created by cmse_scan or the symbol be of
+ a secure function callable from non secure code. */
+ if (!stub_entry && !hash)
+ {
+ bfd_boolean new_stub;
+
+ _bfd_error_handler
+ (_("entry function `%s' disappeared from secure code"), sym_name);
+ hash = (struct elf32_arm_link_hash_entry *)
+ elf_link_hash_lookup (&(htab)->root, sym_name, TRUE, TRUE, TRUE);
+ stub_entry
+ = elf32_arm_create_stub (htab, arm_stub_cmse_branch_thumb_only,
+ NULL, NULL, bfd_abs_section_ptr, hash,
+ sym_name, veneer_value,
+ ST_BRANCH_TO_THUMB, &new_stub);
+ if (stub_entry == NULL)
+ ret = FALSE;
+ else
+ {
+ BFD_ASSERT (new_stub);
+ new_cmse_stubs_created++;
+ (*cmse_stub_created)++;
+ }
+ stub_entry->stub_template_size = stub_entry->stub_size = 0;
+ stub_entry->stub_offset = stub_offset;
+ }
+ /* Symbol found is not callable from non secure code. */
+ else if (!stub_entry)
+ {
+ if (!cmse_entry_fct_p (hash))
+ {
+ _bfd_error_handler (_("`%s' refers to a non entry function"),
+ sym_name);
+ ret = FALSE;
+ }
+ continue;
+ }
+ else
+ {
+ /* Only stubs for SG veneers should have been created. */
+ BFD_ASSERT (stub_entry->stub_type == arm_stub_cmse_branch_thumb_only);
+
+ /* Check visibility hasn't changed. */
+ if (!!(flags & BSF_GLOBAL)
+ != (hash->root.root.type == bfd_link_hash_defined))
+ _bfd_error_handler
+ (_("%pB: visibility of symbol `%s' has changed"), in_implib_bfd,
+ sym_name);
+
+ stub_entry->stub_offset = stub_offset;
+ }
+
+ /* Size should match that of a SG veneer. */
+ if (intsym->st_size != cmse_stub_size)
+ {
+ _bfd_error_handler (_("%pB: incorrect size for symbol `%s'"),
+ in_implib_bfd, sym_name);
+ ret = FALSE;
+ }
+
+ /* Previous veneer address is before current SG veneer section. */
+ if (veneer_value < cmse_stub_sec_vma)
+ {
+ /* Avoid offset underflow. */
+ if (stub_entry)
+ stub_entry->stub_offset = 0;
+ stub_offset = 0;
+ ret = FALSE;
+ }
+
+ /* Complain if stub offset not a multiple of stub size. */
+ if (stub_offset % cmse_stub_size)
+ {
+ _bfd_error_handler
+ (_("offset of veneer for entry function `%s' not a multiple of "
+ "its size"), sym_name);
+ ret = FALSE;
+ }
+
+ if (!ret)
+ continue;
+
+ new_cmse_stubs_created--;
+ if (veneer_value < cmse_stub_array_start)
+ cmse_stub_array_start = veneer_value;
+ next_cmse_stub_offset = stub_offset + ((cmse_stub_size + 7) & ~7);
+ if (next_cmse_stub_offset > htab->new_cmse_stub_offset)
+ htab->new_cmse_stub_offset = next_cmse_stub_offset;
+ }
+
+ if (!info->out_implib_bfd && new_cmse_stubs_created != 0)
+ {
+ BFD_ASSERT (new_cmse_stubs_created > 0);
+ _bfd_error_handler
+ (_("new entry function(s) introduced but no output import library "
+ "specified:"));
+ bfd_hash_traverse (&htab->stub_hash_table, arm_list_new_cmse_stub, info);
+ }
+
+ if (cmse_stub_array_start != cmse_stub_sec_vma)
+ {
+ _bfd_error_handler
+ (_("start address of `%s' is different from previous link"),
+ out_sec_name);
+ ret = FALSE;
+ }
+
+free_sym_buf:
+ free (sympp);
+ 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
+ PC-relative calls to a target that is unreachable with a "bl"
+ instruction. */
+
+bfd_boolean
+elf32_arm_size_stubs (bfd *output_bfd,
+ bfd *stub_bfd,
+ struct bfd_link_info *info,
+ bfd_signed_vma group_size,
+ asection * (*add_stub_section) (const char *, asection *,
+ asection *,
+ unsigned int),
+ void (*layout_sections_again) (void))
+{
+ bfd_boolean ret = TRUE;
+ obj_attribute *out_attr;
+ int cmse_stub_created = 0;
+ bfd_size_type stub_group_size;
+ 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;
+ struct a8_erratum_reloc *a8_relocs = NULL;
+ unsigned int num_a8_relocs = 0, a8_reloc_table_size = 10, i;
+
+ if (htab == NULL)
+ return FALSE;
+
+ if (htab->fix_cortex_a8)
+ {
+ a8_fixes = (struct a8_erratum_fix *)
+ bfd_zmalloc (sizeof (struct a8_erratum_fix) * a8_fix_table_size);
+ a8_relocs = (struct a8_erratum_reloc *)
+ bfd_zmalloc (sizeof (struct a8_erratum_reloc) * a8_reloc_table_size);
+ }
+
+ /* Propagate mach to stub bfd, because it may not have been
+ finalized when we created stub_bfd. */
+ bfd_set_arch_mach (stub_bfd, bfd_get_arch (output_bfd),
+ bfd_get_mach (output_bfd));
+
+ /* Stash our params away. */
+ htab->stub_bfd = stub_bfd;
+ htab->add_stub_section = add_stub_section;
+ 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