* libnlm.h (nlm_backend_data): Added nlm_mangle_relocs.
authorIan Lance Taylor <ian@airs.com>
Wed, 4 Aug 1993 20:25:53 +0000 (20:25 +0000)
committerIan Lance Taylor <ian@airs.com>
Wed, 4 Aug 1993 20:25:53 +0000 (20:25 +0000)
(nlm_mangle_relocs_func): New macro.
* nlm32-i386.c (nlm_i386_write_reloc): Rewrote correctly.
(nlm_i386_mangle_relocs): New function.
* nlmcode.h (nlm_compute_section_file_positions): Move all common
symbols into the .bss section.
(nlm_set_section_contents): Call the mangle_relocs function.
(nlm_write_object_contents): Treat a reloc against any defined
symbol as an internal reloc.  Fix bug in external reloc counting.
Get the offset and debugging type right for .bss symbols.  Only
output debugging symbols for defined symbols.

bfd/ChangeLog
bfd/libnlm.h
bfd/nlm32-i386.c [new file with mode: 0644]
bfd/nlmcode.h

index dfebb05..dc37416 100644 (file)
@@ -1,5 +1,17 @@
 Wed Aug  4 08:33:55 1993  Ian Lance Taylor  (ian@cygnus.com)
 
+       * libnlm.h (nlm_backend_data): Added nlm_mangle_relocs.
+       (nlm_mangle_relocs_func): New macro.
+       * nlm32-i386.c (nlm_i386_write_reloc): Rewrote correctly.
+       (nlm_i386_mangle_relocs): New function.
+       * nlmcode.h (nlm_compute_section_file_positions): Move all common
+       symbols into the .bss section.
+       (nlm_set_section_contents): Call the mangle_relocs function.
+       (nlm_write_object_contents): Treat a reloc against any defined
+       symbol as an internal reloc.  Fix bug in external reloc counting.
+       Get the offset and debugging type right for .bss symbols.  Only
+       output debugging symbols for defined symbols.
+
        * coff-h8500.c (rtype2howto): Do an fprintf to stderr rather than
        using printf.
        * coff-z8k.c (rtype2howto): Likewise.
index eb8d8f5..53199bc 100644 (file)
@@ -21,52 +21,63 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifndef _LIBNLM_H_
 #define _LIBNLM_H_ 1
 
+#ifdef ARCH_SIZE
+#  define NLM_ARCH_SIZE ARCH_SIZE
+#endif
 #include "nlm/common.h"
 #include "nlm/internal.h"
 #include "nlm/external.h"
 
-/* If size isn't specified as 64 or 32, NAME macro should fail.  */
-#ifndef NAME
-#  if ARCH_SIZE==64
-#    define NAME(x,y) CAT4(x,64,_,y)
-#  endif
-#  if ARCH_SIZE==32
-#    define NAME(x,y) CAT4(x,32,_,y)
-#  endif
-#endif
-
-#define NlmNAME(X)     NAME(Nlm,X)
-#define nlmNAME(X)     NAME(nlm,X)
+/* A reloc for an imported NLM symbol.  Normal relocs are associated
+   with sections, and include a symbol.  These relocs are associated
+   with (undefined) symbols, and include a section.  */
 
