diff --git a/binutils/readelf.c b/binutils/readelf.c
index 140337d7bec..20ecbb67b35 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -139,6 +139,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"
@@ -1097,6 +1098,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:
@@ -1894,6 +1896,10 @@ dump_relocations (Filedata *filedata,
 	  rtype = elf_nios2_reloc_type (type);
 	  break;
 
+	case EM_NANOMIPS:
+	  rtype = elf_nanomips_reloc_type (type);
+	  break;
+
 	case EM_TI_PRU:
 	  rtype = elf_pru_reloc_type (type);
 	  break;
@@ -2205,6 +2211,16 @@ get_mips_dynamic_type (unsigned long type)
     }
 }
 
+static const char *
+get_nanomips_dynamic_type (unsigned long type)
+{
+  switch (type)
+    {
+    default:
+      return NULL;
+    }
+}
+
 static const char *
 get_sparc64_dynamic_type (unsigned long type)
 {
@@ -2571,6 +2587,9 @@ get_dynamic_type (Filedata * filedata, unsigned long type)
 	    case EM_RISCV:
 	      result = get_riscv_dynamic_type (type);
 	      break;
+	    case EM_NANOMIPS:
+	      result = get_nanomips_dynamic_type (type);
+	      break;
 	    default:
 	      if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
 		result = get_solaris_dynamic_type (type);
@@ -4393,6 +4412,38 @@ get_machine_flags (Filedata * filedata, unsigned e_flags, unsigned e_machine)
 	    strcat (buf, ", OBJ-v1");
 
 	  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;
+	    }
+	    break;
 	}
     }
 
@@ -4521,6 +4572,20 @@ get_mips_segment_type (unsigned long type)
     }
 }
 
+static const char *
+get_nanomips_segment_type (unsigned long type)
+{
+  switch (type)
+    {
+    case PT_NANOMIPS_ABIFLAGS:
+      return "ABIFLAGS";
+    default:
+      break;
+    }
+
+  return NULL;
+}
+
 static const char *
 get_parisc_segment_type (unsigned long type)
 {
@@ -4675,6 +4740,9 @@ get_segment_type (Filedata * filedata, unsigned long p_type)
 	    case EM_S390_OLD:
 	      result = get_s390_segment_type (p_type);
 	      break;
+	    case EM_NANOMIPS:
+	      result = get_nanomips_segment_type (p_type);
+	      break;
 	    case EM_RISCV:
 	      result = get_riscv_segment_type (p_type);
 	      break;
@@ -10725,6 +10793,17 @@ dynamic_section_mips_val (Filedata * filedata, Elf_Internal_Dyn * entry)
     putchar ('\n');
 }
 
+static void
+dynamic_section_nanomips_val (Elf_Internal_Dyn * entry)
+{
+  switch (entry->d_tag)
+    {
+    default:
+      print_vma (entry->d_un.d_ptr, PREFIX_HEX);
+    }
+    putchar ('\n');
+}
+
 static void
 dynamic_section_parisc_val (Elf_Internal_Dyn * entry)
 {
@@ -12065,6 +12144,9 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n"));
 		case EM_IA_64:
 		  dynamic_section_ia64_val (entry);
 		  break;
+		case EM_NANOMIPS:
+		  dynamic_section_nanomips_val (entry);
+		  break;
 		default:
 		  print_vma (entry->d_un.d_val, PREFIX_HEX);
 		  putchar ('\n');
@@ -14357,6 +14439,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:
@@ -14561,6 +14645,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.  */
     default:
       return false;
     }
@@ -14668,6 +14754,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:
@@ -14896,6 +14984,7 @@ is_none_reloc (Filedata * filedata, unsigned int reloc_type)
     case EM_MOXIE:   /* R_MOXIE_NONE.  */
     case EM_NIOS32:  /* R_NIOS_NONE.  */
     case EM_OR1K:    /* R_OR1K_NONE. */
+    case EM_NANOMIPS:    /* R_NANOMIPS_NONE.  */
     case EM_PARISC:  /* R_PARISC_NONE.  */
     case EM_PPC64:   /* R_PPC64_NONE.  */
     case EM_PPC:     /* R_PPC_NONE.  */
