[PATCH v2 4/6] Readelf for nanoMIPS.

"Tsing" <[email protected]> Fri, 24 Jul 2026 18:30:56 +0800
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
From: tsing <[email protected]>

binutils/
	* readelf.c (guess_is_rela): Add EM_NANOMIPS.
		    (get_machine_name): Likewise.
		    (dump_relocations): Add support for nanomips relocations.
		    (get_machine_flags): Add support for nanomips flags.
		    (is_32bit_abs_reloc): Add R_NANOMIPS_32.
		    (is_32bit_pcrel_reloc): Add R_NANOMIPS_PC32.
		    (is_64bit_abs_reloc): Add R_NANOMIPS_64.
		    (is_16bit_abs_reloc): Add R_NANOMIPS_UNSIGNED_16.
		    (is_none_reloc): Add R_NANOMIPS_NONE.
---
 binutils/readelf.c | 422 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 422 insertions(+)

diff --git a/binutils/readelf.c b/binutils/readelf.c
index a52fe2b8d63..ec9f5ebb484 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -141,6 +141,7 @@
 #include "elf/moxie.h"
 #include "elf/mt.h"
 #include "elf/msp430.h"
+#include "elf/nanomips.h"
 #include "elf/nds32.h"
 #include "elf/nfp.h"
 #include "elf/nios2.h"
@@ -1319,6 +1320,7 @@ guess_is_rela (unsigned int e_machine)
     case EM_MSP430:
     case EM_MSP430_OLD:
     case EM_MT:
+    case EM_NANOMIPS:
     case EM_NDS32:
     case EM_NIOS32:
     case EM_OR1K:
@@ -2687,6 +2689,10 @@ dump_relocations (Filedata *          filedata,
 	case EM_AMDGPU:
 	  rtype = elf_amdgpu_reloc_type (type);
 	  break;
+
+	case EM_NANOMIPS:
+	  rtype = elf_nanomips_reloc_type (type);
+	  break;
 	}
 
       char *symbol_name = NULL;
@@ -3812,6 +3818,7 @@ get_machine_name (unsigned e_machine)
     case EM_CEVA:		return "CEVA Processor Architecture Family";
     case EM_CEVA_X2:		return "CEVA X2 Processor Family";
     case EM_BPF:		return "Linux BPF";
+    case EM_NANOMIPS:		return "nanoMIPS";
     case EM_GRAPHCORE_IPU:	return "Graphcore Intelligent Processing Unit";
     case EM_IMG1:		return "Imagination Technologies";
       /* 250 */
@@ -5625,6 +5632,38 @@ get_machine_flags (Filedata * filedata, unsigned e_flags, unsigned e_machine)
 	  out = decode_MSP430_machine_flags (out, e_flags);
 	  break;
 
+	case EM_NANOMIPS:
+	  if (e_flags & EF_NANOMIPS_PIC)
+	    strcat (buf, ", pic");
+
+	  if (e_flags & EF_NANOMIPS_32BITMODE)
+	    strcat (buf, ", 32bitmode");
+
+	  switch ((e_flags & EF_NANOMIPS_MACH))
+	    {
+	    default: strcat (buf, _(", unknown CPU")); break;
+	    }
+
+	  switch ((e_flags & EF_NANOMIPS_ABI))
+	    {
+	    case E_NANOMIPS_ABI_P32: strcat (buf, ", p32"); break;
+	    case E_NANOMIPS_ABI_P64: strcat (buf, ", p64"); break;
+	    case 0:
+	      /* We simply ignore the field in this case to avoid confusion:
+		 MIPS ELF does not specify EF_MIPS_ABI, it is a GNU extension.
+		 This means it is likely to be an o32 file, but not for
+		 sure.  */
+	      break;
+	    default: strcat (buf, _(", unknown ABI")); break;
+	    }
+
+	  switch ((e_flags & EF_NANOMIPS_ARCH))
+	    {
+	    case E_NANOMIPS_ARCH_32R6: strcat (buf, ", nanomips32r6"); break;
+	    case E_NANOMIPS_ARCH_64R6: strcat (buf, ", nanomips64r6"); break;
+	    default: strcat (buf, _(", unknown ISA")); break;
+	    }
+
 	case EM_Z80:
 	  out = decode_Z80_machine_flags (out, e_flags);
 	  break;
@@ -16077,6 +16116,35 @@ target_specific_reloc_handling (Filedata *filedata,
 	  }
 	break;
       }