-typedef struct
+struct nlm_relent
 {
-  asymbol symbol;
-} nlm32_symbol_type;
+  /* Section of reloc.  */
+  asection *section;
+  /* Reloc info (sym_ptr_ptr field set only when canonicalized).  */
+  arelent reloc;
+};
+
+/* Information we keep for an NLM symbol.  */
 
 typedef struct
 {
+  /* BFD symbol.  */
   asymbol symbol;
-} nlm64_symbol_type;
-
-#define bfd_nlm32_mkobject             bfd_nlm_mkobject
-#define bfd_nlm64_mkobject             bfd_nlm_mkobject
-#define nlm_mkobject                   bfd_nlm_mkobject
-extern boolean bfd_nlm_mkobject PARAMS ((bfd *));
-
-extern void bfd_nlm32_get_symbol_info
-  PARAMS ((bfd *, asymbol *, symbol_info *));
-extern unsigned int bfd_nlm32_get_symtab_upper_bound PARAMS ((bfd *));
-extern unsigned int bfd_nlm64_get_symtab_upper_bound PARAMS ((bfd *));
-extern unsigned int bfd_nlm32_get_symtab PARAMS ((bfd *, asymbol **));
-extern unsigned int bfd_nlm64_get_symtab PARAMS ((bfd *, asymbol **));
-extern asymbol *bfd_nlm32_make_empty_symbol PARAMS ((bfd *));
-extern asymbol *bfd_nlm64_make_empty_symbol PARAMS ((bfd *));
-extern bfd_target *bfd_nlm32_object_p PARAMS ((bfd *));
-extern bfd_target *bfd_nlm64_object_p PARAMS ((bfd *));
-extern boolean bfd_nlm32_set_arch_mach
-  PARAMS ((bfd *, enum bfd_architecture, unsigned long));
-extern boolean bfd_nlm64_set_arch_mach
-  PARAMS ((bfd *, enum bfd_architecture, unsigned long));
+  /* Number of reloc entries for imported symbol.  */
+  bfd_size_type rcnt;
+  /* Array of reloc information for imported symbol.  */
+  struct nlm_relent *relocs;
+} nlmNAME(symbol_type);
+
+extern boolean nlm_mkobject PARAMS ((bfd *));
+extern boolean nlm_set_arch_mach PARAMS ((bfd *, enum bfd_architecture,
+                                         unsigned long));
+
+extern void nlmNAME(get_symbol_info)
+     PARAMS ((bfd *, asymbol *, symbol_info *));
+extern unsigned int nlmNAME(get_symtab_upper_bound)
+     PARAMS ((bfd *));
+extern unsigned int nlmNAME(get_symtab)
+     PARAMS ((bfd *, asymbol **));
+extern asymbol *nlmNAME(make_empty_symbol)
+     PARAMS ((bfd *));
+extern void nlmNAME(print_symbol)
+     PARAMS ((bfd *, PTR, asymbol *, bfd_print_symbol_type));
+extern unsigned int nlmNAME(get_reloc_upper_bound)
+     PARAMS ((bfd *, asection *));
+extern unsigned int nlmNAME(canonicalize_reloc)
+     PARAMS ((bfd *, asection *, arelent **, asymbol **));
+extern bfd_target *nlmNAME(object_p)
+     PARAMS ((bfd *));
+extern boolean nlmNAME(set_arch_mach)
+     PARAMS ((bfd *, enum bfd_architecture, unsigned long));
+extern boolean nlmNAME(set_section_contents)
+     PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type));
+extern boolean nlmNAME(write_object_contents)
+     PARAMS ((bfd *));
 
 /* Some private data is stashed away for future use using the tdata pointer
    in the bfd structure.  */
@@ -80,6 +91,14 @@ struct nlm_obj_tdata
   Nlm_Internal_Copyright_Header        nlm_copyright_hdr[1];
   Nlm_Internal_Extended_Header nlm_extended_hdr[1];
   Nlm_Internal_Custom_Header   nlm_custom_hdr[1];
+  /* BFD NLM symbols.  */
+  nlmNAME(symbol_type)         *nlm_symbols;
+  /* Lowest text and data VMA values.  */
+  bfd_vma                      nlm_text_low;
+  bfd_vma                      nlm_data_low;
+  /* Caches for data read from object file.  */
+  arelent *                    nlm_reloc_fixups;
+  asection **                  nlm_reloc_fixup_secs;
 };
 
 #define nlm_tdata(bfd)                 ((bfd) -> tdata.nlm_obj_data)
@@ -89,6 +108,51 @@ struct nlm_obj_tdata
 #define nlm_copyright_header(bfd)      (nlm_tdata(bfd) -> nlm_copyright_hdr)
 #define nlm_extended_header(bfd)       (nlm_tdata(bfd) -> nlm_extended_hdr)
 #define nlm_custom_header(bfd)         (nlm_tdata(bfd) -> nlm_custom_hdr)
