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

/* 32 bit ELF emulation code for avr25
   Copyright (C) 1991-2026 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_avr25

#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 gldavr25_before_parse (void);
static void gldavr25_before_plugin_all_symbols_read
  (void);
static void gldavr25_after_open (void);
static void gldavr25_before_allocation (void);
static void gldavr25_after_allocation (void);


#include "elf32-avr.h"
#include "ldctor.h"
#include "elf/avr.h"

/* The fake file and it's corresponding section meant to hold
   the linker stubs if needed.  */

static lang_input_statement_type *stub_file;
static asection *avr_stub_section;

/* Variables set by the command-line parameters and transferred
   to the bfd without use of global shared variables.  */

static bool avr_no_stubs = false;
static bool avr_debug_relax = false;
static bool avr_debug_stubs = false;
static bool avr_replace_call_ret_sequences = true;
static bfd_vma avr_pc_wrap_around = 0x10000000;

/* Transfers information to the bfd frontend.  */

static void
avr_elf_set_global_bfd_parameters (void)
{
  elf32_avr_setup_params (& link_info,
			  stub_file->the_bfd,
			  avr_stub_section,
			  avr_no_stubs,
			  avr_debug_stubs,
			  avr_debug_relax,
			  avr_pc_wrap_around,
			  avr_replace_call_ret_sequences);
}


/* Makes a conservative estimate of the trampoline section size that could
   be corrected later on.  */

static void
avr_elf_avr25_before_allocation (void)
{
  int ret;

  gldavr25_before_allocation ();

  if (bfd_get_flavour (link_info.output_bfd) != bfd_target_elf_flavour)
    {
      avr_no_stubs = true;
      return;
    }

  /* We only need stubs for avr6, avrxmega6, and avrxmega7.  */
  if (strcmp ("avr25", "avr6") != 0
      && strcmp ("avr25", "avrxmega6") != 0
      && strcmp ("avr25", "avrxmega7") != 0)
    avr_no_stubs = true;

  avr_elf_set_global_bfd_parameters ();

  /* If generating a relocatable output file, then
     we don't  have to generate the trampolines.  */
  if (bfd_link_relocatable (&link_info))
    avr_no_stubs = true;

  if (avr_no_stubs)
    return;

  ret = elf32_avr_setup_section_lists (link_info.output_bfd, &link_info);

  if (ret < 0)
    einfo (_("%X%P: can not setup the input section list: %E\n"));

  if (ret <= 0)
    return;

  /* Call into the BFD backend to do the real "stub"-work.  */
  if (! elf32_avr_size_stubs (link_info.output_bfd, &link_info, true))
    einfo (_("%X%P: can not size stub section: %E\n"));
}

/* This is called before the input files are opened.  We create a new
   fake input file to hold the stub section and generate the section itself.  */

static void
avr_elf_create_output_section_statements (void)
{
  flagword flags;

  if (bfd_get_flavour (link_info.output_bfd) != bfd_target_elf_flavour)
    {
      fatal (_("%P: error: cannot change output format "
	       "whilst linking %s binaries\n"), "AVR");
      return;
    }

  stub_file = lang_add_input_file ("linker stubs",
				   lang_input_file_is_fake_enum,
				   NULL);

  stub_file->the_bfd = bfd_create ("linker stubs", link_info.output_bfd);
  if (stub_file->the_bfd == NULL
      || !bfd_set_arch_mach (stub_file->the_bfd,
			     bfd_get_arch (link_info.output_bfd),
			     bfd_get_mach (link_info.output_bfd)))
    {
      einfo (_("%X%P: can not create stub BFD: %E\n"));
      return;
    }
  stub_file->the_bfd->flags |= BFD_LINKER_CREATED;

  /* Now we add the stub section.  */

  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
	   | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP);
  avr_stub_section = bfd_make_section_anyway_with_flags (stub_file->the_bfd,
							 ".trampolines",
							 flags);
  if (avr_stub_section == NULL)
    goto err_ret;

  avr_stub_section->alignment_power = 1;

  ldlang_add_file (stub_file);

  return;

 err_ret:
  einfo (_("%X%P: can not make stub section: %E\n"));
  return;
}

/* Re-calculates the size of the stubs so that we won't waste space.  */

static void
avr_elf_after_allocation (void)
{
  if (!avr_no_stubs && ! RELAXATION_ENABLED)
    {
      /* If relaxing, elf32_avr_size_stubs will be called from
	 elf32_avr_relax_section.  */
      if (!elf32_avr_size_stubs (link_info.output_bfd, &link_info, true))
	einfo (_("%X%P: can not size stub section: %E\n"));
    }

  gldavr25_after_allocation ();

  /* Now build the linker stubs.  */
  if (!avr_no_stubs)
    {
      if (!elf32_avr_build_stubs (&link_info))
	einfo (_("%X%P: can not build stubs: %E\n"));
    }
}

static void
avr_elf_before_parse (void)
{
  /* Don't create a demand-paged executable, since this feature isn't
     meaningful in AVR. */
  config.magic_demand_paged = false;

  gldavr25_before_parse ();
}

