+ return TRUE;
+}
+
+/* Extract CPU features from an NTBS. */
+
+static unsigned
+arc_extract_features (const char *p)
+{
+ unsigned i, r = 0;
+
+ if (!p)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE (bfd_feature_list); i++)
+ {
+ char *t = strstr (p, bfd_feature_list[i].attr);
+ unsigned l = strlen (bfd_feature_list[i].attr);
+ if ((t != NULL)
+ && (t[l] == ','
+ || t[l] == '\0'))
+ r |= bfd_feature_list[i].feature;
+ }
+
+ return r;
+}
+
+/* Concatenate two strings. s1 can be NULL but not
+ s2. */
+
+static char *
+arc_stralloc (char * s1, const char * s2)
+{
+ char *p;
+
+ /* Only s1 can be null. */
+ BFD_ASSERT (s2);
+
+ p = s1 ? concat (s1, ",", s2, NULL) : (char *)s2;
+
+ return p;
+}
+
+/* Merge ARC object attributes from IBFD into OBFD. Raise an error if
+ there are conflicting attributes. */
+
+static bfd_boolean
+arc_elf_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+ bfd *obfd = info->output_bfd;
+ obj_attribute *in_attr;
+ obj_attribute *out_attr;
+ int i;
+ bfd_boolean result = TRUE;
+ const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
+ char *tagname = NULL;
+
+ /* Skip the linker stubs file. This preserves previous behavior
+ of accepting unknown attributes in the first input file - but
+ is that a bug? */
+ if (ibfd->flags & BFD_LINKER_CREATED)
+ return TRUE;
+
+ /* Skip any input that hasn't attribute section.
+ This enables to link object files without attribute section with
+ any others. */
+ if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
+ return TRUE;
+
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+ out_attr = elf_known_obj_attributes_proc (obfd);
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ out_attr[0].i = 1;
+
+ return TRUE;
+ }
+
+ in_attr = elf_known_obj_attributes_proc (ibfd);
+ out_attr = elf_known_obj_attributes_proc (obfd);
+
+ for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
+ {
+ /* Merge this attribute with existing attributes. */
+ switch (i)
+ {
+ case Tag_ARC_PCS_config:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (in_attr[i].i != 0 && out_attr[i].i != in_attr[i].i)
+ {
+ const char *tagval[] = { "Absent", "Bare-metal/mwdt",
+ "Bare-metal/newlib", "Linux/uclibc",
+ "Linux/glibc" };
+ BFD_ASSERT (in_attr[i].i < 5);
+ BFD_ASSERT (out_attr[i].i < 5);
+ /* It's sometimes ok to mix different configs, so this is only
+ a warning. */
+ _bfd_error_handler
+ (_("warning: %pB: conflicting platform configuration "
+ "%s with %s"), ibfd,
+ tagval[in_attr[i].i],
+ tagval[out_attr[i].i]);
+ }
+ break;
+
+ case Tag_ARC_CPU_base:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (in_attr[i].i != 0 && out_attr[i].i != in_attr[i].i
+ && ((out_attr[i].i + in_attr[i].i) < 6))
+ {
+ const char *tagval[] = { "Absent", "ARC6xx", "ARC7xx",
+ "ARCEM", "ARCHS" };
+ BFD_ASSERT (in_attr[i].i < 5);
+ BFD_ASSERT (out_attr[i].i < 5);
+ /* We cannot mix code for different CPUs. */
+ _bfd_error_handler
+ (_("error: %pB: unable to merge CPU base attributes "
+ "%s with %s"),
+ obfd,
+ tagval[in_attr[i].i],
+ tagval[out_attr[i].i]);
+ result = FALSE;
+ break;
+ }
+ else
+ {
+ /* The CPUs may be different, check if we can still mix
+ the objects against the output choosen CPU. */
+ unsigned in_feature = 0;
+ unsigned out_feature = 0;
+ char *p1 = in_attr[Tag_ARC_ISA_config].s;
+ char *p2 = out_attr[Tag_ARC_ISA_config].s;
+ unsigned j;
+ unsigned cpu_out;
+ unsigned opcode_map[] = {0, ARC_OPCODE_ARC600, ARC_OPCODE_ARC700,
+ ARC_OPCODE_ARCv2EM, ARC_OPCODE_ARCv2HS};
+
+ BFD_ASSERT (in_attr[i].i < (sizeof (opcode_map)
+ / sizeof (unsigned)));
+ BFD_ASSERT (out_attr[i].i < (sizeof (opcode_map)
+ / sizeof (unsigned)));
+ cpu_out = opcode_map[out_attr[i].i];
+
+ in_feature = arc_extract_features (p1);
+ out_feature = arc_extract_features (p2);
+
+ /* First, check if a feature is compatible with the
+ output object chosen CPU. */
+ for (j = 0; j < ARRAY_SIZE (bfd_feature_list); j++)
+ if (((in_feature | out_feature) & bfd_feature_list[j].feature)
+ && (!(cpu_out & bfd_feature_list[j].cpus)))
+ {
+ _bfd_error_handler
+ (_("error: %pB: unable to merge ISA extension attributes "
+ "%s"),
+ obfd, bfd_feature_list[j].name);
+ result = FALSE;
+ break;
+ }
+ /* Second, if we have compatible features with the
+ chosen CPU, check if they are compatible among
+ them. */
+ for (j = 0; j < ARRAY_SIZE (bfd_conflict_list); j++)
+ if (((in_feature | out_feature) & bfd_conflict_list[j])
+ == bfd_conflict_list[j])
+ {
+ unsigned k;
+ for (k = 0; k < ARRAY_SIZE (bfd_feature_list); k++)
+ {
+ if (in_feature & bfd_feature_list[k].feature
+ & bfd_conflict_list[j])
+ p1 = (char *) bfd_feature_list[k].name;
+ if (out_feature & bfd_feature_list[k].feature
+ & bfd_conflict_list[j])
+ p2 = (char *) bfd_feature_list[k].name;
+ }
+ _bfd_error_handler
+ (_("error: %pB: conflicting ISA extension attributes "
+ "%s with %s"),
+ obfd, p1, p2);
+ result = FALSE;
+ break;
+ }
+ /* Everithing is alright. */
+ out_feature |= in_feature;
+ p1 = NULL;
+ for (j = 0; j < ARRAY_SIZE (bfd_feature_list); j++)
+ if (out_feature & bfd_feature_list[j].feature)
+ p1 = arc_stralloc (p1, bfd_feature_list[j].attr);
+ if (p1)
+ out_attr[Tag_ARC_ISA_config].s =
+ _bfd_elf_attr_strdup (obfd, p1);
+ }
+ /* Fall through. */
+ case Tag_ARC_CPU_variation:
+ case Tag_ARC_ISA_mpy_option:
+ case Tag_ARC_ABI_osver:
+ /* Use the largest value specified. */
+ if (in_attr[i].i > out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+
+ /* The CPU name is given by the vendor, just choose an
+ existing one if missing or different. There are no fail
+ criteria if they different or both missing. */
+ case Tag_ARC_CPU_name:
+ if (!out_attr[i].s && in_attr[i].s)
+ out_attr[i].s = _bfd_elf_attr_strdup (obfd, in_attr[i].s);
+ break;
+
+ case Tag_ARC_ABI_rf16:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (out_attr[i].i != in_attr[i].i)
+ {
+ /* We cannot mix code with rf16 and without. */
+ _bfd_error_handler
+ (_("error: %pB: cannot mix rf16 with full register set %pB"),
+ obfd, ibfd);
+ result = FALSE;
+ }
+ break;
+
+ case Tag_ARC_ABI_pic:
+ tagname = "PIC";
+ /* fall through */
+ case Tag_ARC_ABI_sda:
+ if (!tagname)
+ tagname = "SDA";
+ /* fall through */
+ case Tag_ARC_ABI_tls:
+ {
+ const char *tagval[] = { "Absent", "MWDT", "GNU" };
+
+ if (!tagname)
+ tagname = "TLS";
+
+ BFD_ASSERT (in_attr[i].i < 3);
+ BFD_ASSERT (out_attr[i].i < 3);
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (out_attr[i].i != 0 && in_attr[i].i != 0
+ && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("error: %pB: conflicting attributes %s: %s with %s"),
+ obfd, tagname,
+ tagval[in_attr[i].i],
+ tagval[out_attr[i].i]);
+ result = FALSE;
+ }
+ tagname = NULL;
+ break;
+ }
+
+ case Tag_ARC_ABI_double_size:
+ tagname = "Double size";
+ /* fall through */
+ case Tag_ARC_ABI_enumsize:
+ if (!tagname)
+ tagname = "Enum size";
+ /* fall through */
+ case Tag_ARC_ABI_exceptions:
+ if (!tagname)
+ tagname = "ABI exceptions";
+
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (out_attr[i].i != 0 && in_attr[i].i != 0
+ && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("error: %pB: conflicting attributes %s"),
+ obfd, tagname);
+ result = FALSE;
+ }
+ break;
+
+ case Tag_ARC_ISA_apex:
+ break; /* Do nothing for APEX attributes. */
+
+ case Tag_ARC_ISA_config:
+ /* It is handled in Tag_ARC_CPU_base. */
+ break;
+
+ case Tag_ARC_ATR_version:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ break;
+
+ default:
+ result
+ = result && _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
+ }
+
+ /* If out_attr was copied from in_attr then it won't have a type yet. */
+ if (in_attr[i].type && !out_attr[i].type)
+ out_attr[i].type = in_attr[i].type;
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ if (!_bfd_elf_merge_object_attributes (ibfd, info))
+ return FALSE;
+
+ /* Check for any attributes not known on ARC. */
+ result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
+
+ return result;