* elf32-powerpc.c: Extensive changes to update to preliminary ABI.
authorIan Lance Taylor <ian@airs.com>
Sat, 12 Mar 1994 03:30:52 +0000 (03:30 +0000)
committerIan Lance Taylor <ian@airs.com>
Sat, 12 Mar 1994 03:30:52 +0000 (03:30 +0000)
Many changes to nlm32-powerpc.c as well, but I'm not bothering to make
a ChangeLog entry to avoid sanitization headaches.

bfd/ChangeLog
bfd/elf32-powerpc.c
bfd/nlm32-powerpc.c

index c2218904632812f875fa6be7ccf1e5cccdf50f31..30d4c108f5ce085f74ef81f3318e5b21cf1ec746 100644 (file)
@@ -1,3 +1,7 @@
+Fri Mar 11 22:27:19 1994  Ian Lance Taylor  (ian@tweedledumb.cygnus.com)
+
+       * elf32-powerpc.c: Extensive changes to update to preliminary ABI.
+
 Fri Mar 11 00:34:59 1994  Peter Schauer  (pes@regent.e-technik.tu-muenchen.de)
 
        * sunos.c (sunos_read_dynamic_info):  Assume that dynamic info
@@ -282,6 +286,7 @@ Fri Feb 18 11:41:58 1994  Ian Lance Taylor  (ian@tweedledumb.cygnus.com)
 
        start-sanitize-powerpc-netware
        Support for PowerPC NetWare.
+       * nlm32-powerpc.c: New file.
        * config.bfd (powerpc-*-netware*): New target; use ppc-nlm.
        * config/ppc-nlm.mt: New file.
        * configure.in (nlm32_powerpc_vec): New target vector; use
index 7950133493dcc86e7ebef5f8cac2f386aab0a74c..3d2dd015f192fa7fcad8ed580ad6a3adc07dd508 100644 (file)
@@ -18,50 +18,81 @@ 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.  */
 
-/* I wrote this file without the benefit of a PowerPC ELF ABI.  Thus,
-   it probably does not correspond to whatever people finally settle
-   on.  Ian Taylor.  */
+/* This file is based on a preliminary PowerPC ELF ABI.  The
+   information may not match the final PowerPC ELF ABI.  It includes
+   suggestions from the in-progress Embedded PowerPC ABI, and that
+   information may also not match.  */
 
 #include "bfd.h"
 #include "sysdep.h"
 #include "libbfd.h"
 #include "libelf.h"
 
-static bfd_reloc_status_type powerpc_elf_toc16_reloc PARAMS ((bfd *abfd,
-                                                             arelent *reloc,
-                                                             asymbol *symbol,
-                                                             PTR data,
-                                                             asection *sec,
-                                                             bfd *output_bfd,
-                                                             char **error));
-static bfd_reloc_status_type powerpc_elf_b26_reloc PARAMS ((bfd *abfd,
-                                                           arelent *reloc,
-                                                           asymbol *symbol,
-                                                           PTR data,
-                                                           asection *sec,
-                                                           bfd *output_bfd,
-                                                           char **error));
+static bfd_reloc_status_type ppc_elf_unsupported_reloc
+  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
+static bfd_reloc_status_type ppc_elf_addr16_ha_reloc
+  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
+static bfd_reloc_status_type ppc_elf_got16_reloc
+  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
 static const struct reloc_howto_struct *bfd_elf32_bfd_reloc_type_lookup
   PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
-static void powerpc_info_to_howto_rel
-  PARAMS ((bfd *abfd, arelent *cache_ptr, Elf32_Internal_Rel *dst));
+static void powerpc_info_to_howto
+  PARAMS ((bfd *abfd, arelent *cache_ptr, Elf32_Internal_Rela *dst));
 
