# This shell script emits a C file. -*- C -*-
# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# Contributed by Andes Technology Corporation.
#
# 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.
#

fragment <<EOF

#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.  */
 gld${EMULATION_NAME}_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.  */
 gld${EMULATION_NAME}_after_allocation ();
}

EOF
# Define some shell vars to insert bits of code into the standard elf
# parse_args and list_options functions.
#
PARSE_AND_LIST_LONGOPTS='
 { "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},
'
PARSE_AND_LIST_OPTIONS='
 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"));
'
PARSE_AND_LIST_ARGS_CASES='
 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;
'
LDEMUL_AFTER_OPEN=nds32_elf_after_open
LDEMUL_AFTER_PARSE=nds32_elf_after_parse
LDEMUL_AFTER_ALLOCATION=nds32_elf_after_allocation
LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=nds32_elf_create_output_section_statements