aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ld/ChangeLog27
-rw-r--r--ld/Makefile.in2
-rw-r--r--ld/emultempl/.Sanitize1
-rw-r--r--ld/emultempl/sunos.em564
-rw-r--r--ld/scripttempl/aout.sc44
5 files changed, 637 insertions, 1 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog
index a2a8373..3e28c9b 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,30 @@
+Thu Jun 2 17:24:08 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ Add support for SunOS shared libraries.
+ * aout.sc: Don't define __DYNAMIC here. Add new sections used by
+ shared library support code.
+ * emultempl/sunos.em: New file.
+ * emulparams/sun4.sh (TEMPLATE_NAME): Define as sunos.
+ * Makefile.in (esun4.c): Depend upon sunos.em, not generic.em.
+
+ * ldlang.c: Minor formatting cleanups.
+ (lang_for_each_input_file): New function.
+ * ldlang.h (lang_for_each_input_file): Declare.
+
+ * ldfile.h (search_dirs_type): Move from ldfile.c, and add cmdline
+ field.
+ (search_head): Declare.
+ (ldfile_add_library_path): Add new cmdline argument in prototype.
+ * ldfile.c (search_head): Make non-static.
+ (search_dirs_type): Move to ldfile.h.
+ (ldfile_add_library_path): Accept cmdline argument, and save it.
+ * lexsup.c (parse_args): Pass true for new cmdline argument of
+ ldfile_add_library_path.
+ (set_default_dirlist): Likewise.
+ * ldmain.c (check_for_scripts_dir): Pass false for new cmdline
+ argument of ldfile_add_library_path.
+ * ldgram.y (ifile_p1): Likewise.
+
Wed Jun 1 14:24:08 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
* ldlang.h (lang_input_statement_type): Remove fields subfiles,
diff --git a/ld/Makefile.in b/ld/Makefile.in
index e1c8701..a80cb4b 100644
--- a/ld/Makefile.in
+++ b/ld/Makefile.in
@@ -252,7 +252,7 @@ GENSCRIPTS = $(SHELL) $(srcdir)/genscripts.sh ${srcdir} ${libdir} ${host_alias}
GEN_DEPENDS = $(srcdir)/genscripts.sh $(srcdir)/emultempl/stringify.sed
esun4.c: $(srcdir)/emulparams/sun4.sh \
- $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS}
+ $(srcdir)/emultempl/sunos.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS}
${GENSCRIPTS} sun4
esun3.c: $(srcdir)/emulparams/sun3.sh \
$(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS}
diff --git a/ld/emultempl/.Sanitize b/ld/emultempl/.Sanitize
index 8cf22c1..b4bab8b 100644
--- a/ld/emultempl/.Sanitize
+++ b/ld/emultempl/.Sanitize
@@ -33,6 +33,7 @@ gld960c.em
hppaelf.em
lnk960.em
m88kbcs.em
+sunos.em
vanilla.em
Things-to-lose:
diff --git a/ld/emultempl/sunos.em b/ld/emultempl/sunos.em
new file mode 100644
index 0000000..55bcd6c
--- /dev/null
+++ b/ld/emultempl/sunos.em
@@ -0,0 +1,564 @@
+# This shell script emits a C file. -*- C -*-
+# It does some substitutions.
+cat >e${EMULATION_NAME}.c <<EOF
+/* This file is is generated by a shell script. DO NOT EDIT! */
+
+/* SunOS emulation code for ${EMULATION_NAME}
+ Copyright (C) 1991, 1993, 1994 Free Software Foundation, Inc.
+ Written by Steve Chamberlain <sac@cygnus.com>
+ SunOS shared library support by Ian Lance Taylor <ian@cygnus.com>
+
+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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define TARGET_IS_${EMULATION_NAME}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* FIXME: On some hosts we will need to include a different file.
+ This is correct for SunOS, which is the only place this file will
+ typically be compiled. However, if somebody configures the linker
+ for all targets, they will run into trouble here. */
+#include <dirent.h>
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "bfdlink.h"
+
+#include "ld.h"
+#include "config.h"
+#include "ldmain.h"
+#include "ldemul.h"
+#include "ldfile.h"
+#include "ldmisc.h"
+#include "ldexp.h"
+#include "ldlang.h"
+
+static void gld${EMULATION_NAME}_before_parse PARAMS ((void));
+static void gld${EMULATION_NAME}_create_output_section_statements
+ PARAMS ((void));
+static void gld${EMULATION_NAME}_find_so
+ PARAMS ((lang_input_statement_type *));
+static void gld${EMULATION_NAME}_before_allocation PARAMS ((void));
+static void gld${EMULATION_NAME}_find_statement_assignment
+ PARAMS ((lang_statement_union_type *));
+static void gld${EMULATION_NAME}_find_exp_assignment PARAMS ((etree_type *));
+static void gld${EMULATION_NAME}_count_need
+ PARAMS ((lang_input_statement_type *));
+static void gld${EMULATION_NAME}_set_need
+ PARAMS ((lang_input_statement_type *));
+static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile));
+
+static void
+gld${EMULATION_NAME}_before_parse()
+{
+ ldfile_output_architecture = bfd_arch_${ARCH};
+ config.dynamic_link = true;
+}
+
+/* Despite the name, we use this routine to search for dynamic
+ libraries. On SunOS this requires a directory search. We need to
+ find the .so file with the highest version number. The user may
+ restrict the major version by saying, e.g., -lc.1. Also, if we
+ find a .so file, we need to look for a the same file after
+ replacing .so with .sa; if it exists, it will be an archive which
+ provide some initializations for data symbols, and we need to
+ search it after including the .so file. */
+
+static void
+gld${EMULATION_NAME}_create_output_section_statements ()
+{
+ lang_for_each_input_file (gld${EMULATION_NAME}_find_so);
+}
+
+/* Search the directory for a .so file for each library search. */
+
+static void
+gld${EMULATION_NAME}_find_so (inp)
+ lang_input_statement_type *inp;
+{
+ search_dirs_type *search;
+ const char *filename;
+ const char *dot;
+ int force_maj;
+ unsigned int len;
+ char *alc;
+ int max_maj, max_min;
+ char *found;
+ struct stat st;
+
+ if (! inp->search_dirs_flag
+ || ! inp->is_archive)
+ return;
+
+ ASSERT (strncmp (inp->local_sym_name, "-l", 2) == 0);
+
+ filename = inp->filename;
+ force_maj = -1;
+ dot = strchr (filename, '.');
+ if (dot == NULL)
+ len = strlen (filename);
+ else
+ {
+ len = dot - filename;
+ alc = (char *) alloca (len + 1);
+ strncpy (alc, filename, len);
+ alc[len] = '\0';
+ filename = alc;
+ }
+
+ found = NULL;
+ max_maj = max_min = 0;
+ for (search = search_head; search != NULL; search = search->next)
+ {
+ DIR *dir;
+ struct dirent *entry;
+
+ dir = opendir (search->name);
+ if (dir == NULL)
+ continue;
+
+ while ((entry = readdir (dir)) != NULL)
+ {
+ int found_maj, found_min;
+
+ if (strncmp (entry->d_name, "lib", 3) != 0
+ || strncmp (entry->d_name + 3, inp->filename, len) != 0
+ || strncmp (entry->d_name + 3 + len, ".so", 3) != 0)
+ continue;
+
+ /* We've found a .so file. Work out the major and minor
+ version numbers. */
+ found_maj = 0;
+ found_min = 0;
+ sscanf (entry->d_name + 3 + len, ".so.%d.%d",
+ &found_maj, &found_min);
+
+ if (force_maj != -1 && force_maj != found_maj)
+ continue;
+
+ /* We've found a match for the name we are searching for.
+ See if this is the version we should use. If the major
+ and minor versions match, we use the last entry in
+ alphabetical order; I don't know if this is how SunOS
+ distinguishes libc.so.1.8 from libc.so.1.8.1, but it
+ ought to suffice. */
+ if (found == NULL
+ || (found_maj > max_maj)
+ || (found_maj == max_maj
+ && (found_min > max_min
+ || (found_min == max_min
+ && strcmp (entry->d_name, found) > 0))))
+ {
+ if (found != NULL)
+ free (found);
+ found = (char *) xmalloc (strlen (entry->d_name) + 1);
+ strcpy (found, entry->d_name);
+ max_maj = found_maj;
+ max_min = found_min;
+ }
+ }
+
+ closedir (dir);
+
+ if (found != NULL)
+ break;
+ }
+
+ if (found == NULL)
+ {
+ /* We did not find a matching .so file. This isn't an error,
+ since there might still be a matching .a file, which will be
+ found by the usual search. */
+ return;
+ }
+
+ /* Replace the filename with the one we have found. */
+ alc = (char *) xmalloc (strlen (search->name) + strlen (found) + 2);
+ sprintf (alc, "%s/%s", search->name, found);
+ inp->filename = alc;
+
+ /* Turn off the search_dirs_flag to prevent ldfile_open_file from
+ searching for this file again. */
+ inp->search_dirs_flag = false;
+
+ free (found);
+
+ /* Now look for the same file name, but with .sa instead of .so. If
+ found, add it to the list of input files. */
+ alc = (char *) alloca (strlen (inp->filename) + 1);
+ strcpy (alc, inp->filename);
+ strstr (alc, ".so.")[2] = 'a';
+ if (stat (alc, &st) == 0)
+ {
+ lang_input_statement_type *sa;
+ char *a;
+
+ /* Add the .sa file to the statement list just after the .so
+ file. This is really a hack. */
+ sa = ((lang_input_statement_type *)
+ xmalloc (sizeof (lang_input_statement_type)));
+ sa->header.next = inp->header.next;
+ sa->header.type = lang_input_statement_enum;
+ a = (char *) xmalloc (strlen (alc) + 1);
+ strcpy (a, alc);
+ sa->filename = a;
+ sa->local_sym_name = a;
+ sa->the_bfd = NULL;
+ sa->asymbols = NULL;
+ sa->symbol_count = 0;
+ sa->next = inp->next;
+ sa->next_real_file = inp->next_real_file;
+ sa->is_archive = false;
+ sa->search_dirs_flag = false;
+ sa->just_syms_flag = false;
+ sa->loaded = false;
+
+ inp->header.next = (lang_statement_union_type *) sa;
+ inp->next = (lang_statement_union_type *) sa;
+ inp->next_real_file = (lang_statement_union_type *) sa;
+ }
+}
+
+/* We need to use static variables to pass information around the call
+ to lang_for_each_input_file. Ick. */
+
+static bfd_size_type need_size;
+static bfd_size_type need_entries;
+static bfd_byte *need_contents;
+static bfd_byte *need_pinfo;
+static bfd_byte *need_pnames;
+
+/* The size of one entry in the .need section, not including the file
+ name. */
+
+#define NEED_ENTRY_SIZE (16)
+
+/* This is called after the sections have been attached to output
+ sections, but before any sizes or addresses have been set. */
+
+static void
+gld${EMULATION_NAME}_before_allocation ()
+{
+ struct bfd_link_hash_entry *h = NULL;
+ asection *sneed;
+ asection *srules;
+ asection *sdyn;
+
+ /* We need to create a __DYNAMIC symbol. We don't do this in the
+ linker script because we want to set the value to the start of
+ the dynamic section if there is one, or to zero if there isn't
+ one. We need to create the symbol before calling
+ size_dynamic_sections, although we can't set the value until
+ afterward. */
+ if (! link_info.relocateable)
+ {
+ h = bfd_link_hash_lookup (link_info.hash, "__DYNAMIC", true, false,
+ false);
+ if (h == NULL)
+ einfo ("%P%F: bfd_link_hash_lookup: %E\n");
+ if (! bfd_sunos_record_link_assignment (output_bfd, &link_info,
+ "__DYNAMIC"))
+ einfo ("%P%F: failed to record assignment to __DYNAMIC: %E\n");
+ }
+
+ /* If we are going to make any variable assignments, we need to let
+ the backend linker know about them in case the variables are
+ referred to by dynamic objects. */
+ lang_for_each_statement (gld${EMULATION_NAME}_find_statement_assignment);
+
+ /* Let the backend linker work out the sizes of any sections
+ required by dynamic linking. */
+ if (! bfd_sunos_size_dynamic_sections (output_bfd, &link_info, &sdyn,
+ &sneed, &srules))
+ einfo ("%P%F: failed to set dynamic section sizes: %E\n");
+
+ if (sneed != NULL)
+ {
+ /* Set up the .need section. See the description of the ld_need
+ field in include/sun4/aout.h. */
+
+ need_entries = 0;
+ need_size = 0;
+
+ lang_for_each_input_file (gld${EMULATION_NAME}_count_need);
+
+ /* We should only have a .need section if we have at least one
+ dynamic object. */
+ ASSERT (need_entries != 0);
+
+ sneed->_raw_size = need_size;
+ sneed->contents = (bfd_byte *) xmalloc (need_size);
+
+ need_contents = sneed->contents;
+ need_pinfo = sneed->contents;
+ need_pnames = sneed->contents + need_entries * 16;
+
+ lang_for_each_input_file (gld${EMULATION_NAME}_set_need);
+
+ ASSERT (need_pnames - sneed->contents == need_size);
+ }
+
+ if (srules != NULL)
+ {
+ unsigned int size;
+ search_dirs_type *search;
+
+ /* Set up the .rules section. This is just a PATH like string
+ of the -L arguments given on the command line. */
+ size = 0;
+ for (search = search_head; search != NULL; search = search->next)
+ if (search->cmdline)
+ size += strlen (search->name) + 1;
+ srules->_raw_size = size;
+ if (size > 0)
+ {
+ char *p;
+
+ srules->contents = (bfd_byte *) xmalloc (size);
+ p = (char *) srules->contents;
+ *p = '\0';
+ for (search = search_head; search != NULL; search = search->next)
+ {
+ if (search->cmdline)
+ {
+ if (p != (char *) srules->contents)
+ *p++ = ':';
+ strcpy (p, search->name);
+ p += strlen (p);
+ }
+ }
+ }
+ }
+
+ /* We must assign a value to __DYNAMIC. It should be zero if we are
+ not doing a dynamic link, or the start of the .dynamic section if
+ we are doing one. */
+ if (! link_info.relocateable)
+ {
+ h->type = bfd_link_hash_defined;
+ h->u.def.value = 0;
+ if (sdyn != NULL)
+ h->u.def.section = sdyn;
+ else
+ h->u.def.section = &bfd_abs_section;
+ }
+}
+
+/* This is called by the before_allocation routine via
+ lang_for_each_statement. It locates any assignment statements, and
+ tells the backend linker about them, in case they are assignments
+ to symbols which are referred to by dynamic objects. */
+
+static void
+gld${EMULATION_NAME}_find_statement_assignment (s)
+ lang_statement_union_type *s;
+{
+ if (s->header.type == lang_assignment_statement_enum)
+ gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp);
+}
+
+/* Look through an expression for an assignment statement. */
+
+static void
+gld${EMULATION_NAME}_find_exp_assignment (exp)
+ etree_type *exp;
+{
+ switch (exp->type.node_class)
+ {
+ case etree_assign:
+ if (strcmp (exp->assign.dst, ".") != 0)
+ {
+ if (! bfd_sunos_record_link_assignment (output_bfd, &link_info,
+ exp->assign.dst))
+ einfo ("%P%F: failed to record assignment to %s: %E\n",
+ exp->assign.dst);
+ }
+ gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src);
+ break;
+
+ case etree_binary:
+ gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs);
+ gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs);
+ break;
+
+ case etree_trinary:
+ gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs);
+ gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs);
+ gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs);
+ break;
+
+ case etree_unary:
+ gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Work out the size of the .need section, and the number of entries.
+ The backend will set the ld_need field of the dynamic linking
+ information to point to the .need section. See include/aout/sun4.h
+ for more information. */
+
+static void
+gld${EMULATION_NAME}_count_need (inp)
+ lang_input_statement_type *inp;
+{
+ if (inp->the_bfd != NULL
+ && (inp->the_bfd->flags & DYNAMIC) != 0)
+ {
+ ++need_entries;
+ need_size += NEED_ENTRY_SIZE;
+ if (! inp->is_archive)
+ need_size += strlen (inp->filename) + 1;
+ else
+ {
+ ASSERT (inp->local_sym_name[0] == '-'
+ && inp->local_sym_name[1] == 'l');
+ need_size += strlen (inp->local_sym_name + 2) + 1;
+ }
+ }
+}
+
+/* Fill in the contents of the .need section. */
+
+static void
+gld${EMULATION_NAME}_set_need (inp)
+ lang_input_statement_type *inp;
+{
+ if (inp->the_bfd != NULL
+ && (inp->the_bfd->flags & DYNAMIC) != 0)
+ {
+ bfd_size_type c;
+
+ /* To really fill in the .need section contents, we need to know
+ the final file position of the section, but we don't.
+ Instead, we use offsets, and rely on the BFD backend to
+ finish the section up correctly. FIXME: Talk about lack of
+ referential locality. */
+ bfd_put_32 (output_bfd, need_pnames - need_contents, need_pinfo);
+ if (! inp->is_archive)
+ {
+ bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 4);
+ bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 8);
+ bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 10);
+ strcpy (need_pnames, inp->filename);
+ }
+ else
+ {
+ char *verstr;
+ int maj, min;
+
+ bfd_put_32 (output_bfd, (bfd_vma) 0x80000000, need_pinfo + 4);
+ maj = 0;
+ min = 0;
+ verstr = strstr (inp->filename, ".so.");
+ if (verstr != NULL)
+ sscanf (verstr, ".so.%d.%d", &maj, &min);
+ bfd_put_16 (output_bfd, (bfd_vma) maj, need_pinfo + 8);
+ bfd_put_16 (output_bfd, (bfd_vma) min, need_pinfo + 10);
+ strcpy (need_pnames, inp->local_sym_name + 2);
+ }
+
+ c = (need_pinfo - need_contents) / NEED_ENTRY_SIZE;
+ if (c + 1 >= need_entries)
+ bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 12);
+ else
+ bfd_put_32 (output_bfd, (bfd_vma) (c + 1) * NEED_ENTRY_SIZE,
+ need_pinfo + 12);
+
+ need_pinfo += NEED_ENTRY_SIZE;
+ need_pnames += strlen (need_pnames) + 1;
+ }
+}
+
+static char *
+gld${EMULATION_NAME}_get_script(isfile)
+ int *isfile;
+EOF
+
+if test -n "$COMPILE_IN"
+then
+# Scripts compiled in.
+
+# sed commands to quote an ld script as a C string.
+sc='s/["\\]/\\&/g
+s/$/\\n\\/
+1s/^/"/
+$s/$/n"/
+'
+
+cat >>e${EMULATION_NAME}.c <<EOF
+{
+ *isfile = 0;
+
+ if (link_info.relocateable == true && config.build_constructors == true)
+ return `sed "$sc" ldscripts/${EMULATION_NAME}.xu`;
+ else if (link_info.relocateable == true)
+ return `sed "$sc" ldscripts/${EMULATION_NAME}.xr`;
+ else if (!config.text_read_only)
+ return `sed "$sc" ldscripts/${EMULATION_NAME}.xbn`;
+ else if (!config.magic_demand_paged)
+ return `sed "$sc" ldscripts/${EMULATION_NAME}.xn`;
+ else
+ return `sed "$sc" ldscripts/${EMULATION_NAME}.x`;
+}
+EOF
+
+else
+# Scripts read from the filesystem.
+
+cat >>e${EMULATION_NAME}.c <<EOF
+{
+ *isfile = 1;
+
+ if (link_info.relocateable == true && config.build_constructors == true)
+ return "ldscripts/${EMULATION_NAME}.xu";
+ else if (link_info.relocateable == true)
+ return "ldscripts/${EMULATION_NAME}.xr";
+ else if (!config.text_read_only)
+ return "ldscripts/${EMULATION_NAME}.xbn";
+ else if (!config.magic_demand_paged)
+ return "ldscripts/${EMULATION_NAME}.xn";
+ else
+ return "ldscripts/${EMULATION_NAME}.x";
+}
+EOF
+
+fi
+
+cat >>e${EMULATION_NAME}.c <<EOF
+
+struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
+{
+ gld${EMULATION_NAME}_before_parse,
+ syslib_default,
+ hll_default,
+ after_parse_default,
+ after_allocation_default,
+ set_output_arch_default,
+ ldemul_default_target,
+ gld${EMULATION_NAME}_before_allocation,
+ gld${EMULATION_NAME}_get_script,
+ "${EMULATION_NAME}",
+ "${OUTPUT_FORMAT}",
+ 0, /* finish */
+ gld${EMULATION_NAME}_create_output_section_statements
+};
+EOF
diff --git a/ld/scripttempl/aout.sc b/ld/scripttempl/aout.sc
new file mode 100644
index 0000000..e75764a
--- /dev/null
+++ b/ld/scripttempl/aout.sc
@@ -0,0 +1,44 @@
+cat <<EOF
+OUTPUT_FORMAT("${OUTPUT_FORMAT}")
+OUTPUT_ARCH(${ARCH})
+
+${RELOCATING+${LIB_SEARCH_DIRS}}
+${STACKZERO+${RELOCATING+${STACKZERO}}}
+${SHLIB_PATH+${RELOCATING+${SHLIB_PATH}}}
+SECTIONS
+{
+ .text ${RELOCATING+${TEXT_START_ADDR}}:
+ {
+ CREATE_OBJECT_SYMBOLS
+ *(.text)
+ /* The next six sections are for SunOS dynamic linking. The order
+ is important. */
+ *(.dynrel)
+ *(.hash)
+ *(.dynsym)
+ *(.dynstr)
+ *(.rules)
+ *(.need)
+ ${PAD_TEXT+${RELOCATING+. = ${DATA_ALIGNMENT};}}
+ ${RELOCATING+_etext = ${DATA_ALIGNMENT};}
+ }
+ .data ${RELOCATING+${DATA_ALIGNMENT}} :
+ {
+ /* The first three sections are for SunOS dynamic linking. */
+ *(.dynamic)
+ *(.got)
+ *(.plt)
+ *(.data)
+ ${CONSTRUCTING+CONSTRUCTORS}
+ ${RELOCATING+_edata = .;}
+ }
+ .bss ${RELOCATING+SIZEOF(.data) + ADDR(.data)} :
+ {
+ ${RELOCATING+ __bss_start = .};
+ *(.bss)
+ *(COMMON)
+ ${RELOCATING+_end = ALIGN(4) };
+ ${RELOCATING+__end = ALIGN(4) };
+ }
+}
+EOF