-#define USE_REL
+#define USE_RELA
 
 enum reloc_type
 {
-  R_POWERPC_NONE = 0,
-  R_POWERPC_32,
-  R_POWERPC_B26,
-  R_POWERPC_BA26,
-  R_POWERPC_TOC16,
-  R_POWERPC_max
+  R_PPC_NONE = 0,
+  R_PPC_ADDR32,
+  R_PPC_ADDR24,
+  R_PPC_ADDR16,
+  R_PPC_ADDR16_LO,
+  R_PPC_ADDR16_HI,
+  R_PPC_ADDR16_HA,
+  R_PPC_ADDR14,
+  R_PPC_ADDR14_BRTAKEN,
+  R_PPC_ADDR14_BRNTAKEN,
+  R_PPC_REL24,
+  R_PPC_REL14,
+  R_PPC_REL14_BRTAKEN,
+  R_PPC_REL14_BRNTAKEN,
+  R_PPC_GOT16,
+  R_PPC_GOT16_LO,
+  R_PPC_GOT16_HI,
+  R_PPC_GOT16_HA,
+  R_PPC_PLT24,
+  R_PPC_COPY,
+  R_PPC_GLOB_DAT,
+  R_PPC_JMP_SLOT,
+  R_PPC_RELATIVE,
+  R_PPC_LOCAL24PC,
+  R_PPC_UADDR32,
+  R_PPC_UADDR16,
+
+  /* The remaining relocs are from the Embedded ELF ABI, and are not
+     in the SVR4 ELF ABI.  */
+  R_PPC_CGOT16,
+  R_PPC_CGOT16_LO,
+  R_PPC_CGOT16_HI,
+  R_PPC_CGOT16_HA,
+  R_PPC_GOT_REG,
+  R_PPC_CGOT_REG,
+  R_PPC_NADDR16,
+  R_PPC_NADDR32,
+  R_PPC_NADDR24,
+  R_PPC_DISP16,
+  R_PPC_GOTRL16,
+  R_PPC_GOTRLA16,
+  R_PPC_CGOTRL16,
+  R_PPC_CGOTRLA16,
+  R_PPC_max
 };
 
 static reloc_howto_type elf_powerpc_howto_table[] =
 {
-  /* This relocation does not do anything.  */
-  HOWTO (R_POWERPC_NONE,        /* type */                                 
+  /* This reloc does nothing.  */
+  HOWTO (R_PPC_NONE,           /* type */                                 
         0,                     /* rightshift */                           
         2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
         32,                    /* bitsize */                   
@@ -69,14 +100,14 @@ static reloc_howto_type elf_powerpc_howto_table[] =
         0,                     /* bitpos */                               
         complain_overflow_bitfield, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */                     
-        "R_POWERPC_NONE",      /* name */                                 
-        true,                  /* partial_inplace */                      
+        "R_PPC_NONE",          /* name */                                 
+        false,                 /* partial_inplace */                      
         0,                     /* src_mask */                             
         0,                     /* dst_mask */                             
         false),                /* pcrel_offset */
 
-  /* Standard 32 bit relocation.  */
-  HOWTO (R_POWERPC_32,          /* type */                                 
+  /* A standard 32 bit relocation.  */
+  HOWTO (R_PPC_ADDR32,          /* type */                                 
         0,                     /* rightshift */                           
         2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
         32,                    /* bitsize */                   
@@ -84,69 +115,621 @@ static reloc_howto_type elf_powerpc_howto_table[] =
         0,                     /* bitpos */                               
         complain_overflow_bitfield, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */                     
-        "R_POWERPC_32",        /* name */                                 
-        true,                  /* partial_inplace */                      
-        0xffffffff,            /* src_mask */                             
-        0xffffffff,            /* dst_mask */                             
+        "R_PPC_ADDR32",        /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffffffff,            /* dst_mask */                             
         false),                /* pcrel_offset */
 
-  /* 26 bit relative branch.  */
-  HOWTO (R_POWERPC_B26,         /* type */                                 
+  /* An absolute 26 bit branch; the lower two bits must be zero.
+     FIXME: we don't check that, we just clear them.  */
+  HOWTO (R_PPC_ADDR24,          /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        26,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_ADDR24",        /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x3fffffc,             /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* A standard 16 bit relocation.  */
+  HOWTO (R_PPC_ADDR16,          /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_ADDR16",        /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* A 16 bit relocation without overflow.  */
+  HOWTO (R_PPC_ADDR16_LO,       /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_dont,/* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_ADDR16_LO",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* The high order 16 bits of an address.  */
+  HOWTO (R_PPC_ADDR16_HI,       /* type */                                 
+        16,                    /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_ADDR16_HI",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* The high order 16 bits of an address, plus 1 if the contents of
+     the low 16 bits, treated as a signed number, is negative.  */
+  HOWTO (R_PPC_ADDR16_HA,       /* type */                                 
+        16,                    /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_addr16_ha_reloc, /* special_function */                     
+        "R_PPC_ADDR16_HA",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* An absolute 16 bit branch; the lower two bits must be zero.
+     FIXME: we don't check that, we just clear them.  */
+  HOWTO (R_PPC_ADDR14,          /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_ADDR14",        /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xfffc,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* An absolute 16 bit branch, for which bit 10 should be set to
+     indicate that the branch is expected to be taken.  The lower two
+     bits must be zero.  */
+  HOWTO (R_PPC_ADDR14_BRTAKEN,  /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */             
+        "R_PPC_ADDR14_BRTAKEN",/* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xfffc,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* An absolute 16 bit branch, for which bit 10 should be set to
+     indicate that the branch is not expected to be taken.  The lower
+     two bits must be zero.  */
+  HOWTO (R_PPC_ADDR14_BRNTAKEN, /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */             
+        "R_PPC_ADDR14_BRNTAKEN",/* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xfffc,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* A relative 26 bit branch; the lower two bits must be zero.  */
+  HOWTO (R_PPC_REL24,           /* type */                                 
         0,                     /* rightshift */                           
         2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
         26,                    /* bitsize */                   
         true,                  /* pc_relative */                          
         0,                     /* bitpos */                               
         complain_overflow_signed, /* complain_on_overflow */
-        powerpc_elf_b26_reloc, /* special_function */                     
-        "R_POWERPC_B26",       /* name */                                 
-        true,                  /* partial_inplace */                      
-        0x3fffffc,             /* src_mask */                             
-        0x3fffffc,             /* dst_mask */                             
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_REL24",         /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x3fffffc,             /* dst_mask */                             
+        true),                 /* pcrel_offset */
+
+  /* A relative 16 bit branch; the lower two bits must be zero.  */
+  HOWTO (R_PPC_REL14,           /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        true,                  /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_REL14",         /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xfffc,                /* dst_mask */                             
+        true),                 /* pcrel_offset */
+
+  /* A relative 16 bit branch.  Bit 10 should be set to indicate that
+     the branch is expected to be taken.  The lower two bits must be
+     zero.  */
+  HOWTO (R_PPC_REL14_BRTAKEN,   /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */             
+        "R_PPC_REL14_BRTAKEN", /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xfffc,                /* dst_mask */                             
+        true),                 /* pcrel_offset */
+
+  /* A relative 16 bit branch.  Bit 10 should be set to indicate that
+     the branch is not expected to be taken.  The lower two bits must
+     be zero.  */
+  HOWTO (R_PPC_REL14_BRNTAKEN,  /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */             
+        "R_PPC_REL14_BRNTAKEN",/* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xfffc,                /* dst_mask */                             
+        true),                 /* pcrel_offset */
+
+  /* Like R_PPC_ADDR16, but referring to the GOT table entry for the
+     symbol.  */
+  HOWTO (R_PPC_GOT16,           /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_GOT16",         /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_ADDR16_LO, but referring to the GOT table entry for
+     the symbol.  */
+  HOWTO (R_PPC_GOT16_LO,        /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_GOT16_LO",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
         false),                /* pcrel_offset */
 
-  /* 26 bit absolute branch.  */
-  HOWTO (R_POWERPC_BA26,        /* type */                                 
+  /* Like R_PPC_ADDR16_HI, but referring to the GOT table entry for
+     the symbol.  */
+  HOWTO (R_PPC_GOT16_HI,        /* type */                                 
+        16,                    /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_GOT16_HI",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                 /* pcrel_offset */
+
+  /* Like R_PPC_ADDR16_HA, but referring to the GOT table entry for
+     the symbol.  FIXME: Not supported.  */
+  HOWTO (R_PPC_GOT16_HA,        /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_GOT16_HA",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_REL24, but referring to the procedure linkage table
+     entry for the symbol.  FIXME: Not supported.  */
+  HOWTO (R_PPC_PLT24,           /* type */                                 
         0,                     /* rightshift */                           
         2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
         26,                    /* bitsize */                   
+        true,                  /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_PLT24",         /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x3fffffc,             /* dst_mask */                             
+        true),                 /* pcrel_offset */
+
+  /* This is used only by the dynamic linker.  The symbol should exist
+     both in the object being run and in some shared library.  The
+     dynamic linker copies the data addressed by the symbol from the
+     shared library into the object.  I have no idea what the purpose
+     of this is.  */
+  HOWTO (R_PPC_COPY,            /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        32,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_COPY",          /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0,                     /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_ADDR32, but used when setting global offset table
+     entries.  */
+  HOWTO (R_PPC_GLOB_DAT,        /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        32,                    /* bitsize */                   
         false,                 /* pc_relative */                          
         0,                     /* bitpos */                               
         complain_overflow_bitfield, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */                     
-        "R_POWERPC_BA26",      /* name */                                 
-        true,                  /* partial_inplace */                      
-        0x3fffffc,             /* src_mask */                             
-        0x3fffffc,             /* dst_mask */                             
+        "R_PPC_GLOB_DAT",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffffffff,            /* dst_mask */                             
         false),                /* pcrel_offset */
-  
-  /* 16 bit reference to TOC section.  */
-  HOWTO (R_POWERPC_TOC16,       /* type */                                 
+
+  /* Marks a procedure linkage table entry for a symbol.  */
+  HOWTO (R_PPC_JMP_SLOT,        /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        32,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_JMP_SLOT",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0,                     /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Used only by the dynamic linker.  When the object is run, this
+     longword is set to the load address of the object, plus the
+     addend.  */
+  HOWTO (R_PPC_RELATIVE,        /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        32,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_RELATIVE",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffffffff,            /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_REL24, but uses the value of the symbol within the
+     object rather than the final value.  Normally used for
+     _GLOBAL_OFFSET_TABLE_.  FIXME: Not supported.  */
+  HOWTO (R_PPC_LOCAL24PC,       /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        26,                    /* bitsize */                   
+        true,                  /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_LOCAL24PC",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x3fffffc,             /* dst_mask */                             
+        true),                 /* pcrel_offset */
+
+  /* Like R_PPC_ADDR32, but may be unaligned.  */
+  HOWTO (R_PPC_UADDR32,         /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        32,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_UADDR32",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffffffff,            /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_ADDR16, but may be unaligned.  */
+  HOWTO (R_PPC_UADDR16,         /* type */                                 
         0,                     /* rightshift */                           
         1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
         16,                    /* bitsize */                   
         false,                 /* pc_relative */                          
         0,                     /* bitpos */                               
-        complain_overflow_signed, /* complain_on_overflow */
-        powerpc_elf_toc16_reloc, /* special_function */                     
-        "R_POWERPC_TOC16",     /* name */                                 
-        true,                  /* partial_inplace */                      
-        0xffff,                /* src_mask */                             
-        0xffff,                /* dst_mask */                             
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */                     
+        "R_PPC_UADDR16",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* The remaining relocs are from the Embedded ELF ABI, and are not
+     in the SVR4 ELF ABI.  */
+
+  /* Like R_PPC_GOT16, but using the constant GOT table.  */
+  HOWTO (R_PPC_CGOT16,          /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_CGOT16",        /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_GOT16_LO, but using the constant GOT table.  */
+  HOWTO (R_PPC_CGOT16_LO,       /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_CGOT16_LO",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_GOT16_HI, but using the constant GOT table.  */
+  HOWTO (R_PPC_CGOT16_HI,       /* type */                                 
+        16,                    /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_CGOT16_HI",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_GOT16_HA, but using the constant GOT table.  */
+  HOWTO (R_PPC_CGOT16_HA,       /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_CGOT16_HA",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* The dedicated GOT register.  */
+  HOWTO (R_PPC_GOT_REG,         /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        5,                     /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_GOT_REG",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x1f,                  /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* The dedicated CGOT register.  */
+  HOWTO (R_PPC_CGOT_REG,        /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        5,                     /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_CGOT_REG",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x1f,                  /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_ADDR16, but subtract the symbol.  */
+  HOWTO (R_PPC_NADDR16,         /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_NADDR16",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_ADDR32, but subtract the symbol.  */
+  HOWTO (R_PPC_NADDR32,         /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        32,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_NADDR32",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffffffff,            /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_ADDR24, but subtract the symbol.  */
+  HOWTO (R_PPC_NADDR24,         /* type */                                 
+        0,                     /* rightshift */                           
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        26,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_NADDR24",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0x3fffffc,             /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Offset of symbol relative to containing csect.  */
+  HOWTO (R_PPC_DISP16,          /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_DISP16",        /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_GOT16, but may be converted to an address computation
+     instead of a load.  */
+  HOWTO (R_PPC_GOTRL16,         /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_GOTRL16",       /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_GOT16, but may be converted to a load instead of an
+     address computation.  */
+  HOWTO (R_PPC_GOTRLA16,        /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_got16_reloc,   /* special_function */                     
+        "R_PPC_GOTRLA16",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_CGOT16, but may be converted to an address computation
+     instead of a load.  */
+  HOWTO (R_PPC_CGOTRL16,        /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */                     
+        "R_PPC_CGOTRL16",      /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
+        false),                /* pcrel_offset */
+
+  /* Like R_PPC_CGOT16, but may be converted to a load instead of an
+     address computation.  */
+  HOWTO (R_PPC_CGOTRLA16,       /* type */                                 
+        0,                     /* rightshift */                           
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */ 
+        16,                    /* bitsize */                   
+        false,                 /* pc_relative */                          
+        0,                     /* bitpos */                               
+        complain_overflow_bitfield, /* complain_on_overflow */
+        ppc_elf_unsupported_reloc, /* special_function */
+        "R_PPC_CGOTRLA16",     /* name */
+        false,                 /* partial_inplace */                      
+        0,                     /* src_mask */                             
+        0xffff,                /* dst_mask */                             
         false),                /* pcrel_offset */
 };
 
-/* Handle the TOC16 reloc.  We want to use the offset within the .toc
-   section, not the actual VMA.  */
+/* Don't pretend we can deal with unsupported relocs.  */
 
+/*ARGSUSED*/
 static bfd_reloc_status_type
-powerpc_elf_toc16_reloc (abfd,
-                        reloc_entry,
-                        symbol,
-                        data,
-                        input_section,
-                        output_bfd,
-                        error_message)
+ppc_elf_unsupported_reloc (abfd, reloc_entry, symbol, data, input_section,
+                          output_bfd, error_message)
      bfd *abfd;
      arelent *reloc_entry;
      asymbol *symbol;
@@ -155,33 +738,52 @@ powerpc_elf_toc16_reloc (abfd,
      bfd *output_bfd;
      char **error_message;
 {
-  asection *sec;
+  abort ();
+}
+
+/* Handle the ADDR16_HA reloc by adjusting the reloc addend.  */
+
+static bfd_reloc_status_type
+ppc_elf_addr16_ha_reloc (abfd, reloc_entry, symbol, data, input_section,
+                        output_bfd, error_message)
+     bfd *abfd;
+     arelent *reloc_entry;
+     asymbol *symbol;
+     PTR data;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message;
+{
+  bfd_vma relocation;
+
+  if (output_bfd != (bfd *) NULL)
+    return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+                                 input_section, output_bfd, error_message);
+
+  if (bfd_is_com_section (symbol->section))
+    relocation = 0;
+  else
+    relocation = symbol->value;
+
+  relocation += (symbol->section->output_section->vma
+                + symbol->section->output_offset
+                + reloc_entry->addend);
+
+  if ((relocation & 0x8000) != 0)
+    reloc_entry->addend += 0x10000;
 
-  /* Simply adjust the reloc addend by the negative of the VMA of the
-     .toc section.  */
-  sec = bfd_get_section (*reloc_entry->sym_ptr_ptr);
-  if (sec == &bfd_und_section)
-    return bfd_reloc_continue;
-  BFD_ASSERT (strcmp (bfd_get_section_name (abfd, sec), ".toc") == 0);
-  reloc_entry->addend -= sec->output_section->vma;
   return bfd_reloc_continue;
 }
 
-/* Handle a branch.  If the next instruction is the special
-   instruction "cror 31,31,31", and this branch is to a stub routine
-   we have created, we must change the cror instruction into an
-   instruction which reloads the TOC register.  We can detect a stub
-   routine because it is in a BFD named "linker stubs".  FIXME: What a
-   hack.  */
+/* Handle the GOT16 reloc.  We want to use the offset within the .got
+   section, not the actual VMA.  This is appropriate when generating
+   an embedded ELF object, for which the .got section acts like the
+   AIX .toc section.  When and if we support PIC code, we will have to
+   change this, perhaps by switching off on the e_type field.  */
 
 static bfd_reloc_status_type
-powerpc_elf_b26_reloc (abfd,
-                      reloc_entry,
-                      symbol,
-                      data,
-                      input_section,
-                      output_bfd,
-                      error_message)
+ppc_elf_got16_reloc (abfd, reloc_entry, symbol, data, input_section,
+                    output_bfd, error_message)
      bfd *abfd;
      arelent *reloc_entry;
      asymbol *symbol;
@@ -190,35 +792,17 @@ powerpc_elf_b26_reloc (abfd,
      bfd *output_bfd;
      char **error_message;
 {
-  if (bfd_get_section (symbol) != &bfd_und_section
-      && (strcmp (bfd_get_section (symbol)->owner->filename, "linker stubs")
-         == 0))
-    {
-      bfd_vma val;
-
-      /* This symbol is a stub created by the linker.  */
-      val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address + 4);
-      if (val == 0x4ffffb82)                   /* cror 31,31,31 */
-       bfd_put_32 (abfd, (bfd_vma) 0x80410014, /* l r2,20(r1) */
-                   (bfd_byte *) data + reloc_entry->address + 4);
-    }
+  asection *sec;
 
-  if (output_bfd != (bfd *) NULL
-      && bfd_get_section (symbol) == &bfd_und_section)
-    {
-      bfd_size_type address;
-
-      /* If we are generating relocateable output, and the symbol is
-        undefined, we just want to adjust the reloc by the amount the
-        section moved.  */
-      address = reloc_entry->address;
-      reloc_entry->address += input_section->output_offset;
-      return _bfd_relocate_contents (reloc_entry->howto, abfd,
-                                    - input_section->output_offset,
-                                    (bfd_byte *) data + address);
-    }
+  if (output_bfd != (bfd *) NULL)
+    return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+                                 input_section, output_bfd, error_message);
 
-  /* Otherwise, let bfd_perform_relocation handle it.  */
+  sec = bfd_get_section (*reloc_entry->sym_ptr_ptr);
+  BFD_ASSERT (sec == &bfd_und_section
+             || strcmp (bfd_get_section_name (abfd, sec), ".got") == 0
+             || strcmp (bfd_get_section_name (abfd, sec), ".cgot") == 0);
+  reloc_entry->addend -= sec->output_section->vma;
   return bfd_reloc_continue;
 }
 
@@ -232,12 +816,12 @@ struct powerpc_reloc_map
 
 static const struct powerpc_reloc_map powerpc_reloc_map[] =
 {
-  { BFD_RELOC_NONE, R_POWERPC_NONE, },
-  { BFD_RELOC_32, R_POWERPC_32 },
-  { BFD_RELOC_CTOR, R_POWERPC_32 },
-  { BFD_RELOC_PPC_B26, R_POWERPC_B26 },
-  { BFD_RELOC_PPC_BA26, R_POWERPC_BA26 },
-  { BFD_RELOC_PPC_TOC16, R_POWERPC_TOC16 }
+  { BFD_RELOC_NONE, R_PPC_NONE, },
+  { BFD_RELOC_32, R_PPC_ADDR32 },
+  { BFD_RELOC_CTOR, R_PPC_ADDR32 },
+  { BFD_RELOC_PPC_B26, R_PPC_REL24 },
+  { BFD_RELOC_PPC_BA26, R_PPC_ADDR24 },
+  { BFD_RELOC_PPC_TOC16, R_PPC_GOT16 }
 };
 
 static const struct reloc_howto_struct *
@@ -261,13 +845,13 @@ bfd_elf32_bfd_reloc_type_lookup (abfd, code)
 /* Set the howto pointer for a PowerPC ELF reloc.  */
 
 static void
-powerpc_info_to_howto_rel (abfd, cache_ptr, dst)
+powerpc_info_to_howto (abfd, cache_ptr, dst)
      bfd *abfd;
      arelent *cache_ptr;
-     Elf32_Internal_Rel *dst;
+     Elf32_Internal_Rela *dst;
 {
-  BFD_ASSERT (ELF32_R_TYPE (dst->r_info) < (unsigned int) R_POWERPC_max);
-  cache_ptr->howto = &elf_powerpc_howto_table[ELF32_R_TYPE(dst->r_info)];
+  BFD_ASSERT (ELF32_R_TYPE (dst->r_info) < (unsigned int) R_PPC_max);
+  cache_ptr->howto = &elf_powerpc_howto_table[ELF32_R_TYPE (dst->r_info)];
 }
 
 #define TARGET_BIG_SYM         bfd_elf32_powerpc_vec
@@ -275,7 +859,6 @@ powerpc_info_to_howto_rel (abfd, cache_ptr, dst)
 #define ELF_ARCH               bfd_arch_powerpc
 #define ELF_MACHINE_CODE       EM_CYGNUS_POWERPC
 #define ELF_MAXPAGESIZE                0x10000
-#define elf_info_to_howto      0
-#define elf_info_to_howto_rel  powerpc_info_to_howto_rel
+#define elf_info_to_howto      powerpc_info_to_howto
 
 #include "elf32-target.h"
index c7c40c69163d05841b4bab1699f0e27719326838..eb7a7b5793a6909a32f3bc7b8bea5f8ff92807f5 100644 (file)
@@ -38,6 +38,8 @@ static boolean nlm_powerpc_mangle_relocs
   PARAMS ((bfd *, asection *, PTR, bfd_vma, bfd_size_type));
 static boolean nlm_powerpc_read_import
   PARAMS ((bfd *, nlmNAME(symbol_type) *));
+static boolean nlm_powerpc_write_reloc
+  PARAMS ((bfd *, asection *, arelent *, int));
 static boolean nlm_powerpc_write_import
   PARAMS ((bfd *, asection *, arelent *));
 static boolean nlm_powerpc_write_external
@@ -448,6 +450,7 @@ nlm_powerpc_read_reloc (abfd, sym, secp, rel)
   unsigned long l_symndx;
   int l_rtype;
   int l_rsecnm;
+  asection *code_sec, *data_sec, *bss_sec;
 
   /* Read the reloc from the file.  */
   if (bfd_read (&ext, sizeof ext, 1, abfd) != sizeof ext)
@@ -462,6 +465,11 @@ nlm_powerpc_read_reloc (abfd, sym, secp, rel)
   l_rtype = bfd_h_get_16 (abfd, ext.l_rtype);
   l_rsecnm = bfd_h_get_16 (abfd, ext.l_rsecnm);
 
+  /* Get the sections now, for convenience.  */
+  code_sec = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
+  data_sec = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
+  bss_sec = bfd_get_section_by_name (abfd, NLM_UNINITIALIZED_DATA_NAME);
+
   /* Work out the arelent fields.  */
   if (sym != NULL)
     {
@@ -474,11 +482,11 @@ nlm_powerpc_read_reloc (abfd, sym, secp, rel)
       asection *sec;
 
       if (l_symndx == 0)
-       sec = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
+       sec = code_sec;
       else if (l_symndx == 1)
-       sec = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
+       sec = data_sec;
       else if (l_symndx == 2)
-       sec = bfd_get_section_by_name (abfd, NLM_UNINITIALIZED_DATA_NAME);
+       sec = bss_sec;
       else
        {
          bfd_set_error (bfd_error_bad_value);
@@ -488,7 +496,6 @@ nlm_powerpc_read_reloc (abfd, sym, secp, rel)
       rel->sym_ptr_ptr = sec->symbol_ptr_ptr;
     }
 
-  rel->address = l_vaddr;
   rel->addend = 0;
 
   BFD_ASSERT ((l_rtype & 0xff) < HOWTO_COUNT);
@@ -504,15 +511,20 @@ nlm_powerpc_read_reloc (abfd, sym, secp, rel)
              && ((l_rtype >> 8) & 0x1f) == rel->howto->bitsize - 1);
 
   if (l_rsecnm == 0)
-    *secp = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
+    *secp = code_sec;
   else if (l_rsecnm == 1)
-    *secp = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
+    {
+      *secp = data_sec;
+      l_vaddr -= bfd_section_size (abfd, code_sec);
+    }
   else
     {
       bfd_set_error (bfd_error_bad_value);
       return false;
     }
 
+  rel->address = l_vaddr;
+
   return true;
 }
 
@@ -591,36 +603,47 @@ nlm_powerpc_read_import (abfd, sym)
 /* Write a PowerPC NLM reloc.  */
 
 static boolean
-nlm_powerpc_write_import (abfd, sec, rel)
+nlm_powerpc_write_reloc (abfd, sec, rel, indx)
      bfd *abfd;
      asection *sec;
      arelent *rel;
+     int indx;
 {
   struct nlm32_powerpc_external_reloc ext;
+  asection *code_sec, *data_sec, *bss_sec;
   asymbol *sym;
   asection *symsec;
   unsigned long l_symndx;
   int l_rtype;
   int l_rsecnm;
   const reloc_howto_type *howto;
+  bfd_size_type address;
 
-  bfd_h_put_32 (abfd, rel->address, ext.l_vaddr);
+  /* Get the sections now, for convenience.  */
+  code_sec = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
+  data_sec = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
+  bss_sec = bfd_get_section_by_name (abfd, NLM_UNINITIALIZED_DATA_NAME);
 
   sym = *rel->sym_ptr_ptr;
   symsec = bfd_get_section (sym);
-  if (symsec == &bfd_und_section)
-    l_symndx = 0;
-  else if (symsec == bfd_get_section_by_name (abfd, NLM_CODE_NAME))
-    l_symndx = 0;
-  else if (symsec == bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME))
-    l_symndx = 1;
-  else if (symsec == bfd_get_section_by_name (abfd,
-                                             NLM_UNINITIALIZED_DATA_NAME))
-    l_symndx = 2;
+  if (indx != -1)
+    {
+      BFD_ASSERT (symsec == &bfd_und_section);
+      l_symndx = indx + 3;
+    }
   else
     {
-      bfd_set_error (bfd_error_bad_value);
-      return false;
+      if (symsec == code_sec)
+       l_symndx = 0;
+      else if (symsec == data_sec)
+       l_symndx = 1;
+      else if (symsec == bss_sec)
+       l_symndx = 2;
+      else
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       }
     }
 
   bfd_h_put_32 (abfd, (bfd_vma) l_symndx, ext.l_symndx);
@@ -634,8 +657,12 @@ nlm_powerpc_write_import (abfd, sec, rel)
          && howto->bitsize == rel->howto->bitsize
          && howto->pc_relative == rel->howto->pc_relative
          && howto->bitpos == rel->howto->bitpos
-         && howto->partial_inplace == rel->howto->partial_inplace
-         && howto->src_mask == rel->howto->src_mask
+         && (howto->partial_inplace == rel->howto->partial_inplace
+             || (! rel->howto->partial_inplace
+                 && rel->addend == 0))
+         && (howto->src_mask == rel->howto->src_mask
+             || (rel->howto->src_mask == 0
+                 && rel->addend == 0))
          && howto->dst_mask == rel->howto->dst_mask
          && howto->pcrel_offset == rel->howto->pcrel_offset)
        break;
@@ -652,10 +679,15 @@ nlm_powerpc_write_import (abfd, sec, rel)
   l_rtype |= (howto->bitsize - 1) << 8;
   bfd_h_put_16 (abfd, (bfd_vma) l_rtype, ext.l_rtype);
 
-  if (sec == bfd_get_section_by_name (abfd, NLM_CODE_NAME))
+  address = rel->address;
+
+  if (sec == code_sec)
     l_rsecnm = 0;
-  else if (sec == bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME))
-    l_rsecnm = 1;
+  else if (sec == data_sec)
+    {
+      l_rsecnm = 1;
+      address += bfd_section_size (abfd, code_sec);
+    }
   else
     {
       bfd_set_error (bfd_error_bad_value);
@@ -663,6 +695,7 @@ nlm_powerpc_write_import (abfd, sec, rel)
     }
 
   bfd_h_put_16 (abfd, (bfd_vma) l_rsecnm, ext.l_rsecnm);
+  bfd_h_put_32 (abfd, (bfd_vma) address, ext.l_vaddr);
 
   if (bfd_write (&ext, sizeof ext, 1, abfd) != sizeof ext)
     return false;
@@ -670,7 +703,20 @@ nlm_powerpc_write_import (abfd, sec, rel)
   return true;
 }
 
-/* Write a PowerPC NLM external symbol.  */
+/* Write a PowerPC NLM import.  */
+
+static boolean
+nlm_powerpc_write_import (abfd, sec, rel)
+     bfd *abfd;
+     asection *sec;
+     arelent *rel;
+{
+  return nlm_powerpc_write_reloc (abfd, sec, rel, -1);
+}
+
+/* Write a PowerPC NLM external symbol.  This routine keeps a static
+   count of the symbol index.  FIXME: I don't know if this is
+   necessary, and the index never gets reset.  */
 
 static boolean
 nlm_powerpc_write_external (abfd, count, sym, relocs)
@@ -682,6 +728,7 @@ nlm_powerpc_write_external (abfd, count, sym, relocs)
   int i;
   bfd_byte len;
   unsigned char temp[NLM_TARGET_LONG_SIZE];
+  static int indx;
 
   len = strlen (sym->name);
   if ((bfd_write (&len, sizeof (bfd_byte), 1, abfd) != sizeof(bfd_byte))
@@ -700,11 +747,13 @@ nlm_powerpc_write_external (abfd, count, sym, relocs)
 
   for (i = 0; i < count; i++)
     {
-      if (nlm_powerpc_write_import (abfd, relocs[i].sec,
-                                   relocs[i].rel) == false)
+      if (nlm_powerpc_write_reloc (abfd, relocs[i].sec,
+                                  relocs[i].rel, indx) == false)
        return false;
     }
 
+  ++indx;
+
   return true;
 }