2011-04-07 Paul Brook <paul@codesourcery.com>
authorPaul Brook <paul@codesourcery.com>
Thu, 7 Apr 2011 11:40:12 +0000 (11:40 +0000)
committerPaul Brook <paul@codesourcery.com>
Thu, 7 Apr 2011 11:40:12 +0000 (11:40 +0000)
binutils/
* readelf.c (arm_section_get_word): Handle C6000 relocations.
(decode_tic6x_unwind_regmask, decode_arm_unwind_bytecode,
decode_tic6x_unwind_bytecode, expand_prel31): New functions.
(decode_arm_unwind): Split out common code from ARM specific bits.
(dump_arm_unwind): Use expand_prel31.
(arm_process_unwind): Handle SHT_C6000_UNWIND sections.
(process_unwind): Add SHT_C6000_UNWIND.

binutils/ChangeLog
binutils/readelf.c

index 2297df868e044c277cfe6420a24245e515015bb4..f2315f5299091de0a855dda9414bcde455f27ee4 100644 (file)
@@ -1,3 +1,13 @@
+2011-04-07  Paul Brook  <paul@codesourcery.com>
+
+       * readelf.c (arm_section_get_word): Handle C6000 relocations.
+       (decode_tic6x_unwind_regmask, decode_arm_unwind_bytecode,
+       decode_tic6x_unwind_bytecode, expand_prel31): New functions.
+       (decode_arm_unwind): Split out common code from ARM specific bits.
+       (dump_arm_unwind): Use expand_prel31.
+       (arm_process_unwind): Handle SHT_C6000_UNWIND sections.
+       (process_unwind): Add SHT_C6000_UNWIND.
+
 2011-04-06  Joseph Myers  <joseph@codesourcery.com>
 
        * configure.in (thumb-*-pe*): Remove.
