/* This file is is generated by a shell script.  DO NOT EDIT! */

/* 32 bit ELF emulation code for nds32elf16m
   Copyright (C) 1991-2025 Free Software Foundation, Inc.
   Written by Steve Chamberlain <sac@cygnus.com>
   ELF support by Ian Lance Taylor <ian@cygnus.com>

   This file is part of the GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#define TARGET_IS_nds32elf16m

#include "sysdep.h"
#include "bfd.h"
#include "libiberty.h"
#include "getopt.h"
#include "bfdlink.h"
#include "ctf-api.h"
#include "ld.h"
#include "ldmain.h"
#include "ldmisc.h"
#include "ldexp.h"
#include "ldlang.h"
#include "ldfile.h"
#include "ldlex.h"
#include "ldemul.h"
#include <ldgram.h>
#include "elf-bfd.h"
#include "ldelf.h"
#include "ldelfgen.h"

/* Declare functions used by various EXTRA_EM_FILEs.  */
static void gldnds32elf16m_before_parse (void);
static void gldnds32elf16m_before_plugin_all_symbols_read
  (void);
static void gldnds32elf16m_after_open (void);
static void gldnds32elf16m_before_allocation (void);
static void gldnds32elf16m_after_allocation (void);


#include "elf-bfd.h"
#include "elf/nds32.h"
#include <stdint.h>
#include "elf32-nds32.h"

static int relax_fp_as_gp = 1;		/* --mrelax-omit-fp  */
static int eliminate_gc_relocs = 0;	/* --meliminate-gc-relocs  */
static FILE *sym_ld_script = NULL;	/* --mgen-symbol-ld-script=<file>  */
static int hyper_relax = 1;		/* --mhyper-relax  */
static int tls_desc_trampoline = 0;	/* --m[no]tlsdesc-trampoline.  */
/* Disable if linking a dynamically linked executable.  */
static int load_store_relax = 1;

/* Save the target options into output bfd to avoid using to many global
   variables. Do this after the output has been created, but before
   inputs are read.  */
static void
nds32_elf_create_output_section_statements (void)
{
  if (strstr (bfd_get_target (link_info.output_bfd), "nds32") == NULL)
    {
      /* Check the output target is nds32.  */
      fatal (_("%P: error: cannot change output format whilst "
	       "linking %s binaries\n"), "NDS32");
      return;
    }

  bfd_elf32_nds32_set_target_option (&link_info,
				     relax_fp_as_gp,
				     eliminate_gc_relocs,
				     sym_ld_script,
				     hyper_relax,
				     tls_desc_trampoline,
				     load_store_relax);
}

static void
nds32_elf_after_parse (void)
{
  if (bfd_link_relocatable (&link_info)
      || bfd_link_pic (&link_info))
    DISABLE_RELAXATION;

  if (!RELAXATION_ENABLED)
    relax_fp_as_gp = 0;

  ldelf_after_parse ();
}

static void
nds32_elf_after_open (void)
{
  unsigned int arch_ver = (unsigned int)-1;
  unsigned int abi_ver = (unsigned int)-1;
  bfd *abfd;

  /* For now, make sure all object files are of the same architecture.
     We may try to merge object files with different architecture together.  */
  for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next)
    {
      if (arch_ver == (unsigned int)-1 && E_N1_ARCH != (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH))
	arch_ver = elf_elfheader (abfd)->e_flags & EF_NDS_ARCH ;

      if (abi_ver == (unsigned int)-1)
	{
	  /* Initialize ABI version, if not ABI0.
	     (OS uses empty file to create empty ELF with ABI0).  */
	  if ((elf_elfheader (abfd)->e_flags & EF_NDS_ABI) != 0)
	    abi_ver = elf_elfheader (abfd)->e_flags & EF_NDS_ABI ;
	}
      else if ((elf_elfheader (abfd)->e_flags & EF_NDS_ABI) != 0
	       && abi_ver != (elf_elfheader (abfd)->e_flags & EF_NDS_ABI))
	{
	  /* Incompatible objects.  */
	  fatal (_("%P: %pB: ABI version of object files mismatched\n"),
		 abfd);
	}
    }

  /* Check object files if the target is dynamic linked executable
     or shared object.  */
  if (elf_hash_table (&link_info)->dynamic_sections_created
      || bfd_link_pic (&link_info)
      || bfd_link_pie (&link_info))
    {
      /* Dynamic linked executable with SDA and non-PIC.
	 Turn off load/store relaxtion.  */
      /* This may support in the future.  */
      load_store_relax = 0 ;
      relax_fp_as_gp = 0;
    }

  /* Call the standard elf routine.  */
  gldnds32elf16m_after_open ();
}

