Add support for SunOS shared libraries.
authorIan Lance Taylor <ian@airs.com>
Thu, 2 Jun 1994 22:01:53 +0000 (22:01 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 2 Jun 1994 22:01:53 +0000 (22:01 +0000)
* 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.

ld/ChangeLog
ld/Makefile.in
ld/emultempl/.Sanitize
ld/emultempl/sunos.em [new file with mode: 0644]
ld/scripttempl/aout.sc [new file with mode: 0644]

index a2a8373..3e28c9b 100644 (file)
@@ -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,
index e1c8701..a80cb4b 100644 (file)
@@ -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}
index 8cf22c1..b4bab8b 100644 (file)
@@ -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 (file)
index 0000000..55bcd6c
--- /dev/null
@@ -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 (file)
index 0000000..e75764a
--- /dev/null
@@ -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