+#define nlm_get_symbols(bfd)           (nlm_tdata(bfd) -> nlm_symbols)
+#define nlm_set_symbols(bfd, p)                (nlm_tdata(bfd) -> nlm_symbols = (p))
+#define nlm_set_text_low(bfd, i)       (nlm_tdata(bfd) -> nlm_text_low = (i))
+#define nlm_get_text_low(bfd)          (nlm_tdata(bfd) -> nlm_text_low)
+#define nlm_set_data_low(bfd, i)       (nlm_tdata(bfd) -> nlm_data_low = (i))
+#define nlm_get_data_low(bfd)          (nlm_tdata(bfd) -> nlm_data_low)
+#define nlm_relocation_fixups(bfd)     (nlm_tdata(bfd) -> nlm_reloc_fixups)
+#define nlm_relocation_fixup_secs(bfd) (nlm_tdata(bfd)->nlm_reloc_fixup_secs)
+
+/* We store some function pointer in the backend structure.  This lets
+   different NLM targets share most of the same code, while providing
+   slightly different code where necessary.  */
+
+struct nlm_backend_data
+{
+  /* Machine architecture.  */
+  enum bfd_architecture arch;
+  /* Read a relocation fixup from abfd.  The reloc information is
+     machine specific.  The second argument is the symbol if this is
+     an import, or NULL if this is a reloc fixup.  This function
+     should set the third argument to the section which the reloc
+     belongs in, and the fourth argument to the reloc itself; it does
+     not need to fill in the sym_ptr_ptr field for a reloc against an
+     import symbol.  */
+  boolean (*nlm_read_reloc) PARAMS ((bfd *, nlmNAME(symbol_type) *,
+                                    asection **, arelent *));
+  /* Write a relocation fixup to abfd.  */
+  boolean (*nlm_write_reloc) PARAMS ((bfd *, asection *, arelent *));
+  /* To make objcopy to an i386 NLM work, the i386 backend needs a
+     chance to work over the relocs.  This is a bit icky.  */
+  boolean (*nlm_mangle_relocs) PARAMS ((bfd *, asection *, PTR data,
+                                       bfd_vma offset,
+                                       bfd_size_type count));
+};
+
+#define nlm_backend(bfd) \
+  ((struct nlm_backend_data *)((bfd) -> xvec -> backend_data))
+#define nlm_architecture(bfd) \
+  (nlm_backend(bfd) ? nlm_backend(bfd) -> arch : bfd_arch_unknown)
+#define nlm_read_reloc_func(bfd) \
+  (nlm_backend(bfd) ? nlm_backend(bfd) -> nlm_read_reloc : 0)
+#define nlm_write_reloc_func(bfd) \
+  (nlm_backend(bfd) ? nlm_backend(bfd) -> nlm_write_reloc : 0)
+#define nlm_mangle_relocs_func(bfd) \
+  (nlm_backend(bfd) ? nlm_backend(bfd) -> nlm_mangle_relocs : 0)
 
 /* The NLM code, data, and uninitialized sections have no names defined
    in the NLM, but bfd wants to give them names, so use the traditional
diff --git a/bfd/nlm32-i386.c b/bfd/nlm32-i386.c
new file mode 100644 (file)
index 0000000..20328b1
--- /dev/null
@@ -0,0 +1,354 @@
+/* Support for 32-bit i386 NLM (NetWare Loadable Module)
+   Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+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.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+#define ARCH_SIZE 32
+#include "libnlm.h"
+
+static boolean nlm_i386_read_reloc
+  PARAMS ((bfd *, nlmNAME(symbol_type) *, asection **, arelent *));
+static boolean nlm_i386_write_reloc
+  PARAMS ((bfd *, asection *, arelent *));
+static boolean nlm_i386_mangle_relocs
+  PARAMS ((bfd *, asection *, PTR, bfd_vma, bfd_size_type));
+
+/* Adjust the reloc location by an absolute value.  */
+
+static reloc_howto_type nlm_i386_abs_howto =
+  HOWTO (0,                    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "32",                  /* name */
+        true,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        false);                /* pcrel_offset */
+
+/* Adjust the reloc location by a PC relative displacement.  */
+
+static reloc_howto_type nlm_i386_pcrel_howto =
+  HOWTO (1,                    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        true,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DISP32",              /* name */
+        true,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        true);                 /* pcrel_offset */
+
+/* Read a NetWare i386 reloc.  */
+
+static boolean
+nlm_i386_read_reloc (abfd, sym, secp, rel)
+     bfd *abfd;
+     nlmNAME(symbol_type) *sym;
+     asection **secp;
+     arelent *rel;
+{
+  bfd_byte temp[4];
+  bfd_vma val;
+  const char *name;
+
+  if (bfd_read (temp, sizeof (temp), 1, abfd) != sizeof (temp))
+    {
+      bfd_error = system_call_error;
+      return false;
+    }
+
+  val = bfd_get_32 (abfd, temp);
+
+  /* The value is an offset into either the code or data segment.
+     This is the location which needs to be adjusted.
+
+     If this is a relocation fixup rather than an imported symbol (the
+     sym argument is NULL) then the high bit is 0 if the location
+     needs to be adjusted by the address of the data segment, or 1 if
+     the location needs to be adjusted by the address of the code
+     segment.  If this is an imported symbol, then the high bit is 0
+     if the location is 0 if the location should be adjusted by the
+     offset to the symbol, or 1 if the location should adjusted by the
+     absolute value of the symbol.
+
+     The second most significant bit is 0 if the value is an offset
+     into the data segment, or 1 if the value is an offset into the
+     code segment.
+
+     All this translates fairly easily into a BFD reloc.  */
+
+  if (sym == NULL)
+    {
+      if ((val & NLM_HIBIT) == 0)
+       name = NLM_INITIALIZED_DATA_NAME;
+      else
+       {
+         name = NLM_CODE_NAME;
+         val &=~ NLM_HIBIT;
+       }
+      rel->sym_ptr_ptr = bfd_get_section_by_name (abfd, name)->symbol_ptr_ptr;
+      rel->howto = &nlm_i386_abs_howto;
+    }
+  else
+    {
+      /* In this case we do not need to set the sym_ptr_ptr field.  */
+      rel->sym_ptr_ptr = NULL;
+      if ((val & NLM_HIBIT) == 0)
+       rel->howto = &nlm_i386_pcrel_howto;
+      else
+       {
+         rel->howto = &nlm_i386_abs_howto;
+         val &=~ NLM_HIBIT;
+       }
+    }
+
+  if ((val & (NLM_HIBIT >> 1)) == 0)
+    *secp = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
+  else
+    {
+      *secp = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
+      val &=~ (NLM_HIBIT >> 1);
+    }
+
+  rel->address = val;
+  rel->addend = 0;
+
+  return true;
+}
+
+/* Write a NetWare i386 reloc.  */
+
+static boolean
+nlm_i386_write_reloc (abfd, sec, rel)
+     bfd *abfd;
+     asection *sec;
+     arelent *rel;
+{
+  asymbol *sym;
+  bfd_vma val;
+  bfd_byte temp[4];
+
+  /* NetWare only supports two kinds of relocs.  We should check
+     special_function here, as well, but at the moment coff-i386
+     relocs uses a special_function which does not affect what we do
+     here.  */
+  if (rel->addend != 0
+      || rel->howto == NULL
+      || rel->howto->rightshift != 0
+      || rel->howto->size != 2
+      || rel->howto->bitsize != 32
+      || rel->howto->bitpos != 0
+      || ! rel->howto->partial_inplace
+      || rel->howto->src_mask != 0xffffffff
+      || rel->howto->dst_mask != 0xffffffff)
+    {
+      bfd_error = invalid_operation;
+      return false;
+    }
+
+  sym = *rel->sym_ptr_ptr;
+
+  /* The value we write out is the offset into the appropriate
+     segment.  This offset is the section vma, adjusted by the vma of
+     the lowest section in that segment, plus the address of the
+     relocation.  */
+  val = bfd_get_section_vma (abfd, sec) + rel->address;
+
+  /* The second most significant bit is 0 if the value is an offset
+     into the data segment, or 1 if the value is an offset into the
+     code segment.  */
+  if (bfd_get_section_flags (abfd, sec) & SEC_CODE)
+    {
+      val -= nlm_get_text_low (abfd);
+      val |= NLM_HIBIT >> 1;
+    }
+  else
+    val -= nlm_get_data_low (abfd);
+
+  if (bfd_get_section (sym) != &bfd_und_section)
+    {
+      /* NetWare only supports absolute internal relocs.  */
+      if (rel->howto->pc_relative)
+       {
+         bfd_error = invalid_operation;
+         return false;
+       }
+
+      /* The high bit is 1 if the reloc is against the code section, 0
+        if against the data section.  */
+      if (bfd_get_section_flags (abfd, bfd_get_section (sym)) & SEC_CODE)
+       val |= NLM_HIBIT;
+    }
+  else
+    {
+      /* The high bit is 1 if this is an absolute reloc, 0 if it is PC
+        relative.  */
+      if (! rel->howto->pc_relative)
+       val |= NLM_HIBIT;
+      else
+       {
+         /* PC relative relocs on NetWare must be pcrel_offset.  */
+         if (! rel->howto->pcrel_offset)
+           {
+             bfd_error = invalid_operation;
+             return false;
+           }
+       }
+    }
+  
+  bfd_put_32 (abfd, val, temp);
+  if (bfd_write (temp, sizeof (temp), 1, abfd) != sizeof (temp))
+    {
+      bfd_error = system_call_error;
+      return false;
+    }
+
+  return true;
+}
+
+/* I want to be able to use objcopy to turn a i386 a.out or COFF file
+   into a NetWare i386 module.  That means that the relocs from the
+   source file have to be mapped into relocs that apply to the target
+   file.  This function is called by nlm_set_section_contents to give
+   it a chance to rework the relocs.
+
+   This is actually a fairly general concept.  However, this is not a
+   general implementation.  */
+
+static boolean
+nlm_i386_mangle_relocs (abfd, sec, data, offset, count)
+     bfd *abfd;
+     asection *sec;
+     PTR data;
+     bfd_vma offset;
+     bfd_size_type count;
+{
+  arelent **rel_ptr_ptr, **rel_end;
+
+  rel_ptr_ptr = sec->orelocation;
+  rel_end = rel_ptr_ptr + sec->reloc_count;
+  for (; rel_ptr_ptr < rel_end; rel_ptr_ptr++)
+    {
+      arelent *rel;
+      asymbol *sym;
+      bfd_vma addend;
+
+      rel = *rel_ptr_ptr;
+      sym = *rel->sym_ptr_ptr;
+
+      /* Note that no serious harm will ensue if we fail to change a
+        reloc.  We will wind up failing in nlm_i386_write_reloc.  */
+
+      /* Make sure this reloc is within the data we have.  We only 4
+        byte relocs here, so we insist on having 4 bytes.  */
+      if (rel->address < offset
+         || rel->address + 4 > offset + count)
+       continue;
+
+      /* NetWare doesn't support reloc addends, so we get rid of them
+        here by simply adding them into the object data.  We handle
+        the symbol value, if any, the same way.  */
+      addend = rel->addend + sym->value;
+
+      /* The value of a symbol is the offset into the section.  If the
+        symbol is in the .bss segment, we need to include the size of
+        the data segment in the offset as well.  Fortunately, we know
+        that at this point the size of the data section is in the NLM
+        header.  */
+      if (((bfd_get_section_flags (abfd, bfd_get_section (sym))
+           & (SEC_CODE | SEC_DATA)) == 0)
+         && ((bfd_get_section_flags (abfd, bfd_get_section (sym))
+              & SEC_ALLOC) != 0))
+       addend += nlm_fixed_header (abfd)->dataImageSize;
+
+      if (addend != 0
+         && rel->howto != NULL
+         && rel->howto->rightshift == 0
+         && rel->howto->size == 2
+         && rel->howto->bitsize == 32
+         && rel->howto->bitpos == 0
+         && rel->howto->partial_inplace
+         && rel->howto->src_mask == 0xffffffff
+         && rel->howto->dst_mask == 0xffffffff)
+       {
+         bfd_vma val;
+
+         val = bfd_get_32 (abfd, (char *) data + rel->address - offset);
+         val += addend;
+         bfd_put_32 (abfd, val, (char *) data + rel->address - offset);
+         rel->addend = 0;
+       }
+
+      /* NetWare uses a reloc with pcrel_offset set.  We adjust
+        pc_relative relocs accordingly.  We are going to change the
+        howto field, so we can only do this if the current one is
+        compatible.  We should check special_function here, but at
+        the moment coff-i386 uses a special_function which does not
+        affect what we are doing here.  */
+      if (rel->howto != NULL
+         && rel->howto->pc_relative
+         && ! rel->howto->pcrel_offset
+         && rel->howto->rightshift == 0
+         && rel->howto->size == 2
+         && rel->howto->bitsize == 32
+         && rel->howto->bitpos == 0
+         && rel->howto->partial_inplace
+         && rel->howto->src_mask == 0xffffffff
+         && rel->howto->dst_mask == 0xffffffff)
+       {
+         bfd_vma val;
+
+         /* When pcrel_offset is not set, it means that the negative
+            of the address of the memory location is stored in the
+            memory location.  We must add it back in.  */
+         val = bfd_get_32 (abfd, (char *) data + rel->address - offset);
+         val += rel->address;
+         bfd_put_32 (abfd, val, (char *) data + rel->address - offset);
+
+         rel->howto = &nlm_i386_pcrel_howto;
+       }
+    }
+
+  return true;
+}
+
+static const struct nlm_backend_data nlm32_i386_backend =
+{
+  bfd_arch_i386,
+  nlm_i386_read_reloc,
+  nlm_i386_write_reloc,
+  nlm_i386_mangle_relocs
+};
+
+#define TARGET_LITTLE_NAME             "nlm32-i386"
+#define TARGET_LITTLE_SYM              nlmNAME(i386_vec)
+#define TARGET_BACKEND_DATA            &nlm32_i386_backend
+
+#include "nlm-target.h"
index 4c1679d..43ea019 100644 (file)
@@ -1337,7 +1337,14 @@ nlm_canonicalize_reloc (abfd, sec, relptr, symbols)
    no way to check this.
 
    This routine also sets the Size and Offset fields in the fixed