static void
nds32_elf_after_allocation (void)
{
  /* Call default after allocation callback.
     1. This is where relaxation is done.
     2. It calls ldelf_map_segments to build ELF segment table.
     3. Any relaxation requires relax being done must be called after it.  */
  gldnds32elf16m_after_allocation ();
}


static void
gldnds32elf16m_before_parse (void)
{
  ldfile_set_output_arch ("nds32", bfd_arch_nds32);
  input_flags.dynamic = true;
  config.has_shared = false;
  config.separate_code = false;
  link_info.check_relocs_after_open_input = true;
  link_info.relro = DEFAULT_LD_Z_RELRO;
  link_info.separate_code = DEFAULT_LD_Z_SEPARATE_CODE;
  link_info.one_rosegment = DEFAULT_LD_ROSEGMENT;
  link_info.warn_execstack = DEFAULT_LD_WARN_EXECSTACK;
  link_info.no_warn_rwx_segments = ! DEFAULT_LD_WARN_RWX_SEGMENTS;
  link_info.default_execstack = DEFAULT_LD_EXECSTACK;
  link_info.error_execstack = DEFAULT_LD_ERROR_EXECSTACK;
  link_info.warn_is_error_for_rwx_segments = DEFAULT_LD_ERROR_RWX_SEGMENTS;
  link_info.memory_seal = DEFAULT_LD_Z_MEMORY_SEAL;
}


/* These variables are used to implement target options */

static char *audit; /* colon (typically) separated list of libs */
static char *depaudit; /* colon (typically) separated list of libs */


/* This is called before calling plugin 'all symbols read' hook.  */

static void
gldnds32elf16m_before_plugin_all_symbols_read (void)
{
  ldelf_before_plugin_all_symbols_read (false, false,
				        false,
					false,
					32, "/usr");
}

/* This is called after all the input files have been opened.  */

static void
gldnds32elf16m_after_open (void)
{
  ldelf_after_open (false, false,
		    false, false, 32, "/usr");
}


/* This is called after the sections have been attached to output
   sections, but before any sizes or addresses have been set.  */

static void
gldnds32elf16m_before_allocation (void)
{
  ldelf_before_allocation (&audit, &depaudit, NULL);
}


static void
gldnds32elf16m_after_allocation (void)
{
  int need_layout = bfd_elf_discard_info (link_info.output_bfd, &link_info);

  if (need_layout < 0)
    einfo (_("%X%P: .eh_frame/.stab edit: %E\n"));
  else
    ldelf_map_segments (need_layout);
}

static char *
gldnds32elf16m_get_script (int *isfile)
{
  *isfile = 1;

  if (bfd_link_relocatable (&link_info) && config.build_constructors)
    return "ldscripts/nds32elf16m.xu";
  else if (bfd_link_relocatable (&link_info))
    return "ldscripts/nds32elf16m.xr";
  else if (!config.text_read_only)
    return "ldscripts/nds32elf16m.xbn";
  else if (!config.magic_demand_paged)
    return "ldscripts/nds32elf16m.xn";
  else
    {
      if (link_info.separate_code)
	{
	  if (link_info.one_rosegment)
	    return "ldscripts/nds32elf16m.xer";
	  else
	    return "ldscripts/nds32elf16m.xe";
	}
      else
	return "ldscripts/nds32elf16m.x";
    }
}