index c975189241961a3784a328df4cedddb595a1c346..7ac9914ada23060872f6d46f593a6bd4d8ae4259 100644 (file)
@@ -6311,12 +6311,26 @@ arm_section_get_word (struct arm_unw_aux_info *aux,
       if (rp->r_offset < word_offset)
        continue;
 
-      relname = elf_arm_reloc_type (ELF32_R_TYPE (rp->r_info));
+      switch (elf_header.e_machine)
+       {
+       case EM_ARM:
+         relname = elf_arm_reloc_type (ELF32_R_TYPE (rp->r_info));
+         break;
+
+       case EM_TI_C6000:
+         relname = elf_tic6x_reloc_type (ELF32_R_TYPE (rp->r_info));
+         break;
+
+       default:
+           abort();
+       }
 
-      if (streq (relname, "R_ARM_NONE"))
+      if (streq (relname, "R_ARM_NONE")
+         || streq (relname, "R_C6000_NONE"))
        continue;
 
-      if (! streq (relname, "R_ARM_PREL31"))
+      if (!(streq (relname, "R_ARM_PREL31")
+           || streq (relname, "R_C6000_PREL31")))
        {
          warn (_("Skipping unexpected relocation type %s\n"), relname);
          continue;
@@ -6336,6 +6350,9 @@ arm_section_get_word (struct arm_unw_aux_info *aux,
       offset += sym->st_value;
       prelval = offset - (arm_sec->sec->sh_addr + rp->r_offset);
 
+      if (streq (relname, "R_C6000_PREL31"))
+       prelval >>= 1;
+
       word = (word & ~ (bfd_vma) 0x7fffffff) | (prelval & 0x7fffffff);
       addr->section = sym->st_shndx;
       addr->offset = offset;
@@ -6348,15 +6365,26 @@ arm_section_get_word (struct arm_unw_aux_info *aux,
   return 1;
 }
 
+static const char *tic6x_unwind_regnames[16] = {
+    "A15", "B15", "B14", "B13", "B12", "B11", "B10", "B3", 
+    "A14", "A13", "A12", "A11", "A10", 
+    "[invalid reg 13]", "[invalid reg 14]", "[invalid reg 15]"};
+
 static void
-decode_arm_unwind (struct arm_unw_aux_info *aux,
-                  unsigned int word, unsigned int remaining,
-                  bfd_vma data_offset, Elf_Internal_Shdr *data_sec,
-                  struct arm_section *data_arm_sec)
+decode_tic6x_unwind_regmask (unsigned int mask)
 {
-  int per_index;
-  unsigned int more_words;
-  struct absaddr addr;
+  int i;
+
+  for (i = 12; mask; mask >>= 1, i--)
+    {
+      if (mask & 1)
+       {
+         fputs (tic6x_unwind_regnames[i], stdout);
+         if (mask > 1)
+           fputs (", ", stdout);
+       }
+    }
+}
 
 #define ADVANCE                                                        \
   if (remaining == 0 && more_words)                            \
@@ -6384,77 +6412,14 @@ decode_arm_unwind (struct arm_unw_aux_info *aux,
     }                                  \
   printf ("0x%02x ", OP)
 
-  if (remaining == 0)
-    {
-      /* Fetch the first word.  */
-      if (!arm_section_get_word (aux, data_arm_sec, data_sec, data_offset,
-                                &word, &addr))
-       return;
-      remaining = 4;
-    }
-
-  if ((word & 0x80000000) == 0)
-    {
-      /* Expand prel31 for personality routine.  */
-      bfd_vma fn;
-      const char *procname;
-
-      fn = word;
-      if (fn & 0x40000000)
-       fn |= ~ (bfd_vma) 0x7fffffff;
-      fn = fn + data_sec->sh_addr + data_offset;
-
-      printf (_("  Personality routine: "));
-      procname = arm_print_vma_and_name (aux, fn, addr);
-      fputc ('\n', stdout);
-
-      /* The GCC personality routines use the standard compact
-        encoding, starting with one byte giving the number of
-        words.  */
-      if (procname != NULL
-         && (const_strneq (procname, "__gcc_personality_v0")
-             || const_strneq (procname, "__gxx_personality_v0")
-             || const_strneq (procname, "__gcj_personality_v0")
-             || const_strneq (procname, "__gnu_objc_personality_v0")))
-       {
-         remaining = 0;
-         more_words = 1;
-         ADVANCE;
-         if (!remaining)
-           {
-             printf (_("  [Truncated data]\n"));
-             return;
-           }
-         more_words = word >> 24;
-         word <<= 8;
-         remaining--;
-       }
-      else
-       return;
-    }
-  else
-    {
-      per_index = (word >> 24) & 0x7f;
-      if (per_index != 0 && per_index != 1 && per_index != 2)
-       {
-         printf (_("  [reserved compact index %d]\n"), per_index);
-         return;
-       }
-
-      printf (_("  Compact model %d\n"), per_index);
-      if (per_index == 0)
-       {
-         more_words = 0;
-         word <<= 8;
-         remaining--;
-       }
-      else
-       {
-         more_words = (word >> 16) & 0xff;
-         word <<= 16;
-         remaining -= 2;
-       }
-    }
+static void
+decode_arm_unwind_bytecode (struct arm_unw_aux_info *aux,
+                           unsigned int word, unsigned int remaining,
+                           unsigned int more_words,
+                           bfd_vma data_offset, Elf_Internal_Shdr *data_sec,
+                           struct arm_section *data_arm_sec)
+{
+  struct absaddr addr;
 
   /* Decode the unwinding instructions.  */
   while (1)
@@ -6652,6 +6617,271 @@ decode_arm_unwind (struct arm_unw_aux_info *aux,
        printf (_("     [unsupported opcode]"));
       printf ("\n");
     }
+}
+
+static void
+decode_tic6x_unwind_bytecode (struct arm_unw_aux_info *aux,
+                           unsigned int word, unsigned int remaining,
+                           unsigned int more_words,
+                           bfd_vma data_offset, Elf_Internal_Shdr *data_sec,
+                           struct arm_section *data_arm_sec)
+{
+  struct absaddr addr;
+
+  /* Decode the unwinding instructions.  */
+  while (1)
+    {
+      unsigned int op, op2;
+
+      ADVANCE;
+      if (remaining == 0)
+       break;
+      remaining--;
+      op = word >> 24;
+      word <<= 8;
+
+      printf (_("  0x%02x "), op);
+
+      if ((op & 0xc0) == 0x00)
+       {
+         int offset = ((op & 0x3f) << 3) + 8;
+         printf (_("     sp = sp + %d"), offset);
+       }
+      else if ((op & 0xc0) == 0x80)
+       {
+         GET_OP (op2);
+         if (op == 0x80 && op2 == 0)
+           printf (_("Refuse to unwind"));
+         else
+           {
+             unsigned int mask = ((op & 0x1f) << 8) | op2;
+             if (op & 0x20)
+               printf ("pop compact {");
+             else
+               printf ("pop {");
+
+             decode_tic6x_unwind_regmask (mask);
+             printf("}");
+           }
+       }
+      else if ((op & 0xf0) == 0xc0)
+       {
+         unsigned int reg;
+         unsigned int nregs;
+         unsigned int i;
+         const char *name;
+         struct {
+             unsigned int offset;
+             unsigned int reg;
+         } regpos[16];
+
+         /* Scan entire instruction first so that GET_OP output is not
+            interleaved with disassembly.  */
+         nregs = 0;
+         for (i = 0; nregs < (op & 0xf); i++)
+           {
+             GET_OP (op2);
+             reg = op2 >> 4;
+             if (reg != 0xf)
+               {
+                 regpos[nregs].offset = i * 2;
+                 regpos[nregs].reg = reg;
+                 nregs++;
+               }
+
+             reg = op2 & 0xf;
+             if (reg != 0xf)
+               {
+                 regpos[nregs].offset = i * 2 + 1;
+                 regpos[nregs].reg = reg;
+                 nregs++;
+               }
+           }
+
+         printf (_("pop frame {"));
+         reg = nregs - 1;
+         for (i = i * 2; i > 0; i--)
+           {
+             if (regpos[reg].offset == i - 1)
+               {
+                 name = tic6x_unwind_regnames[regpos[reg].reg];
+                 if (reg > 0)
+                   reg--;
+               }
+             else
+               name = _("[pad]");
+
+             fputs (name, stdout);
+             if (i > 1)
+               printf (", ");
+           }
+
+         printf ("}");
+       }
+      else if (op == 0xd0)
+       printf ("     MOV FP, SP");
+      else if (op == 0xd1)
+       printf ("     __c6xabi_pop_rts");
+      else if (op == 0xd2)
+       {
+         unsigned char buf[9];
+         unsigned int i, len;
+         unsigned long offset;
+         for (i = 0; i < sizeof (buf); i++)
+           {
+             GET_OP (buf[i]);
+             if ((buf[i] & 0x80) == 0)
+               break;
+           }
+         assert (i < sizeof (buf));
+         offset = read_uleb128 (buf, &len);
+         assert (len == i + 1);
+         offset = offset * 8 + 0x408;
+         printf (_("sp = sp + %ld"), offset);
+       }
+      else if ((op & 0xf0) == 0xe0)
+       {
+         if ((op & 0x0f) == 7)
+           printf ("     RETURN");
+         else
+           printf ("     MV %s, B3", tic6x_unwind_regnames[op & 0x0f]);
+       }
+      else
+       {
+         printf (_("     [unsupported opcode]"));
+       }
+      putchar ('\n');
+    }
+}
+
+static bfd_vma
+expand_prel31 (bfd_vma word, bfd_vma where)
+{
+  bfd_vma offset;
+
+  offset = word & 0x7fffffff;
+  if (offset & 0x40000000)
+    offset |= ~ (bfd_vma) 0x7fffffff;
+
+  if (elf_header.e_machine == EM_TI_C6000)
+    offset <<= 1;
+
+  return offset + where;
+}
+
+static void
+decode_arm_unwind (struct arm_unw_aux_info *aux,
+                  unsigned int word, unsigned int remaining,
+                  bfd_vma data_offset, Elf_Internal_Shdr *data_sec,
+                  struct arm_section *data_arm_sec)
+{
+  int per_index;
+  unsigned int more_words = 0;
+  struct absaddr addr;
+
+  if (remaining == 0)
+    {
+      /* Fetch the first word.  */
+      if (!arm_section_get_word (aux, data_arm_sec, data_sec, data_offset,
+                                &word, &addr))
+       return;
+      remaining = 4;
+    }
+
+  if ((word & 0x80000000) == 0)
+    {
+      /* Expand prel31 for personality routine.  */
+      bfd_vma fn;
+      const char *procname;
+
+      fn = expand_prel31 (word, data_sec->sh_addr + data_offset);
+      printf (_("  Personality routine: "));
+      procname = arm_print_vma_and_name (aux, fn, addr);
+      fputc ('\n', stdout);
+
+      /* The GCC personality routines use the standard compact
+        encoding, starting with one byte giving the number of
+        words.  */
+      if (procname != NULL
+         && (const_strneq (procname, "__gcc_personality_v0")
+             || const_strneq (procname, "__gxx_personality_v0")
+             || const_strneq (procname, "__gcj_personality_v0")
+             || const_strneq (procname, "__gnu_objc_personality_v0")))
+       {
+         remaining = 0;
+         more_words = 1;
+         ADVANCE;
+         if (!remaining)
+           {
+             printf (_("  [Truncated data]\n"));
+             return;
+           }
+         more_words = word >> 24;
+         word <<= 8;
+         remaining--;
+         per_index = -1;
+       }
+      else
+       return;
+    }
+  else
+    {
+      
+      per_index = (word >> 24) & 0x7f;
+      printf (_("  Compact model %d\n"), per_index);
+      if (per_index == 0)
+       {
+         more_words = 0;
+         word <<= 8;
+         remaining--;
+       }
+      else if (per_index < 3)
+       {
+         more_words = (word >> 16) & 0xff;
+         word <<= 16;
+         remaining -= 2;
+       }
+    }
+
+  switch (elf_header.e_machine)
+    {
+    case EM_ARM:
+      if (per_index < 3)
+       {
+         decode_arm_unwind_bytecode (aux, word, remaining, more_words,
+                                     data_offset, data_sec, data_arm_sec);
+       }
+      else
+       printf ("  [reserved]\n");
+      break;
+
+    case EM_TI_C6000:
+      if (per_index < 3)
+       {
+         decode_tic6x_unwind_bytecode (aux, word, remaining, more_words,
+                                     data_offset, data_sec, data_arm_sec);
+       }
+      else if (per_index < 5)
+       {
+         if (((word >> 17) & 0x7f) == 0x7f)
+           printf (_("  Restore stack from frame pointer\n"));
+         else
+           printf (_("  Stack increment %d\n"), (word >> 14) & 0x1fc);
+         printf (_("  Registers restored: "));
+         if (per_index == 4)
+           printf (" (compact) ");
+         decode_tic6x_unwind_regmask ((word >> 4) & 0x1fff);
+         putchar ('\n');
+         printf (_("  Return register: %s\n"),
+                 tic6x_unwind_regnames[word & 0xf]);
+       }
+      else
+       printf ("  [reserved]\n");
+      break;
+
+    default:
+      abort ();
+    }
 
   /* Decode the descriptors.  Not implemented.  */
 }
@@ -6684,10 +6914,7 @@ dump_arm_unwind (struct arm_unw_aux_info *aux, Elf_Internal_Shdr *exidx_sec)
          return;
        }
 
-      fn = exidx_fn & 0x7fffffff;
-      if (fn & 0x40000000)
-       fn |= ~ (bfd_vma) 0x7fffffff;
-      fn = fn + exidx_sec->sh_addr + 8 * i;
+      fn = expand_prel31 (exidx_fn, exidx_sec->sh_addr + 8 * i);
 
       arm_print_vma_and_name (aux, fn, entry_addr);
       fputs (": ", stdout);
@@ -6709,10 +6936,7 @@ dump_arm_unwind (struct arm_unw_aux_info *aux, Elf_Internal_Shdr *exidx_sec)
          Elf_Internal_Shdr *table_sec;
 
          fputs ("@", stdout);
-         table = exidx_entry;
-         if (table & 0x40000000)
-           table |= ~ (bfd_vma) 0x7fffffff;
-         table = table + exidx_sec->sh_addr + 8 * i + 4;
+         table = expand_prel31 (exidx_entry, exidx_sec->sh_addr + 8 * i + 4);
          print_vma (table, PREFIX_HEX);
          printf ("\n");
 
@@ -6746,6 +6970,7 @@ dump_arm_unwind (struct arm_unw_aux_info *aux, Elf_Internal_Shdr *exidx_sec)
   arm_free_section (&extab_arm_sec);
 }
 
+/* Used for both ARM and C6X unwinding tables.  */
 static int
 arm_process_unwind (FILE *file)
 {
@@ -6754,10 +6979,25 @@ arm_process_unwind (FILE *file)
   Elf_Internal_Shdr *strsec;
   Elf_Internal_Shdr *sec;
   unsigned long i;
+  unsigned int sec_type;
 
   memset (& aux, 0, sizeof (aux));
   aux.file = file;
 
+  switch (elf_header.e_machine)
+    {
+    case EM_ARM:
+      sec_type = SHT_ARM_EXIDX;
+      break;
+
+    case EM_TI_C6000:
+      sec_type = SHT_C6000_UNWIND;
+      break;
+
+    default:
+       abort();
+    }
+
   if (string_table == NULL)
     return 1;
 
@@ -6773,7 +7013,7 @@ arm_process_unwind (FILE *file)
                                 1, strsec->sh_size, _("string table"));
          aux.strtab_size = aux.strtab != NULL ? strsec->sh_size : 0;
        }
-      else if (sec->sh_type == SHT_ARM_EXIDX)
+      else if (sec->sh_type == sec_type)
        unwsec = sec;
     }
 
@@ -6782,7 +7022,7 @@ arm_process_unwind (FILE *file)
 
   for (i = 0, sec = section_headers; i < elf_header.e_shnum; ++i, ++sec)
     {
-      if (sec->sh_type == SHT_ARM_EXIDX)
+      if (sec->sh_type == sec_type)
        {
          printf (_("\nUnwind table index '%s' at offset 0x%lx contains %lu entries:\n"),
                  SECTION_NAME (sec),
@@ -6813,6 +7053,7 @@ process_unwind (FILE * file)
     { EM_ARM, arm_process_unwind },
     { EM_IA_64, ia64_process_unwind },
     { EM_PARISC, hppa_process_unwind },
+    { EM_TI_C6000, arm_process_unwind },
     { 0, 0 }
   };
   int i;