-   header.  */
+   header.
+
+   It also looks over the symbols and moves any common symbols into
+   the .bss section; NLM has no way to represent a common symbol.
+   This approach means that either the symbols must already have been
+   set at this point, or there must be no common symbols.  We need to
+   move the symbols at this point so that mangle_relocs can see the
+   final values.  */
 
 static boolean
 nlm_compute_section_file_positions (abfd)
@@ -1349,6 +1356,7 @@ nlm_compute_section_file_positions (abfd)
   bfd_vma text_low, data_low;
   int text_align, data_align, other_align;
   file_ptr text_ptr, data_ptr, other_ptr;
+  asymbol **sym_ptr_ptr;
 
   if (abfd->output_has_begun == true)
     return true;
@@ -1469,6 +1477,53 @@ nlm_compute_section_file_positions (abfd)
 
   nlm_fixed_header (abfd)->relocationFixupOffset = other_ptr;
 
+  /* Move all common symbols into the .bss section.  */
+
+  sym_ptr_ptr = bfd_get_outsymbols (abfd);
+  if (sym_ptr_ptr != NULL)
+    {
+      asymbol **sym_end;
+      asection *bss_sec;
+      bfd_vma add;
+
+      /* Make sure we have a section to hold uninitialized data.  */
+      bss_sec = bfd_get_section_by_name (abfd, NLM_UNINITIALIZED_DATA_NAME);
+      if (bss_sec == NULL)
+       {
+         if (! add_bfd_section (abfd, NLM_UNINITIALIZED_DATA_NAME,
+                                (file_ptr) 0, (bfd_size_type) 0,
+                                SEC_ALLOC))
+           return false;
+         bss_sec = bfd_get_section_by_name (abfd,
+                                            NLM_UNINITIALIZED_DATA_NAME);
+         bss_sec->vma = data_low + data;
+       }
+
+      sym_end = sym_ptr_ptr + bfd_get_symcount (abfd);
+      add = 0;
+      for (; sym_ptr_ptr < sym_end; sym_ptr_ptr++)
+       {
+         asymbol *sym;
+         bfd_vma size;
+
+         sym = *sym_ptr_ptr;
+
+         if (! bfd_is_com_section (bfd_get_section (sym)))
+           continue;
+
+         /* Put the common symbol in the .bss section, and increase
+            the size of the .bss section by the size of the common
+            symbol (which is the old value of the symbol).  */
+         sym->section = bss_sec;
+         size = sym->value;
+         sym->value = bss_sec->_raw_size + add;
+         add += size;
+         add = BFD_ALIGN (add, 1 << bss_sec->alignment_power);
+       }
+      nlm_fixed_header (abfd)->uninitializedDataSize += add;
+      bss_sec->_raw_size += add;
+    }
+
   return true;
 }
 