static void
gldnds32elf16m_add_options
  (int ns, char **shortopts, int nl, struct option **longopts,
   int nrl ATTRIBUTE_UNUSED, struct option **really_longopts ATTRIBUTE_UNUSED)
{
  static const char xtra_short[] = "z:";
  static const struct option xtra_long[] = {
    {"build-id", optional_argument, NULL, OPTION_BUILD_ID},
    {"package-metadata", optional_argument, NULL, OPTION_PACKAGE_METADATA},
    {"compress-debug-sections", required_argument, NULL, OPTION_COMPRESS_DEBUG},
    {"rosegment", no_argument, NULL, OPTION_ROSEGMENT},
    {"no-rosegment", no_argument, NULL, OPTION_NO_ROSEGMENT},
    
  { "mfp-as-gp", no_argument, NULL, OPTION_FP_AS_GP},
  { "mno-fp-as-gp", no_argument, NULL, OPTION_NO_FP_AS_GP},
  { "mexport-symbols", required_argument, NULL, OPTION_EXPORT_SYMBOLS},
  { "mhyper-relax", required_argument, NULL, OPTION_HYPER_RELAX},
  { "mtlsdesc-trampoline", no_argument, NULL, OPTION_TLSDESC_TRAMPOLINE},
  { "mno-tlsdesc-trampoline", no_argument, NULL, OPTION_NO_TLSDESC_TRAMPOLINE},
  /* These are deprecated options.  Remove them in the future.  */
  { "mrelax-reduce-fp-update", no_argument, NULL, OPTION_REDUCE_FP_UPDATE},
  { "mrelax-no-reduce-fp-update", no_argument, NULL, OPTION_NO_REDUCE_FP_UPDATE},
  { "mbaseline", required_argument, NULL, OPTION_BASELINE},
  { "meliminate-gc-relocs", no_argument, NULL, OPTION_ELIM_GC_RELOCS},
  { "mrelax-omit-fp", no_argument, NULL, OPTION_FP_AS_GP},
  { "mrelax-no-omit-fp", no_argument, NULL, OPTION_NO_FP_AS_GP},
  { "mgen-symbol-ld-script", required_argument, NULL, OPTION_EXPORT_SYMBOLS},

    {NULL, no_argument, NULL, 0}
  };

  *shortopts = (char *) xrealloc (*shortopts, ns + sizeof (xtra_short));
  memcpy (*shortopts + ns, &xtra_short, sizeof (xtra_short));
  *longopts = (struct option *)
    xrealloc (*longopts, nl * sizeof (struct option) + sizeof (xtra_long));
  memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long));
}

#define DEFAULT_BUILD_ID_STYLE	"sha1"