static void
avr_finish (void)
{
  bfd *abfd;
  bool avr_link_relax;

  if (bfd_link_relocatable (&link_info))
    {
      avr_link_relax = true;
      for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next)
	{
	  /* Don't let the linker stubs prevent the final object being
	     marked as link-relax ready.  */
	  if ((elf_elfheader (abfd)->e_flags
	       & EF_AVR_LINKRELAX_PREPARED) == 0
	      && abfd != stub_file->the_bfd)
	    {
	      avr_link_relax = false;
	      break;
	    }
	}
    }
  else
    {
      avr_link_relax = RELAXATION_ENABLED;
    }

  abfd = link_info.output_bfd;

  if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
    {
      if (avr_link_relax)
	elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
      else
	elf_elfheader (abfd)->e_flags &= ~EF_AVR_LINKRELAX_PREPARED;
    }

  ldelf_finish ();
}

static void
gldavr25_before_parse (void)
{
  ldfile_set_output_arch ("avr:25", bfd_arch_avr);
  input_flags.dynamic = true;
  config.has_shared = false;
  config.separate_code = false;
  link_info.check_relocs_after_open_input = true;
  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
gldavr25_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
gldavr25_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
gldavr25_before_allocation (void)
{
  ldelf_before_allocation (&audit, &depaudit, NULL);
}


static void
gldavr25_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 *
gldavr25_get_script (int *isfile)
{
  *isfile = 1;

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


static void
gldavr25_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},
    
  { "no-call-ret-replacement", no_argument,
    NULL, OPTION_NO_CALL_RET_REPLACEMENT},
  { "pmem-wrap-around", required_argument,
    NULL, OPTION_PMEM_WRAP_AROUND},
  { "no-stubs", no_argument,
    NULL, OPTION_NO_STUBS},
  { "debug-stubs", no_argument,
    NULL, OPTION_DEBUG_STUBS},
  { "debug-relax", no_argument,
    NULL, OPTION_DEBUG_RELAX},

    {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
gldavr25_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_PMEM_WRAP_AROUND:
      {
	/* This variable is defined in the bfd library.  */
	if ((!strcmp (optarg,"32k"))      || (!strcmp (optarg,"32K")))
	  avr_pc_wrap_around = 32768;
	else if ((!strcmp (optarg,"8k"))  || (!strcmp (optarg,"8K")))
	  avr_pc_wrap_around = 8192;
	else if ((!strcmp (optarg,"16k")) || (!strcmp (optarg,"16K")))
	  avr_pc_wrap_around = 16384;
	else if ((!strcmp (optarg,"64k")) || (!strcmp (optarg,"64K")))
	  avr_pc_wrap_around = 0x10000;
	else
	  return false;
      }
      break;

    case OPTION_DEBUG_STUBS:
      avr_debug_stubs = true;
      break;

    case OPTION_DEBUG_RELAX:
      avr_debug_relax = true;
      break;

    case OPTION_NO_STUBS:
      avr_no_stubs = true;
      break;

    case OPTION_NO_CALL_RET_REPLACEMENT:
      {
	/* This variable is defined in the bfd library.  */
	avr_replace_call_ret_sequences = false;
      }
      break;

    }

  return true;
}


static void
gldavr25_list_options (FILE * file)
{
 
  fprintf (file, _("  --pmem-wrap-around=<val>    "
		   "Make the linker relaxation machine assume that a\n"
		   "                              "
		   "  program counter wrap-around occurs at address\n"
		   "                              "
		   "  <val>.  Supported values: 8k, 16k, 32k and 64k.\n"));
  fprintf (file, _("  --no-call-ret-replacement   "
		   "The relaxation machine normally will\n"
		   "                              "
		   "  substitute two immediately following call/ret\n"
		   "                              "
		   "  instructions by a single jump instruction.\n"
		   "                              "
		   "  This option disables this optimization.\n"));
  fprintf (file, _("  --no-stubs                  "
		   "If the linker detects to attempt to access\n"
		   "                              "
		   "  an instruction beyond 128k by a reloc that\n"
		   "                              "
		   "  is limited to 128k max, it inserts a jump\n"
		   "                              "
		   "  stub. You can de-active this with this switch.\n"));
  fprintf (file, _("  --debug-stubs               "
		   "Used for debugging avr-ld.\n"));
  fprintf (file, _("  --debug-relax               "
		   "Used for debugging avr-ld.\n"));

}

struct ld_emulation_xfer_struct ld_avr25_emulation =
{
  avr_elf_before_parse,
  syslib_default,
  hll_default,
  ldelf_after_parse,
  gldavr25_before_plugin_all_symbols_read,
  gldavr25_after_open,
  after_check_relocs_default,
  ldelf_before_place_orphans,
  avr_elf_after_allocation,
  ldelf_set_output_arch,
  ldemul_default_target,
  avr_elf_avr25_before_allocation,
  gldavr25_get_script,
  "avr25",
  "elf32-avr",
  avr_finish,
  avr_elf_create_output_section_statements,
  ldelf_open_dynamic_archive,
  ldelf_place_orphan,
  NULL,
  NULL,
  gldavr25_add_options,
  gldavr25_handle_option,
  NULL,
  gldavr25_list_options,
  ldelf_load_symbols,
  NULL,
  NULL,
  NULL,
  ldelf_emit_ctf_early,
  ldelf_acquire_strings_for_ctf,
  ldelf_new_dynsym_for_ctf,
  NULL,
  NULL
};