@@ -1492,6 +1547,25 @@ nlm_set_section_contents (abfd, section, location, offset, count)
   if (count == 0)
     return true;
 
+  /* i386 NetWare has a very restricted set of relocs.  In order for
+     objcopy to work, the NLM i386 backend needs a chance to rework
+     the section contents so that its set of relocs will work.  If all
+     the relocs are already acceptable, this will not do anything.  */
+  if (section->reloc_count != 0)
+    {
+      boolean (*mangle_relocs_func) PARAMS ((bfd *, asection *, PTR data,
+                                            bfd_vma offset,
+                                            bfd_size_type count));
+
+      mangle_relocs_func = nlm_mangle_relocs_func (abfd);
+      if (mangle_relocs_func != NULL)
+       {
+         if (! (*mangle_relocs_func) (abfd, section, location,
+                                      (bfd_vma) offset, count))
+           return false;
+       }
+    }
+
   if (bfd_seek (abfd, (file_ptr) (section->filepos + offset), SEEK_SET) != 0
       || bfd_write (location, 1, count, abfd) != count)
     {
@@ -1637,7 +1711,7 @@ nlm_write_object_contents (abfd)
          rel = *rel_ptr_ptr;
          sym = *rel->sym_ptr_ptr;
 
-         if ((sym->flags & BSF_SECTION_SYM) != 0)
+         if (bfd_get_section (sym) != &bfd_und_section)
            {
              ++internal_reloc_count;
              if ((*write_reloc_func) (abfd, sec, rel) == false)
@@ -1680,7 +1754,7 @@ nlm_write_object_contents (abfd)
          rel = *rel_ptr_ptr;
          sym = *rel->sym_ptr_ptr;
 
-         if ((sym->flags & BSF_SECTION_SYM) != 0)
+         if (bfd_get_section (sym) != &bfd_und_section)
            continue;
 
          external_relocs[i].rel = rel;
@@ -1704,7 +1778,7 @@ nlm_write_object_contents (abfd)
       arelent *rel;
       asymbol *sym;
       bfd_byte len;
-      bfd_size_type cnt;
+      bfd_size_type j, cnt;
       bfd_byte temp[NLM_TARGET_LONG_SIZE];
 
       ++c;
@@ -1722,8 +1796,10 @@ nlm_write_object_contents (abfd)
        }
 
       cnt = 0;
-      while (i < external_reloc_count
-            && *external_relocs[i].rel->sym_ptr_ptr == sym)
+      for (j = i;
+          (j < external_reloc_count
+           && *external_relocs[j].rel->sym_ptr_ptr == sym);
+          j++)
        ++cnt;
 
       put_word (abfd, (bfd_vma) cnt, temp);
@@ -1762,7 +1838,8 @@ nlm_write_object_contents (abfd)
 
          sym = *sym_ptr_ptr;
 
-         if ((sym->flags & (BSF_EXPORT | BSF_GLOBAL)) == 0)
+         if ((sym->flags & (BSF_EXPORT | BSF_GLOBAL)) == 0
+             || bfd_get_section (sym) == &bfd_und_section)
            continue;
 
          ++c;
@@ -1783,8 +1860,9 @@ nlm_write_object_contents (abfd)
              offset -= nlm_get_text_low (abfd);
              offset |= NLM_HIBIT;
            }