static bool
gldnds32elf16m_handle_option (int optc)
{
  switch (optc)
    {
    default:
      return false;

    case OPTION_BUILD_ID:
      free ((char *) ldelf_emit_note_gnu_build_id);
      ldelf_emit_note_gnu_build_id = NULL;
      if (optarg == NULL)
	optarg = DEFAULT_BUILD_ID_STYLE;
      if (strcmp (optarg, "none"))
	ldelf_emit_note_gnu_build_id = xstrdup (optarg);
      break;

    case OPTION_PACKAGE_METADATA:
      free ((char *) ldelf_emit_note_fdo_package_metadata);
      ldelf_emit_note_fdo_package_metadata = NULL;
      if (optarg != NULL)
	{
	  size_t len = strlen (optarg);
	  if (len > 0)
	    {
	      char *package_metadata = xmalloc (len + 1);
	      percent_decode (optarg, package_metadata);
	      ldelf_emit_note_fdo_package_metadata = package_metadata;
	    }
	}
      break;

    case OPTION_COMPRESS_DEBUG:
      config.compress_debug = bfd_get_compression_algorithm (optarg);
      if (strcasecmp (optarg, "zstd") == 0)
	{
#ifndef HAVE_ZSTD
	  if (config.compress_debug == COMPRESS_DEBUG_ZSTD)
	    fatal (_("%P: --compress-debug-sections=zstd: ld is not built "
		     "with zstd support\n"));
#endif
	}
      if (config.compress_debug == COMPRESS_UNKNOWN)
	fatal (_("%P: invalid --compress-debug-sections option: `%s'\n"),
	       optarg);
      break;

    case OPTION_ROSEGMENT:
      link_info.one_rosegment = true;
      break;
    case OPTION_NO_ROSEGMENT:
      link_info.one_rosegment = false;
      break;      
    case 'z':
      if (strcmp (optarg, "defs") == 0)
	link_info.unresolved_syms_in_objects = RM_DIAGNOSE;
      else if (strcmp (optarg, "undefs") == 0)
	link_info.unresolved_syms_in_objects = RM_IGNORE;
      else if (strcmp (optarg, "muldefs") == 0)
	link_info.allow_multiple_definition = true;
      else if (startswith (optarg, "max-page-size="))
	{
	  char *end;

	  link_info.maxpagesize = strtoul (optarg + 14, &end, 0);
	  if (*end
	      || (link_info.maxpagesize & (link_info.maxpagesize - 1)) != 0)
	    fatal (_("%P: invalid maximum page size `%s'\n"),
		   optarg + 14);
	  link_info.maxpagesize_is_set = true;
	}
      else if (startswith (optarg, "common-page-size="))
	{
	  char *end;
	  link_info.commonpagesize = strtoul (optarg + 17, &end, 0);
	  if (*end
	      || (link_info.commonpagesize & (link_info.commonpagesize - 1)) != 0)
	    fatal (_("%P: invalid common page size `%s'\n"),
		   optarg + 17);
	  link_info.commonpagesize_is_set = true;
	}
      else if (startswith (optarg, "stack-size="))
	{
	  char *end;
	  link_info.stacksize = strtoul (optarg + 11, &end, 0);
	  if (*end || link_info.stacksize < 0)
	    fatal (_("%P: invalid stack size `%s'\n"), optarg + 11);
	  if (!link_info.stacksize)
	    /* Use -1 for explicit no-stack, because zero means
	       'default'.   */
	    link_info.stacksize = -1;
	}
      else if (strcmp (optarg, "execstack") == 0)
	{
	  link_info.execstack = true;
	  link_info.noexecstack = false;
	}
      else if (strcmp (optarg, "noexecstack") == 0)
	{
	  link_info.noexecstack = true;
	  link_info.execstack = false;
	}
      else if (strcmp (optarg, "unique-symbol") == 0)
	link_info.unique_symbol = true;
      else if (strcmp (optarg, "nounique-symbol") == 0)
	link_info.unique_symbol = false;
      else if (strcmp (optarg, "globalaudit") == 0)
	{
	  link_info.flags_1 |= DF_1_GLOBAUDIT;
	}
      else if (startswith (optarg, "start-stop-gc"))
	link_info.start_stop_gc = true;
      else if (startswith (optarg, "nostart-stop-gc"))
	link_info.start_stop_gc = false;
      else if (startswith (optarg, "start-stop-visibility="))
	{
	  if (strcmp (optarg, "start-stop-visibility=default") == 0)
	    link_info.start_stop_visibility = STV_DEFAULT;
	  else if (strcmp (optarg, "start-stop-visibility=internal") == 0)
	    link_info.start_stop_visibility = STV_INTERNAL;
	  else if (strcmp (optarg, "start-stop-visibility=hidden") == 0)
	    link_info.start_stop_visibility = STV_HIDDEN;
	  else if (strcmp (optarg, "start-stop-visibility=protected") == 0)
	    link_info.start_stop_visibility = STV_PROTECTED;
	  else
	    fatal (_("%P: invalid visibility in `-z %s'; "
		     "must be default, internal, hidden, or protected"),
		   optarg);
	}
      else if (strcmp (optarg, "sectionheader") == 0)
	config.no_section_header = false;
      else if (strcmp (optarg, "nosectionheader") == 0)
	config.no_section_header = true;
      else if (strcmp (optarg, "separate-code") == 0)
	link_info.separate_code = true;
      else if (strcmp (optarg, "noseparate-code") == 0)
	link_info.separate_code = false;
      else if (strcmp (optarg, "common") == 0)
	link_info.elf_stt_common = elf_stt_common;
      else if (strcmp (optarg, "nocommon") == 0)
	link_info.elf_stt_common = no_elf_stt_common;
      else if (strcmp (optarg, "text") == 0)
	link_info.textrel_check = textrel_check_error;
      else if (strcmp (optarg, "notext") == 0)
	link_info.textrel_check = textrel_check_none;
      else if (strcmp (optarg, "textoff") == 0)
	link_info.textrel_check = textrel_check_none;
      else
	queue_unknown_cmdline_warning ("-z %s", optarg);
      break;
 
  case OPTION_BASELINE:
    einfo (_("%P: --mbaseline is not used anymore\n"));
    break;
  case OPTION_ELIM_GC_RELOCS:
    eliminate_gc_relocs = 1;
    break;
  case OPTION_FP_AS_GP:
  case OPTION_NO_FP_AS_GP:
    relax_fp_as_gp = (optc == OPTION_FP_AS_GP);
    break;
  case OPTION_REDUCE_FP_UPDATE:
  case OPTION_NO_REDUCE_FP_UPDATE:
    einfo (_("%P: --relax-[no-]reduce-fp-updat is not used anymore\n"));
    break;
  case OPTION_EXPORT_SYMBOLS:
    if (!optarg)
      einfo (_("%P: missing file for --mexport-symbols\n"), optarg);

    if(strcmp (optarg, "-") == 0)
      sym_ld_script = stdout;
    else
      {
	sym_ld_script = fopen (optarg, FOPEN_WT);
	if(sym_ld_script == NULL)
	  fatal (_("%P: cannot open map file %s: %E\n"), optarg);
      }
    break;
  case OPTION_HYPER_RELAX:
    if (!optarg)
      einfo (_("%P: valid arguments to --mhyper-relax=(low|medium|high)\n"));

    if (strcmp (optarg, "low") == 0)
      hyper_relax = 0;
    else if (strcmp (optarg, "medium") == 0)
      hyper_relax = 1;
    else if (strcmp (optarg, "high") == 0)
      hyper_relax = 2;
    else
      einfo (_("%P: valid arguments to --mhyper-relax=(low|medium|high)\n"));

    break;
  case OPTION_TLSDESC_TRAMPOLINE:
    tls_desc_trampoline = 1;
    break;
  case OPTION_NO_TLSDESC_TRAMPOLINE:
    tls_desc_trampoline = 0;
     break;

    }

  return true;
}