+      case EM_NANOMIPS:
+	{
+	  static Elf_Internal_Sym * saved_sym = NULL;
+
+	  if (reloc == NULL)
+	    {
+	      saved_sym = NULL;
+	      return true;
+	    }
+
+	  if (reloc_type == 3)
+	    {
+	      saved_sym = symtab + sym_index;
+	      bfd_vma value;
+
+	      if (sym_index >= num_syms)
+		error (_("nanomips reloc contains invalid symbol index %lu\n"),
+		       sym_index);
+	      else
+		{
+		  value = reloc->r_addend - saved_sym->st_value;
+		  reloc++;
+		  reloc->r_addend += value;
+		  return true;
+		}
+	      break;
+	    }
+	  break;
+	}
     }
 
   return false;
@@ -16210,6 +16278,8 @@ is_32bit_abs_reloc (Filedata * filedata, unsigned int reloc_type)
       return reloc_type == 1; /* R_MSP430_32 or R_MSP320_ABS32.  */
     case EM_MT:
       return reloc_type == 2; /* R_MT_32.  */
+    case EM_NANOMIPS:
+      return reloc_type == 1; /* R_NANOMIPS_32.  */
     case EM_NDS32:
       return reloc_type == 20; /* R_NDS32_32_RELA.  */
     case EM_ALTERA_NIOS2:
@@ -16332,6 +16402,8 @@ is_32bit_pcrel_reloc (Filedata * filedata, unsigned int reloc_type)
       return reloc_type == 99;  /* R_LARCH_32_PCREL.  */
     case EM_MICROBLAZE:
       return reloc_type == 2;  /* R_MICROBLAZE_32_PCREL.  */
+    case EM_NANOMIPS:
+      return reloc_type == 248;  /* R_NANOMIPS_PC32.  */
     case EM_OR1K:
       return reloc_type == 9; /* R_OR1K_32_PCREL.  */
     case EM_PARISC:
@@ -16421,6 +16493,8 @@ is_64bit_abs_reloc (Filedata * filedata, unsigned int reloc_type)
       return reloc_type == 1; /* R_TILEGX_64.  */
     case EM_MIPS:
       return reloc_type == 18;	/* R_MIPS_64.  */
+    case EM_NANOMIPS:
+      return reloc_type == 2;	/* R_NANOMIPS_64.  */
     case EM_KVX:
       return reloc_type == 3; /* R_KVX_64 */
     default:
@@ -16534,6 +16608,8 @@ is_16bit_abs_reloc (Filedata * filedata, unsigned int reloc_type)
       /* Fall through.  */
     case EM_MSP430_OLD:
       return reloc_type == 5; /* R_MSP430_16_BYTE.  */
+    case EM_NANOMIPS:
+      return reloc_type == 7; /* R_NANOMIPS_UNSIGNED_16 */
     case EM_NDS32:
       return reloc_type == 19; /* R_NDS32_16_RELA.  */
     case EM_ALTERA_NIOS2:
@@ -16818,6 +16894,7 @@ is_none_reloc (Filedata * filedata, unsigned int reloc_type)
     case EM_X86_64:  /* R_X86_64_NONE.  */
     case EM_Z80:     /* R_Z80_NONE. */
     case EM_WEBASSEMBLY: /* R_WASM32_NONE.  */
+    case EM_NANOMIPS: /* R_NANOMIPS_NONE.  */
       return reloc_type == 0;
 
     case EM_AARCH64:
@@ -19367,6 +19444,23 @@ display_mips_gnu_attribute (const unsigned char * p,
   return display_tag_value (tag & 1, p, end);
 }
 
+static unsigned char *
+display_nanomips_gnu_attribute (unsigned char * p,
+				unsigned int tag,
+				const unsigned char * const end)
+{
+  if (tag == Tag_GNU_NANOMIPS_ABI_FP)
+    {
+      int val;
+
+      READ_ULEB (val, p, end);
+      printf ("  Tag_GNU_NANOMIPS_ABI_FP: ");
+      return p;
+   }
+
+  return display_tag_value (tag & 1, p, end);
+}
+
 static const unsigned char *
 display_tic6x_attribute (const unsigned char * p,
 			 const unsigned char * const end)
@@ -21826,6 +21920,331 @@ process_got_section_contents (Filedata * filedata)
   return res;
 }
 