@@ -17480,6 +17569,71 @@ display_mips_gnu_attribute (unsigned char * p,
   return display_tag_value (tag & 1, p, end);
 }
 
+static void
+print_nanomips_fp_abi_value (int val)
+{
+  switch (val)
+    {
+    case Val_GNU_NANOMIPS_ABI_FP_ANY:
+      printf (_("Hard or soft float\n"));
+      break;
+    case Val_GNU_NANOMIPS_ABI_FP_DOUBLE:
+      printf (_("Hard float (double precision)\n"));
+      break;
+    case Val_GNU_NANOMIPS_ABI_FP_SINGLE:
+      printf (_("Hard float (single precision)\n"));
+      break;
+    case Val_GNU_NANOMIPS_ABI_FP_SOFT:
+      printf (_("Soft float\n"));
+      break;
+    default:
+      printf ("??? (%d)\n", val);
+      break;
+    }
+}
+
+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)
+	  {
+	    uint64_t val;
+
+	    READ_ULEB (val, p, end);
+	    printf ("  Tag_GNU_NANOMIPS_ABI_FP: ");
+
+	    print_nanomips_fp_abi_value (val);
+
+	    return p;
+	  }
+
+	if (tag == Tag_GNU_NANOMIPS_ABI_MSA)
+	  {
+	    uint64_t val;
+
+	    READ_ULEB (val, p, end);
+	    printf ("  Tag_GNU_NANOMIPS_ABI_MSA: ");
+
+	  switch (val)
+	    {
+	      case Val_GNU_NANOMIPS_ABI_MSA_ANY:
+		printf (_("Any MSA or not\n"));
+		break;
+	      case Val_GNU_NANOMIPS_ABI_MSA_128:
+		printf (_("128-bit MSA\n"));
+		break;
+	      default:
+		printf ("??? (%lu)\n", val);
+		break;
+	    }
+	  return p;
+	}
+
+	return display_tag_value (tag & 1, p, end);
+}
+
 static unsigned char *
 display_tic6x_attribute (unsigned char * p,
 			 const unsigned char * const end)
@@ -19317,6 +19471,350 @@ process_mips_specific (Filedata * filedata)
   return res;
 }
 