-         else if (sec->flags & SEC_DATA)
+         else if (sec->flags & (SEC_DATA | SEC_ALLOC))
            {
+             /* SEC_ALLOC is for the .bss section.  */
              offset -= nlm_get_data_low (abfd);
            }
          else
@@ -1806,6 +1884,7 @@ nlm_write_object_contents (abfd)
 
       nlm_fixed_header (abfd)->debugInfoOffset = bfd_tell (abfd);
       c = 0;
+      sym_ptr_ptr = bfd_get_outsymbols (abfd);
       sym_end = sym_ptr_ptr + bfd_get_symcount (abfd);
       for (; sym_ptr_ptr < sym_end; sym_ptr_ptr++)
        {
@@ -1817,6 +1896,14 @@ nlm_write_object_contents (abfd)
 
          sym = *sym_ptr_ptr;
 
+         /* The NLM notion of a debugging symbol is actually what BFD
+            calls a local or global symbol.  What BFD calls a
+            debugging symbol NLM does not understand at all.  */
+         if ((sym->flags & (BSF_LOCAL | BSF_GLOBAL | BSF_EXPORT)) == 0
+             || (sym->flags & BSF_DEBUGGING) != 0
+             || bfd_get_section (sym) == &bfd_und_section)
+           continue;
+
          ++c;
 
          offset = bfd_asymbol_value (sym);
@@ -1826,15 +1913,15 @@ nlm_write_object_contents (abfd)
              offset -= nlm_get_text_low (abfd);
              type = 1;
            }