+static void
+print_nanomips_ases (unsigned int mask)
+{
+  if (mask & NANOMIPS_ASE_MT)
+    fputs ("\n\tMT ASE", stdout);
+  if (mask & NANOMIPS_ASE_VIRT)
+    fputs ("\n\tVZ ASE", stdout);
+ if ((mask & NANOMIPS_ASE_xNMS) == 0)
+    fputs ("\n\tnanoMIPS subset", stdout);
+  else if (mask == 0)
+    fprintf (stdout, "\n\t%s", _("None"));
+  else if ((mask & ~NANOMIPS_ASE_MASK) != 0)
+    fprintf (stdout, "\n\t%s (%x)", _("Unknown"), mask & ~NANOMIPS_ASE_MASK);
+}
+
+static void
+print_nanomips_isa_ext (unsigned int isa_ext)
+{
+  switch (isa_ext)
+    {
+    case 0:
+      fputs (_("None"), stdout);
+      break;
+    default:
+      fprintf (stdout, "%s (%d)", _("Unknown"), isa_ext);
+    }
+}
+
+static int
+get_nanomips_reg_size (int reg_size)
+{
+  return (get_mips_reg_size (reg_size));
+}
+
+/* DATA points to the contents of a nanoMIPS GOT that starts at VMA PLTGOT.
+   Print the Address, Access and Initial fields of an entry at VMA ADDR
+   and return the VMA of the next entry, or -1 if there was a problem.
+   Does not read from DATA_END or beyond.  */
+
+static bfd_vma
+print_nanomips_got_entry (unsigned char * data, bfd_vma pltgot, bfd_vma addr,
+			  unsigned char * data_end)
+{
+  printf (" ");
+  print_vma (addr, LONG_HEX);
+  printf (" %8d(gp)  ", (int) (addr - pltgot));
+
+  if (data == NULL)
+    printf ("%*s", is_32bit_elf ? 8 : 16, _("<unknown>"));
+  else
+    {
+      bfd_vma entry;
+      unsigned char * from = data + addr - pltgot;
+
+      if (from + (is_32bit_elf ? 4 : 8) > data_end)
+	{
+	  warn (_("nanoMIPS GOT entry extends beyond the end of available data\n"));
+	  printf ("%*s", is_32bit_elf ? 8 : 16, _("<corrupt>"));
+	  return (bfd_vma) -1;
+	}
+      else
+	{
+	  entry = byte_get (data + addr - pltgot, is_32bit_elf ? 4 : 8);
+	  print_vma (entry, LONG_HEX);
+	}
+    }
+  return addr + (is_32bit_elf ? 4 : 8);
+}
+
+/* Comparison function to sort relocs by increasing offsets.  */
+
+static /* signed */ int
+relcmp (const void *p, const void *q)
+{
+  Elf_Internal_Rela *sp = (Elf_Internal_Rela *) p;
+  Elf_Internal_Rela *sq = (Elf_Internal_Rela *) q;
+
+  return (sp->r_offset > sq->r_offset
+	  ? 1
+	  : (sp->r_offset < sq->r_offset ? -1 : 0));
+}
+
+/* Pretty-print the nanoMIPS GOT.  */
+
+static bool
+print_nanomips_got (Filedata * filedata)
+{
+  Elf_Internal_Shdr *sect = NULL;
+  bfd_vma pltgot = 0;
+  unsigned char *data_end;
+  unsigned char *data;
+  bfd_vma ent = 0;
+  bfd_vma end;
+  int addr_size;
+  int sym_width;
+  struct {
+    Elf_Internal_Rela * rels;
+    unsigned long relsize;
+    unsigned rindex;
+  } rtable[3];
+  int i;
+  enum {REL=0, RELA=1, JMPREL=2};
+
+  /* Clear the relocs.  */
+  memset (rtable, 0, sizeof (rtable));
+
+  /* Fetch the GOT.  */
+  sect = find_section (filedata, ".got");
+
+  if (sect == NULL)
+    return true;
+
+  /* This is overly complicated in order to handle dynamic relocations as
+     either REL or RELA and to handle the case where jump relocations are
+     embedded within the dynamic relocations section.  */
+  if (filedata->dynamic_info[DT_REL] != 0)
+    {
+      if (!slurp_rel_relocs (filedata,
+			     offset_from_vma (filedata, filedata->dynamic_info[DT_REL],
+					      filedata->dynamic_info[DT_RELENT]),
+			     filedata->dynamic_info[DT_RELSZ],
+			     &rtable[REL].rels, &rtable[REL].relsize))
+	return false;
+      /* Sort for easy lookup.  */
+      qsort (rtable[REL].rels, rtable[REL].relsize,
+	     sizeof (Elf_Internal_Rela), relcmp);
+    }
+  if (filedata->dynamic_info[DT_RELA] != 0)
+    {
+      if (!slurp_rel_relocs (filedata,
+			     offset_from_vma (filedata, filedata->dynamic_info[DT_RELA],
+					      filedata->dynamic_info[DT_RELAENT]),
+			     filedata->dynamic_info[DT_RELASZ],
+			     &rtable[RELA].rels, &rtable[RELA].relsize))
+	return false;
+      /* Sort for easy lookup.  */
+      qsort (rtable[RELA].rels, rtable[RELA].relsize, sizeof (Elf_Internal_Rela), relcmp);
+    }
+  if (filedata->dynamic_info[DT_JMPREL] != 0)
+    {
+      int rent = ((filedata->dynamic_info[DT_PLTREL] == DT_REL)
+		  ? filedata->dynamic_info[DT_RELENT] : filedata->dynamic_info[DT_RELAENT]);
+      /* If jump relocations are embedded within regular dynamic relocations,
+	 we don't need to read them again.  They have already been read above.  */
+      if ((filedata->dynamic_info[DT_PLTREL] == DT_REL
+	   && (filedata->dynamic_info[DT_JMPREL] < filedata->dynamic_info[DT_REL]
+	       || filedata->dynamic_info[DT_JMPREL] >= filedata->dynamic_info[DT_REL] + filedata->dynamic_info[DT_RELSZ]))
+	  || (filedata->dynamic_info[DT_PLTREL] == DT_RELA
+	      && (filedata->dynamic_info[DT_JMPREL] < filedata->dynamic_info[DT_RELA]
+		  || filedata->dynamic_info[DT_JMPREL] >= filedata->dynamic_info[DT_RELA] + filedata->dynamic_info[DT_RELASZ])))
+	{
+	  if (!slurp_rel_relocs (filedata,
+				 offset_from_vma (filedata, filedata->dynamic_info[DT_JMPREL], rent),
+				 filedata->dynamic_info[DT_PLTRELSZ],
+				 &rtable[JMPREL].rels, &rtable[JMPREL].relsize))
+	    return false;
+	  /* Sort for easy lookup.  */
+	  qsort (rtable[JMPREL].rels, rtable[JMPREL].relsize, sizeof (Elf_Internal_Rela), relcmp);
+	}
+    }
+  /* Temporary hack. Remove when the linker generate DT_JMPREL.  */
+  else if (find_section (filedata, ".rel.nanoMIPS.stubs") != NULL)
+    {
+      Elf_Internal_Shdr *jumpsect = find_section (filedata, ".rel.nanoMIPS.stubs");
+      if (!slurp_rel_relocs (filedata,
+			     offset_from_vma (filedata, jumpsect->sh_addr,
+					      filedata->dynamic_info[DT_RELENT]),
+			     jumpsect->sh_size,
+			     &rtable[JMPREL].rels, &rtable[JMPREL].relsize))
+	return false;
+    }
+
+  pltgot = sect->sh_addr;
+
+  /* Find relocations corresponding to the beginning of the GOT.  */
+  for (i = REL; i <= JMPREL; i++)
+    while (rtable[i].rindex < rtable[i].relsize
+	   && rtable[i].rels[rtable[i].rindex].r_offset < pltgot)
+      rtable[i].rindex++;
+
+  ent = pltgot;
+  addr_size = (is_32bit_elf ? 4 : 8);
+  /* Available screen width for symbols is whatever is left over
+     after everything else, including column-separating whitespaces.  */
+  sym_width = ((is_32bit_elf ? 80 : 160) - 32 - addr_size * 4);
+  end = pltgot + sect->sh_size;
+
+  data = (unsigned char *) get_data (NULL, filedata, sect->sh_offset,
+				     end - pltgot, 1,
+				     _("Global Offset Table data"));
+  data_end = data + (end - pltgot);
+
+  printf (_("\nGOT:\n"));
+  printf (_(" GP value: "));
+  print_vma (ent, LONG_HEX);
+  printf ("\n\n");
+
+  /* In a dynamic binary GOT[0] is reserved for the dynamic
+     loader to store the lazy resolver pointer, however in
+     a static binary it may well have been omitted and GOT
+     reduced to a table of addresses.  */
+  if (data
+      && data + ent - pltgot + addr_size <= data_end
+      && byte_get (data + ent - pltgot, addr_size) == 0)
+    {
+      printf (_(" Reserved entries:\n"));
+      printf (_("  %*s %*s   %*s   Purpose\n"),
+	      (- addr_size * 2) + 1, _("Address"), 12, _("Access"),
+	      (- addr_size * 2) + 1, _("Initial"));
+      ent = print_nanomips_got_entry (data, pltgot, ent, data_end);
+      printf (_("  Lazy stub resolver\n"));
+      if (ent == (bfd_vma) -1)
+	goto sgot_print_fail;
+
+      if (ent == (bfd_vma) -1)
+	goto sgot_print_fail;
+
+      /* Check for the MSB of GOT[1] being set, identifying a
+	 GNU object.  This entry will be used by some runtime
+	 loaders, to store the module pointer.  Otherwise this
+	 is an ordinary local entry.  */
+      if (data
+	  && data + ent - pltgot + addr_size <= data_end
+	  && (byte_get (data + ent - pltgot, addr_size)
+	      >> (addr_size * 8 - 1)) != 0)
+	{
+	  printf ("\n");
+	  ent = print_nanomips_got_entry (data, pltgot, ent, data_end);
+	  printf (_("  Module pointer\n"));
+	  if (ent == (bfd_vma) -1)
+	    goto sgot_print_fail;
+	}
+      printf ("\n");
+    }
+
+  if (ent < end)
+    {
+      printf (_(" Entries:\n"));
+      printf (_("  %*s %*s   %*s   %*s   Name\n"),
+	      (- addr_size * 2) + 1, _("Address"), 12, _("Access"),
+	      (- addr_size * 2) + 1, _("Value"), -9, _("Type"));
+    }
+
+  while (ent < end)
+    {
+      Elf_Internal_Rela *rel = NULL;
+      bfd_vma next_ent = print_nanomips_got_entry (data, pltgot,
+						   ent, data_end);
+      if (next_ent == (bfd_vma) -1)
+	{
+	  ent = (bfd_vma) -1;
+	  goto sgot_print_fail;
+	}
+
+      /* Scan all relocations for a match against this GOT slot.  */
+      for (i = REL; i <= JMPREL; i++)
+	{
+	  while (rel == NULL && rtable[i].rindex < rtable[i].relsize)
+	    {
+	      if (rtable[i].rels[rtable[i].rindex].r_offset == ent)
+		rel = &rtable[i].rels[rtable[i].rindex];
+	      if (rtable[i].rels[rtable[i].rindex].r_offset >= ent)
+		break;
+	      rtable[i].rindex ++;
+	    }
+	}
+
+      if (rel != NULL)
+	{
+	  Elf_Internal_Sym * psym = 0;
+
+	  switch (get_reloc_type (filedata, rel->r_info))
+	    {
+	      case 11: /* R_NANOMIPS_JUMP_SLOT.  */
+		printf ("  %*s  ", -10, "Lazy-stub"); break;
+	      case 12: /* R_NANOMIPS_IRELATIVE.  */
+		printf ("  %*s  ", -10, "Indirect"); break;
+	      case 10: /* R_NANOMIPS_GLOBAL.  */
+	      case 1: /* R_NANOMIPS_32.  */
+	      case 2: /* R_NANOMIPS_64.  */
+		printf ("  %*s  ", -10, "Global"); break;
+	      case 9: /* R_NANOMIPS_RELATIVE.  */
+		printf ("  %*s  ", -10, "Local"); break;
+	      default:
+		break;
+	    }
+
+	  if (filedata->dynamic_symbols)
+	    {
+	      psym = (filedata->dynamic_symbols + get_reloc_symindex (rel->r_info));
+
+	      if (valid_dynamic_name (filedata, psym->st_name))
+		print_symbol_name (sym_width, get_dynamic_name (filedata, psym->st_name));
+	      else
+		printf (_("<corrupt: %14ld>"), psym->st_name);
+	    }
+	}
+
+      ent = next_ent;
+      printf ("\n");
+    }
+  printf ("\n");
+
+ sgot_print_fail:
+  if (data)
+    free (data);
+
+  for (i = REL; i <= JMPREL; i++)
+    if (rtable[i].rels)
+      free (rtable[i].rels);
+
+  return (ent != (bfd_vma) -1);;
+}
+
+
+static bool
+process_nanomips_specific (Filedata * filedata)
+{
+  process_attributes (filedata, NULL, SHT_GNU_ATTRIBUTES, NULL,
+		      display_nanomips_gnu_attribute);
+
+  /* Pretty-print the GOT.  */
+  return print_nanomips_got (filedata);
+}
+
 static bool
 process_gnu_liblist (Filedata * filedata)
 {
@@ -24759,6 +25178,9 @@ process_arch_specific (Filedata * filedata)
 				display_riscv_attribute,
 				display_generic_attribute);
 
+    case EM_NANOMIPS:
+      return process_nanomips_specific (filedata);
+
     case EM_NDS32:
       return process_nds32_specific (filedata);
 
-- 
2.39.5