+static void
+print_nanomips_ases (unsigned int mask)
+{
+  if (mask & NANOMIPS_ASE_DSPR3)
+    fputs ("\n\tDSP R3 ASE", stdout);
+  if (mask & NANOMIPS_ASE_EVA)
+    fputs ("\n\tEnhanced VA Scheme", stdout);
+  if (mask & NANOMIPS_ASE_MCU)
+    fputs ("\n\tMCU (MicroController) ASE", stdout);
+  if (mask & NANOMIPS_ASE_MT)
+    fputs ("\n\tMT ASE", stdout);
+  if (mask & NANOMIPS_ASE_VIRT)
+    fputs ("\n\tVZ ASE", stdout);
+  if (mask & NANOMIPS_ASE_MSA)
+    fputs ("\n\tMSA ASE", stdout);
+  if (mask & NANOMIPS_ASE_TLB)
+    fputs ("\n\tTLB ASE", stdout);
+  if (mask & NANOMIPS_ASE_GINV)
+    fputs ("\n\tGINV 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));
+}
+
+static bool
+process_nanomips_specific (Filedata * filedata)
+{
+	Elf_Internal_Dyn * entry;
+	Elf_Internal_Shdr *sect = NULL;
+	size_t options_offset = 0;
+	bfd_vma pltgot = 0;
+	bfd_vma gotsym = 0;
+	bfd_vma symtabno = 0;
+
+	process_attributes (filedata, NULL, SHT_GNU_ATTRIBUTES, NULL,
+	display_nanomips_gnu_attribute);
+
+	sect = find_section (filedata, ".nanoMIPS.abiflags");
+
+	if (sect != NULL)
+	  {
+	    Elf_External_ABIFlags_v0 *abiflags_ext;
+	    Elf_Internal_ABIFlags_v0 abiflags_in;
+
+	    if (sizeof (Elf_External_ABIFlags_v0) != sect->sh_size)
+	      fputs ("\nCorrupt ABI Flags section.\n", stdout);
+	    else
+	      {
+		abiflags_ext = get_data (NULL, filedata, sect->sh_offset, 1,
+		  sect->sh_size, _("nanoMIPS ABI Flags section"));
+		if (abiflags_ext)
+		  {
+		    abiflags_in.version = BYTE_GET (abiflags_ext->version);
+		    abiflags_in.isa_level = BYTE_GET (abiflags_ext->isa_level);
+		    abiflags_in.isa_rev = BYTE_GET (abiflags_ext->isa_rev);
+		    abiflags_in.gpr_size = BYTE_GET (abiflags_ext->gpr_size);
+		    abiflags_in.cpr1_size = BYTE_GET (abiflags_ext->cpr1_size);
+		    abiflags_in.cpr2_size = BYTE_GET (abiflags_ext->cpr2_size);
+		    abiflags_in.fp_abi = BYTE_GET (abiflags_ext->fp_abi);
+		    abiflags_in.isa_ext = BYTE_GET (abiflags_ext->isa_ext);
+		    abiflags_in.ases = BYTE_GET (abiflags_ext->ases);
+		    abiflags_in.flags1 = BYTE_GET (abiflags_ext->flags1);
+		    abiflags_in.flags2 = BYTE_GET (abiflags_ext->flags2);
+
+		    printf ("\nnanoMIPS ABI Flags Version: %d\n", abiflags_in.version);
+		    printf ("\nISA: nanoMIPS%d", abiflags_in.isa_level);
+		    if (abiflags_in.isa_rev > 1)
+		      printf ("r%d", abiflags_in.isa_rev);
+		    printf ("\nGPR size: %d",
+		    get_nanomips_reg_size (abiflags_in.gpr_size));
+		    printf ("\nCPR1 size: %d",
+		    get_nanomips_reg_size (abiflags_in.cpr1_size));
+		    printf ("\nCPR2 size: %d",
+		    get_nanomips_reg_size (abiflags_in.cpr2_size));
+		    fputs ("\nFP ABI: ", stdout);
+		    print_nanomips_fp_abi_value (abiflags_in.fp_abi);
+		    fputs ("ISA Extension: ", stdout);
+		    print_nanomips_isa_ext (abiflags_in.isa_ext);
+		    fputs ("\nASEs:", stdout);
+		    print_nanomips_ases (abiflags_in.ases);
+		    printf ("\nFLAGS 1: %8.8lx", abiflags_in.flags1);
+		    printf ("\nFLAGS 2: %8.8lx", abiflags_in.flags2);
+		    fputc ('\n', stdout);
+		    free (abiflags_ext);
+		  }
+		}
+	      }
+
+	    if (!get_dynamic_section (filedata))
+	      return false;
+
+	    for (entry = filedata->dynamic_section;
+	      entry < filedata->dynamic_section + filedata->dynamic_nent && entry->d_tag != DT_NULL;
+	      ++entry)
+	      switch (entry->d_tag)
+		{
+		case DT_PLTGOT:
+		  pltgot = entry->d_un.d_ptr;
+		  break;
+		default:
+		  break;
+		}
+
+	    if (options_offset != 0)
+	      {
+		Elf_External_Options * eopt;
+		Elf_Internal_Options * iopt;
+		Elf_Internal_Options * option;
+		size_t offset;
+		int cnt;
+		sect = filedata->section_headers;
+
+		/* Find the section header so that we get the size.  */
+		sect = find_section_by_type (filedata, SHT_MIPS_OPTIONS);
+		if (sect == NULL)
+		  {
+		    error (_("No MIPS_OPTIONS header found\n"));
+		    return 0;
+		  }
+
+		eopt = (Elf_External_Options *) get_data (NULL, filedata, options_offset, 1,
+		  sect->sh_size, _("options"));
+		if (eopt)
+		  {
+		    iopt = (Elf_Internal_Options *)
+		      cmalloc ((sect->sh_size / sizeof (eopt)), sizeof (* iopt));
+		    if (iopt == NULL)
+		      {
+			error (_("Out of memory allocatinf space for MIPS options\n"));
+			return 0;
+		      }
+
+		    offset = cnt = 0;
+		    option = iopt;
+
+		    while (offset < sect->sh_size)
+		      {
+			Elf_External_Options * eoption;
+			eoption = (Elf_External_Options *) ((char *) eopt + offset);
+
+			option->kind = BYTE_GET (eoption->kind);
+			option->size = BYTE_GET (eoption->size);
+			option->section = BYTE_GET (eoption->section);
+			option->info = BYTE_GET (eoption->info);
+
+			offset += option->size;
+
+			++option;
+			++cnt;
+		      }
+
+		    printf (_("\nSection '%s' contains %d entries:\n"),
+		    printable_section_name (filedata, sect), cnt);
+
+		    option = iopt;
+
+		    while (cnt-- > 0)
+		      {
+			size_t len;
+
+			switch (option->kind)
+			  {
+			  case ODK_NULL:
+			    /* This shouldn't happen.  */
+			    printf (" NULL       %d %x", option->section, option->info);
+			    break;
+			  case ODK_EXCEPTIONS:
+			    fputs (" EXCEPTIONS fpe_min(", stdout);
+			    process_mips_fpe_exception (option->info & OEX_FPU_MIN);
+			    fputs (") fpe_max(", stdout);
+			    process_mips_fpe_exception ((option->info & OEX_FPU_MAX) >> 8);
+			    fputs (")", stdout);
+
+			    if (option->info & OEX_PAGE0)
+			      fputs (" PAGE0", stdout);
+			    if (option->info & OEX_SMM)
+			      fputs (" SMM", stdout);
+			    if (option->info & OEX_FPDBUG)
+			      fputs (" FPDBUG", stdout);
+			    if (option->info & OEX_DISMISS)
+			      fputs (" DISMISS", stdout);
+			    break;
+			  case ODK_PAD:
+			    fputs (" PAD       ", stdout);
+			    if (option->info & OPAD_PREFIX)
+			    fputs (" PREFIX", stdout);
+			    if (option->info & OPAD_POSTFIX)
+			    fputs (" POSTFIX", stdout);
+			    if (option->info & OPAD_SYMBOL)
+			    fputs (" SYMBOL", stdout);
+			    break;
+			  case ODK_HWPATCH:
+			    fputs (" HWPATCH   ", stdout);
+			    if (option->info & OHW_R4KEOP)
+			      fputs (" R4KEOP", stdout);
+			    if (option->info & OHW_R8KPFETCH)
+			      fputs (" R8KPFETCH", stdout);
+			    if (option->info & OHW_R5KEOP)
+			      fputs (" R5KEOP", stdout);
+			    if (option->info & OHW_R5KCVTL)
+			      fputs (" R5KCVTL", stdout);
+			    break;
+			  case ODK_FILL:
+			    fputs (" FILL       ", stdout);
+			    /* XXX Print content of info word?  */
+			    break;
+			  case ODK_TAGS:
+			    fputs (" TAGS       ", stdout);
+			    /* XXX Print content of info word?  */
+			    break;
+			  case ODK_HWAND:
+			    fputs (" HWAND     ", stdout);
+			    if (option->info & OHWA0_R4KEOP_CHECKED)
+			      fputs (" R4KEOP_CHECKED", stdout);
+			    if (option->info & OHWA0_R4KEOP_CLEAN)
+			      fputs (" R4KEOP_CLEAN", stdout);
+			    break;
+			  case ODK_HWOR:
+			    fputs (" HWOR      ", stdout);
+			    if (option->info & OHWA0_R4KEOP_CHECKED)
+			      fputs (" R4KEOP_CHECKED", stdout);
+			    if (option->info & OHWA0_R4KEOP_CLEAN)
+			      fputs (" R4KEOP_CLEAN", stdout);
+			    break;
+			  case ODK_GP_GROUP:
+			    printf (" GP_GROUP  %#06x  self-contained %#06x",
+			      option->info & OGP_GROUP,
+			      (option->info & OGP_SELF) >> 16);
+			    break;
+			  case ODK_IDENT:
+			    printf (" IDENT     %#06x  self-contained %#06x",
+			      option->info & OGP_GROUP,
+			      (option->info & OGP_SELF) >> 16);
+			    break;
+			  default:
+			    /* This shouldn't happen.  */
+			    printf (" %3d ???     %d %x",
+			    option->kind, option->section, option->info);
+			  break;
+			  }
+			len = sizeof (* eopt);
+			while (len < option->size)
+			  if (((char *) option)[len] >= ' '
+			    && ((char *) option)[len] < 0x7f)
+			    printf ("%c", ((char *) option)[len++]);
+			  else
+			    printf ("\\%03o", ((char *) option)[len++]);
+
+			fputs ("\n", stdout);
+			++option;
+		      }
+		    free (eopt);
+		  }
+	      }
+
+	    if (pltgot != 0)
+	      {
+		bfd_vma ent, end;
+		size_t i, offset;
+		unsigned char * data;
+		int addr_size;
+
+		ent = pltgot;
+		addr_size = (is_32bit_elf ? 4 : 8);
+
+		if (symtabno < gotsym)
+		  {
+		    error (_("The GOT symbol offset (%lu) is greater than the symbol table size (%lu)\n"),
+		      (long) gotsym, (long) symtabno);
+		    return 0;
+		  }
+
+		end = pltgot + (symtabno - gotsym) * addr_size;
+		offset = offset_from_vma (filedata, pltgot, end - pltgot);
+		data = (unsigned char *) get_data (NULL, filedata, offset,
+		  end - pltgot, 1,
+		_("Global Offset Table data"));
+		if (data == NULL)
+		  return 0;
+
+		printf (_(" Canonical gp value: "));
+		print_vma (pltgot + 0x7ff0, LONG_HEX);
+		printf ("\n\n");
+
+		printf (_(" Reserved entries:\n"));
+		printf (_("  %*s %10s %*s Purpose\n"),
+		addr_size * 2, _("Address"), _("Access"),
+		addr_size * 2, _("Initial"));
+		ent = print_mips_got_entry (data, pltgot, ent, data + (end - pltgot));
+		printf (_(" Lazy resolver\n"));
+		if (data
+		  && (byte_get (data + ent - pltgot, addr_size)
+		  >> (addr_size * 8 - 1)) != 0)
+		  {
+		    ent = print_mips_got_entry (data, pltgot, ent, data + (end - pltgot));
+		    printf (_(" Module pointer (GNU extension)\n"));
+		  }
+		printf ("\n\n");
+		printf (_(" Entries:\n"));
+		printf ("  %*s %10s %*s %*s %-7s %3s %s\n",
+		addr_size * 2, _("Address"),
+		  _("Access"),
+		  addr_size * 2, _("Initial"),
+		  addr_size * 2, _("Sym.Val."),
+		  _("Type"),
+		  /* Note for translators: "Ndx" = abbreviated form of "Index".  */
+		  _("Ndx"), _("Name"));
+
+		for (i = gotsym; i < symtabno; i++)
+		  {
+		    ent = print_mips_got_entry (data, pltgot, ent, data + (end - pltgot));
+		    printf (" ");
+		    printf ("\n");
+		  }
+
+		if (data)
+		  free (data);
+	  }
+
+	return 1;
+}
+
 static bool
 process_nds32_specific (Filedata * filedata)
 {
@@ -22264,6 +22762,8 @@ process_arch_specific (Filedata * filedata)
       return process_attributes (filedata, "c6xabi", SHT_C6000_ATTRIBUTES,
 				 display_tic6x_attribute,
 				 display_generic_attribute);
+    case EM_NANOMIPS:
+      return process_nanomips_specific (filedata);
 
     case EM_CSKY:
       return process_attributes (filedata, "csky", SHT_CSKY_ATTRIBUTES,