-         else if (sec->flags & SEC_DATA)
+         else if (sec->flags & (SEC_DATA | SEC_ALLOC))
            {
              offset -= nlm_get_data_low (abfd);
              type = 0;
            }
          else
-           type = 3;
+           type = 2;
 
-         /* The type is 0 for data, 1 for code, 3 for absolute.  */
+         /* The type is 0 for data, 1 for code, 2 for absolute.  */
          if (bfd_write (&type, sizeof (bfd_byte), 1, abfd)
              != sizeof (bfd_byte))
            {
@@ -1905,12 +1992,8 @@ nlm_write_object_contents (abfd)
   /* We have no convenient way for the caller to pass in the exit
      procedure or the check unload procedure, so the caller must set
      the values in the header to the values of the symbols.  */
-  if (nlm_fixed_header (abfd)->exitProcedureOffset == 0)
-    {
-      bfd_error = invalid_operation;
-      return false;
-    }
-  nlm_fixed_header (abfd)->exitProcedureOffset -= nlm_get_text_low (abfd);
+  if (nlm_fixed_header (abfd)->exitProcedureOffset != 0)
+    nlm_fixed_header (abfd)->exitProcedureOffset -= nlm_get_text_low (abfd);
   if (nlm_fixed_header (abfd)->checkUnloadProcedureOffset != 0)
     nlm_fixed_header (abfd)->checkUnloadProcedureOffset -=
       nlm_get_text_low (abfd);