PR20744, Incorrect PowerPC VLE relocs
[external/binutils.git] / gas / config / tc-ppc.c
index fc2a045..84d4ebc 100644 (file)
@@ -133,6 +133,7 @@ static void ppc_elf_rdata (int);
 static void ppc_elf_lcomm (int);
 static void ppc_elf_localentry (int);
 static void ppc_elf_abiversion (int);
+static void ppc_elf_gnu_attribute (int);
 #endif
 
 #ifdef TE_PE
@@ -270,6 +271,7 @@ const pseudo_typeS md_pseudo_table[] =
   { "lcomm",   ppc_elf_lcomm,  0 },
   { "localentry", ppc_elf_localentry,  0 },
   { "abiversion", ppc_elf_abiversion,  0 },
+  { "gnu_attribute", ppc_elf_gnu_attribute, 0},
 #endif
 
 #ifdef TE_PE
@@ -2314,6 +2316,28 @@ ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Parse a .gnu_attribute directive.  */
+static void
+ppc_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  int tag = obj_elf_vendor_attribute (OBJ_ATTR_GNU);
+
+  /* Check validity of defined powerpc tags.  */
+  if (tag == Tag_GNU_Power_ABI_FP
+      || tag == Tag_GNU_Power_ABI_Vector
+      || tag == Tag_GNU_Power_ABI_Struct_Return)
+    {
+      unsigned int val;
+
+      val = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, tag);
+
+      if ((tag == Tag_GNU_Power_ABI_FP && val > 15)
+         || (tag == Tag_GNU_Power_ABI_Vector && val > 3)
+         || (tag == Tag_GNU_Power_ABI_Struct_Return && val > 2))
+       as_warn (_("unknown .gnu_attribute value"));
+    }
+}
+
 /* Set ABI version in output file.  */
 void
 ppc_elf_end (void)
@@ -2593,22 +2617,6 @@ struct ppc_fixup
 
 #define MAX_INSN_FIXUPS (5)
 
-/* Form I16L.  */
-#define E_OR2I_INSN            0x7000C000
-#define E_AND2I_DOT_INSN       0x7000C800
-#define E_OR2IS_INSN           0x7000D000
-#define E_LIS_INSN             0x7000E000
-#define        E_AND2IS_DOT_INSN       0x7000E800
-
-/* Form I16A.  */
-#define E_ADD2I_DOT_INSN       0x70008800
-#define E_ADD2IS_INSN          0x70009000
-#define E_CMP16I_INSN          0x70009800
-#define E_MULL2I_INSN          0x7000A000
-#define E_CMPL16I_INSN         0x7000A800
-#define E_CMPH16I_INSN         0x7000B000
-#define E_CMPHL16I_INSN                0x7000B800
-
 /* This routine is called for each instruction to be assembled.  */
 
 void
@@ -2671,7 +2679,8 @@ md_assemble (char *str)
       const struct powerpc_operand *operand;
 
       operand = &powerpc_operands[*opindex_ptr];
-      if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
+      if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+         && !((operand->flags & PPC_OPERAND_OPTIONAL32) != 0 && ppc_obj64))
        {
          unsigned int opcount;
          unsigned int num_operands_expected;
@@ -2741,6 +2750,7 @@ md_assemble (char *str)
       /* If this is an optional operand, and we are skipping it, just
         insert a zero.  */
       if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+         && !((operand->flags & PPC_OPERAND_OPTIONAL32) != 0 && ppc_obj64)
          && skip_optional)
        {
          long val = ppc_optional_operand_value (operand);
@@ -3087,14 +3097,14 @@ md_assemble (char *str)
                {
                  int tmp_insn = insn & opcode->mask;
 
-                 int use_d_reloc = (tmp_insn == E_OR2I_INSN
+                 int use_a_reloc = (tmp_insn == E_OR2I_INSN
                                     || tmp_insn == E_AND2I_DOT_INSN
                                     || tmp_insn == E_OR2IS_INSN
                                     || tmp_insn == E_LIS_INSN
                                     || tmp_insn == E_AND2IS_DOT_INSN);
 
 
-                 int use_a_reloc = (tmp_insn == E_ADD2I_DOT_INSN
+                 int use_d_reloc = (tmp_insn == E_ADD2I_DOT_INSN
                                     || tmp_insn == E_ADD2IS_INSN
                                     || tmp_insn == E_CMP16I_INSN
                                     || tmp_insn == E_MULL2I_INSN
@@ -3377,13 +3387,17 @@ md_assemble (char *str)
          however it'll remain clear for dual-mode instructions on
          dual-mode and, more importantly, standard-mode processors.  */
       if ((ppc_cpu & opcode->flags) == PPC_OPCODE_VLE)
-       ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
+       {
+         ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
+         if (elf_section_data (now_seg) != NULL)
+           elf_section_data (now_seg)->this_hdr.sh_flags |= SHF_PPC_VLE;
+       }
     }
 #endif
 
   /* Write out the instruction.  */
   /* Differentiate between two and four byte insns.  */
-  if (ppc_mach () == bfd_mach_ppc_vle)
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
     {
       if (PPC_OP_SE_VLE (insn))
         insn_length = 2;
@@ -3400,7 +3414,7 @@ md_assemble (char *str)
   f = frag_more (insn_length);
   if (frag_now->has_code && frag_now->insn_addr != addr_mod)
     {
-      if (ppc_mach() == bfd_mach_ppc_vle)
+      if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
         as_bad (_("instruction address is not a multiple of 2"));
       else
         as_bad (_("instruction address is not a multiple of 4"));
@@ -6346,7 +6360,7 @@ ppc_frag_check (struct frag *fragP)
   if (!fragP->has_code)
     return;
 
-  if (ppc_mach() == bfd_mach_ppc_vle)
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
     {
       if (((fragP->fr_address + fragP->insn_addr) & 1) != 0)
         as_bad (_("instruction address is not a multiple of 2"));
@@ -6367,7 +6381,7 @@ ppc_handle_align (struct frag *fragP)
   valueT count = (fragP->fr_next->fr_address
                  - (fragP->fr_address + fragP->fr_fix));
 
-  if (ppc_mach() == bfd_mach_ppc_vle && count != 0 && (count & 1) == 0)
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0 && count != 0 && (count & 1) == 0)
     {
       char *dest = fragP->fr_literal + fragP->fr_fix;