static void
gldnds32elf16m_list_options (FILE * file)
{
 
  fprintf (file, _("\
  --m[no-]fp-as-gp            Disable/enable fp-as-gp relaxation\n"));
  fprintf (file, _("\
  --mexport-symbols=FILE      Exporting symbols in linker script\n"));
  fprintf (file, _("\
  --mhyper-relax=level        Adjust relax level (low|medium|high). default: medium\n"));
  fprintf (file, _("\
  --m[no-]tlsdesc-trampoline  Disable/enable TLS DESC trampoline\n"));

}

struct ld_emulation_xfer_struct ld_nds32elf16m_emulation =
{
  gldnds32elf16m_before_parse,
  syslib_default,
  hll_default,
  nds32_elf_after_parse,
  gldnds32elf16m_before_plugin_all_symbols_read,
  nds32_elf_after_open,
  after_check_relocs_default,
  ldelf_before_place_orphans,
  nds32_elf_after_allocation,
  ldelf_set_output_arch,
  ldemul_default_target,
  gldnds32elf16m_before_allocation,
  gldnds32elf16m_get_script,
  "nds32elf16m",
  "elf32-nds32le",
  ldelf_finish,
  nds32_elf_create_output_section_statements,
  ldelf_open_dynamic_archive,
  ldelf_place_orphan,
  NULL,
  NULL,
  gldnds32elf16m_add_options,
  gldnds32elf16m_handle_option,
  NULL,
  gldnds32elf16m_list_options,
  ldelf_load_symbols,
  NULL,
  NULL,
  NULL,
  ldelf_emit_ctf_early,
  ldelf_acquire_strings_for_ctf,
  ldelf_new_dynsym_for_ctf,
  NULL,
  NULL
};
