# This shell script emits a C file. -*- C -*-
#   Copyright (C) 2023-2025 Free Software Foundation, Inc.
#
# This file is part of GLD, the Gnu Linker.
#
# 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 2 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.
#

# This file is sourced from elf.em, and defines extra Neutrino
# specific routines.

# NTO templates aims to refine the default ${ARCH}elf.em template.
source_em "${srcdir}/emultempl/${ARCH}elf.em"

cat >>e${EMULATION_NAME}.c <<EOF

#include "elf/internal.h"
#include "elf/common.h"
#include "elf-bfd.h"
#include "../bfd/libbfd.h"

bool nto_lazy_stack = false;
struct nto_stack_note
{
 unsigned char stacksize[4];
 unsigned char stackalloc[4];
 unsigned char execstack[4];
};

static asection*
nto_create_QNX_note_section(int type)
{
 asection *note_sec;
 flagword flags;
 Elf_External_Note *e_note;
 bfd_size_type size;

 /* As ${ARCH}elf.em is imported and ${ARCH}_elf_create_output_section_statements
    is called before this function, stub_file should already be defined.  */
 if (!stub_file)
   {
     fatal (_("%P: cannot create .note section in stub BFD.\n"));
     return NULL;
   }

 flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
          | SEC_IN_MEMORY | SEC_LINKER_CREATED);
 note_sec = bfd_make_section_anyway_with_flags (stub_file->the_bfd, ".note", flags);
 if (! note_sec)
   {
     fatal (_("%P: failed to create .note section\n"));
     return NULL;
   }

 size = offsetof (Elf_External_Note, name[sizeof "QNX"]);
 size = (size + 3) & -(bfd_size_type) 4;
 size += sizeof (struct nto_stack_note);
 note_sec->size = size;

 elf_section_type (note_sec) = SHT_NOTE;
 note_sec->contents = xmalloc (note_sec->size);
 e_note = (Elf_External_Note *) note_sec->contents;
 bfd_h_put_32 (stub_file->the_bfd, sizeof "QNX", &e_note->namesz);
 bfd_h_put_32 (stub_file->the_bfd, sizeof (struct nto_stack_note), &e_note->descsz);
 bfd_h_put_32 (stub_file->the_bfd, type, &e_note->type);
 memcpy (e_note->name, "QNX", sizeof "QNX");

 return note_sec;
}

/* Lookup for a section holding a QNX note or create a new section.  */
static asection*
nto_lookup_QNX_note_section(int type)
{
 asection *stack_note_sec = NULL;
 bfd *abfd;
 bool duplicated_notes_detected = false;
 for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next)
   {
     Elf_External_Note *e_note;
     asection *sec;

     /* QNX notes are held under a note section simply named ".note".  */
     sec = bfd_get_section_by_name (abfd, ".note");
     if (!sec)
       continue;

     /* Verify that this is a QNX note of the expected type.  */
     sec->contents = xmalloc(sec->size);
     if (!bfd_get_section_contents (sec->owner, sec, sec->contents, (file_ptr) 0,
                                    sec->size))
       fatal (_("%P: %pB: can't read contents of section .note: %E\n"),
              sec->owner);

     e_note = (Elf_External_Note *) sec->contents;
     if (! strcmp("QNX", e_note->name) && *e_note->type == type)
       {
         if (stack_note_sec)
           {
             if (!duplicated_notes_detected)
               {
                 einfo (_("%P: %pB: warning: duplicated QNX stack .note detected\n"),
                        stack_note_sec->owner);
                 duplicated_notes_detected = true;
               }
             einfo (_("%P: %pB: warning: duplicated QNX stack .note detected\n"),
                    sec->owner);
           }
         else
           {
             stack_note_sec = sec;
             /* Allow modification of this .note content.  */
             stack_note_sec->flags |= SEC_IN_MEMORY;
           }
       }
   }

 if (stack_note_sec)
   return stack_note_sec;
 else
   return nto_create_QNX_note_section(type);

}

/* Generate the QNX stack .note section.  */
static void
nto_add_note_section (void) {
 asection *note_sec;
 struct nto_stack_note *n_note;
 bfd_size_type h_size;
 bool is_update = false;

 if (nto_lazy_stack && !link_info.stacksize)
   {
     fatal (_("%P: error: --lazy-stack must follow -zstack-size=<size>\n"));
     return;
   }

 /* Don't create a note if none of the stack parameter have to be modified.  */
 if (link_info.stacksize <= 0 && (link_info.execstack == link_info.noexecstack))
   return;

 note_sec = nto_lookup_QNX_note_section(QNT_STACK);
 if (! note_sec)
   return;

 /* Update QNX stack note content.  */
 h_size = note_sec->size - sizeof(struct nto_stack_note);
 n_note = (struct nto_stack_note *) (note_sec->contents + h_size);
 is_update = note_sec->owner != stub_file->the_bfd;

 if (link_info.stacksize > 0)
   bfd_h_put_32 (note_sec->owner, link_info.stacksize, &n_note->stacksize);
 else if (!is_update)
   bfd_h_put_32 (note_sec->owner, 0, &n_note->stacksize);

 if (nto_lazy_stack || (!is_update && link_info.stacksize <= 0))
   bfd_h_put_32 (note_sec->owner, 4096, &n_note->stackalloc);
 else if (link_info.stacksize > 0)
   bfd_h_put_32 (note_sec->owner, link_info.stacksize, &n_note->stackalloc);

 if (link_info.execstack)
   bfd_h_put_32 (note_sec->owner, 0, &n_note->execstack);
 else if (!is_update || link_info.noexecstack)
   bfd_h_put_32 (note_sec->owner, 1, &n_note->execstack);
}

static void
nto_after_open (void)
{
 nto_add_note_section();
 gld${EMULATION_NAME}_after_open ();
}

EOF

# Define some shell vars to insert bits of code into the standard elf
# parse_args and list_options functions.
#

PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
 { "stack", required_argument, NULL, OPTION_STACK },
 { "lazy-stack", no_argument, NULL, OPTION_LAZY_STACK },
'

PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
 fprintf (file, _("\
 --stack <size>              Set size of the initial stack\n\
 --lazy-stack                Set lazy allocation of stack\n\
"));
'

PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
   case OPTION_STACK:
     {
       char *end;
       link_info.stacksize = strtoul (optarg, &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;
       break;
     }
   case OPTION_LAZY_STACK:
     nto_lazy_stack = true;
     break;
'

# Put these extra Neutrino routines in ld_${EMULATION_NAME}_emulation
#

LDEMUL_AFTER_OPEN=nto_after_open