OSDN Git Service

bfd/
authorRichard Sandiford <rsandifo@nildram.co.uk>
Wed, 1 Apr 2009 19:27:37 +0000 (19:27 +0000)
committerRichard Sandiford <rsandifo@nildram.co.uk>
Wed, 1 Apr 2009 19:27:37 +0000 (19:27 +0000)
* xcofflink.c (xcoff_link_create_extra_sections): Don't create
a .loader section for relocatable links.
(xcoff_need_ldrel_p): New function.
(xcoff_mark): Use it.
(bfd_xcoff_link_count_reloc): Only count loader relocs if there's
a loader section.
(xcoff_build_ldsym): New function, split out from...
(xcoff_build_ldsyms): ...here.  Rename to...
(xcoff_post_gc_symbol): ...this.  Only export symbols, and only
call xcoff_build_ldsym, if there's a loader section.
(xcoff_build_loader_section): New function, extracted verbatim from...
(bfd_xcoff_size_dynamic_sections): ...here.  Only call it if
there's a loader section.  Only add an __rtinit loader symbol
if there's a loader section.  Update after above name change.
(xcoff_symbol_section, xcoff_create_ldrel): New functions.
(bfd_link_input_bfd): Use xcoff_need_ldrel_p, xcoff_symbol_section
and xcoff_create_ldrel.
(xcoff_write_global_symbol): Use xcoff_create_ldrel.
(xcoff_reloc_link_order): Likewise, but only call it if there's
a loader section.  Use xcoff_symbol_section.
(_bfd_xcoff_bfd_final_link): Only use fdinfo.ldrel and fdinfo.ldsym
if there's a loader section.

ld/testsuite/
* ld-powerpc/aix-rel-1.s, ld-powerpc/aix-rel-1.od: New test.
* ld-powerpc/aix52.exp: Run it.

bfd/xcofflink.c

index ac0f529..6905a3a 100644 (file)
@@ -814,7 +814,8 @@ xcoff_link_create_extra_sections (bfd * abfd, struct bfd_link_info *info)
         won't work if we're producing an XCOFF output file with no
         XCOFF input files.  FIXME.  */
 
-      if (xcoff_hash_table (info)->loader_section == NULL)
+      if (!info->relocatable
+         && xcoff_hash_table (info)->loader_section == NULL)
        {
          asection *lsec;
          flagword flags = SEC_HAS_CONTENTS | SEC_IN_MEMORY;
@@ -2409,6 +2410,59 @@ xcoff_auto_export_p (struct xcoff_link_hash_entry *h,
   return FALSE;
 }
 \f
+/* Return true if relocation REL needs to be copied to the .loader section.
+   If REL is against a global symbol, H is that symbol, otherwise it
+   is null.  */
+
+static bfd_boolean
+xcoff_need_ldrel_p (struct bfd_link_info *info, struct internal_reloc *rel,
+                   struct xcoff_link_hash_entry *h)
+{
+  if (!xcoff_hash_table (info)->loader_section)
+    return FALSE;
+
+  switch (rel->r_type)
+    {
+    case R_TOC:
+    case R_GL:
+    case R_TCL:
+    case R_TRL:
+    case R_TRLA:
+      /* We should never need a .loader reloc for a TOC-relative reloc.  */
+      return FALSE;
+
+    default:
+      /* In this case, relocations against defined symbols can be resolved
+        statically.  */
+      if (h == NULL
+         || h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak
+         || h->root.type == bfd_link_hash_common)
+       return FALSE;
+
+      /* We will always provide a local definition of function symbols,
+        even if we don't have one yet.  */
+      if ((h->flags & XCOFF_CALLED) != 0)
+       return FALSE;
+
+      return TRUE;
+
+    case R_POS:
+    case R_NEG:
+    case R_RL:
+    case R_RLA:
+      /* Absolute relocations against absolute symbols can be
+        resolved statically.  */
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && bfd_is_abs_section (h->root.u.def.section))
+       return FALSE;
+
+      return TRUE;
+    }
+}
+\f
 /* Mark a symbol as not being garbage, including the section in which
    it is defined.  */
 
@@ -2681,39 +2735,11 @@ xcoff_mark (struct bfd_link_info *info, asection *sec)
 
              /* See if this reloc needs to be copied into the .loader
                 section.  */
-             switch (rel->r_type)
+             if (xcoff_need_ldrel_p (info, rel, h))
                {
-               default:
-                 if (h == NULL
-                     || h->root.type == bfd_link_hash_defined
-                     || h->root.type == bfd_link_hash_defweak
-                     || h->root.type == bfd_link_hash_common
-                     /* We will always provide a local definition of
-                        function symbols.  */
-                     || (h->flags & XCOFF_CALLED) != 0)
-                   break;
-                 /* Fall through.  */
-               case R_POS:
-               case R_NEG:
-               case R_RL:
-               case R_RLA:
-                 if (h != NULL
-                     && (h->root.type == bfd_link_hash_defined
-                         || h->root.type == bfd_link_hash_defweak)
-                     && bfd_is_abs_section (h->root.u.def.section))
-                   break;
                  ++xcoff_hash_table (info)->ldrel_count;
                  if (h != NULL)
                    h->flags |= XCOFF_LDREL;
-                 break;
-               case R_TOC:
-               case R_GL:
-               case R_TCL:
-               case R_TRL:
-               case R_TRLA:
-                 /* We should never need a .loader reloc for a TOC
-                    relative reloc.  */
-                 break;
                }
            }
 
@@ -2941,8 +2967,12 @@ bfd_xcoff_link_count_reloc (bfd *output_bfd,
       return FALSE;
     }
 
-  h->flags |= XCOFF_REF_REGULAR | XCOFF_LDREL;
-  ++xcoff_hash_table (info)->ldrel_count;
+  h->flags |= XCOFF_REF_REGULAR;
+  if (xcoff_hash_table (info)->loader_section)
+    {
+      h->flags |= XCOFF_LDREL;
+      ++xcoff_hash_table (info)->ldrel_count;
+    }
 
   /* Mark the symbol to avoid garbage collection.  */
   if (! xcoff_mark_symbol (info, h))
@@ -3024,101 +3054,34 @@ xcoff_final_definition_p (bfd *input_bfd, struct xcoff_link_hash_entry *h,
     }
 }
 
+/* See if H should have a loader symbol associated with it.  */
+
 static bfd_boolean
-xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
+xcoff_build_ldsym (struct xcoff_loader_info *ldinfo,
+                  struct xcoff_link_hash_entry *h)
 {
-  struct xcoff_loader_info *ldinfo = (struct xcoff_loader_info *) p;
   bfd_size_type amt;
 
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct xcoff_link_hash_entry *) h->root.u.i.link;
-
-  /* __rtinit, this symbol has special handling. */
-  if (h->flags & XCOFF_RTINIT)
-      return TRUE;
-
-  /* If this is a final link, and the symbol was defined as a common
-     symbol in a regular object file, and there was no definition in
-     any dynamic object, then the linker will have allocated space for
-     the symbol in a common section but the XCOFF_DEF_REGULAR flag
-     will not have been set.  */
-  if (h->root.type == bfd_link_hash_defined
-      && (h->flags & XCOFF_DEF_REGULAR) == 0
-      && (h->flags & XCOFF_REF_REGULAR) != 0
-      && (h->flags & XCOFF_DEF_DYNAMIC) == 0
-      && (bfd_is_abs_section (h->root.u.def.section)
-         || (h->root.u.def.section->owner->flags & DYNAMIC) == 0))
-    h->flags |= XCOFF_DEF_REGULAR;
-
-  /* If all defined symbols should be exported, mark them now.  We
-     don't want to export the actual functions, just the function
-     descriptors.  */
-  if (xcoff_auto_export_p (h, ldinfo->auto_export_flags))
-    h->flags |= XCOFF_EXPORT;
-
-  /* We don't want to garbage collect symbols which are not defined in
-     XCOFF files.  This is a convenient place to mark them.  */
-  if (xcoff_hash_table (ldinfo->info)->gc
-      && (h->flags & XCOFF_MARK) == 0
-      && (h->root.type == bfd_link_hash_defined
-         || h->root.type == bfd_link_hash_defweak)
-      && (h->root.u.def.section->owner == NULL
-         || (h->root.u.def.section->owner->xvec
-             != ldinfo->info->output_bfd->xvec)))
-    h->flags |= XCOFF_MARK;
-
-  /* If this symbol is exported, but not defined, we need to try to
-     define it.  */
+  /* Warn if this symbol is exported but not defined.  */
   if ((h->flags & XCOFF_EXPORT) != 0
       && (h->flags & XCOFF_WAS_UNDEFINED) != 0)
     {
       (*_bfd_error_handler)
        (_("warning: attempt to export undefined symbol `%s'"),
         h->root.root.string);
-      h->ldsym = NULL;
       return TRUE;
     }
 
-  /* If this is still a common symbol, and it wasn't garbage
-     collected, we need to actually allocate space for it in the .bss
-     section.  */
-  if (h->root.type == bfd_link_hash_common
-      && (! xcoff_hash_table (ldinfo->info)->gc
-         || (h->flags & XCOFF_MARK) != 0)
-      && h->root.u.c.p->section->size == 0)
-    {
-      BFD_ASSERT (bfd_is_com_section (h->root.u.c.p->section));
-      h->root.u.c.p->section->size = h->root.u.c.size;
-    }
-
   /* We need to add a symbol to the .loader section if it is mentioned
      in a reloc which we are copying to the .loader section and it was
      not defined or common, or if it is the entry point, or if it is
      being exported.  */
-
   if (((h->flags & XCOFF_LDREL) == 0
        || h->root.type == bfd_link_hash_defined
        || h->root.type == bfd_link_hash_defweak
        || h->root.type == bfd_link_hash_common)
       && (h->flags & XCOFF_ENTRY) == 0
       && (h->flags & XCOFF_EXPORT) == 0)
-    {
-      h->ldsym = NULL;
-      return TRUE;
-    }
-
-  /* We don't need to add this symbol if we did garbage collection and
-     we did not mark this symbol.  */
-  if (xcoff_hash_table (ldinfo->info)->gc
-      && (h->flags & XCOFF_MARK) == 0)
-    {
-      h->ldsym = NULL;
-      return TRUE;
-    }
-
-  /* We may have already processed this symbol due to the recursive
-     call above.  */
-  if ((h->flags & XCOFF_BUILT_LDSYM) != 0)
     return TRUE;
 
   /* We need to add this symbol to the .loader symbols.  */
@@ -3151,6 +3114,71 @@ xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
     return FALSE;
 
   h->flags |= XCOFF_BUILT_LDSYM;
+  return TRUE;
+}
+
+/* An xcoff_htab_traverse callback that is called for each symbol
+   once garbage collection is complete.  */
+
+static bfd_boolean
+xcoff_post_gc_symbol (struct xcoff_link_hash_entry *h, void * p)
+{
+  struct xcoff_loader_info *ldinfo = (struct xcoff_loader_info *) p;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct xcoff_link_hash_entry *) h->root.u.i.link;
+
+  /* __rtinit, this symbol has special handling. */
+  if (h->flags & XCOFF_RTINIT)
+    return TRUE;
+
+  /* If this is a final link, and the symbol was defined as a common
+     symbol in a regular object file, and there was no definition in
+     any dynamic object, then the linker will have allocated space for
+     the symbol in a common section but the XCOFF_DEF_REGULAR flag
+     will not have been set.  */
+  if (h->root.type == bfd_link_hash_defined
+      && (h->flags & XCOFF_DEF_REGULAR) == 0
+      && (h->flags & XCOFF_REF_REGULAR) != 0
+      && (h->flags & XCOFF_DEF_DYNAMIC) == 0
+      && (bfd_is_abs_section (h->root.u.def.section)
+         || (h->root.u.def.section->owner->flags & DYNAMIC) == 0))
+    h->flags |= XCOFF_DEF_REGULAR;
+
+  /* We don't want to garbage collect symbols which are not defined in
+     XCOFF files.  This is a convenient place to mark them.  */
+  if (xcoff_hash_table (ldinfo->info)->gc
+      && (h->flags & XCOFF_MARK) == 0
+      && (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+      && (h->root.u.def.section->owner == NULL
+         || (h->root.u.def.section->owner->xvec
+             != ldinfo->info->output_bfd->xvec)))
+    h->flags |= XCOFF_MARK;
+
+  /* Skip discarded symbols.  */
+  if (xcoff_hash_table (ldinfo->info)->gc
+      && (h->flags & XCOFF_MARK) == 0)
+    return TRUE;
+
+  /* If this is still a common symbol, and it wasn't garbage
+     collected, we need to actually allocate space for it in the .bss
+     section.  */
+  if (h->root.type == bfd_link_hash_common
+      && h->root.u.c.p->section->size == 0)
+    {
+      BFD_ASSERT (bfd_is_com_section (h->root.u.c.p->section));
+      h->root.u.c.p->section->size = h->root.u.c.size;
+    }
+
+  if (xcoff_hash_table (ldinfo->info)->loader_section)
+    {
+      if (xcoff_auto_export_p (h, ldinfo->auto_export_flags))
+       h->flags |= XCOFF_EXPORT;
+
+      if (!xcoff_build_ldsym (ldinfo, h))
+       return FALSE;
+    }
 
   return TRUE;
 }
@@ -3246,6 +3274,119 @@ xcoff_keep_symbol_p (struct bfd_link_info *info, bfd *input_bfd,
   return 1;
 }
 
+/* Lay out the .loader section, filling in the header and the import paths.
+   LIBPATH is as for bfd_xcoff_size_dynamic_sections.  */
+
+static bfd_boolean
+xcoff_build_loader_section (struct xcoff_loader_info *ldinfo,
+                           const char *libpath)
+{
+  bfd *output_bfd;
+  struct xcoff_link_hash_table *htab;
+  struct internal_ldhdr *ldhdr;
+  struct xcoff_import_file *fl;
+  bfd_size_type stoff;
+  size_t impsize, impcount;
+  asection *lsec;
+  char *out;
+
+  /* Work out the size of the import file names.  Each import file ID
+     consists of three null terminated strings: the path, the file
+     name, and the archive member name.  The first entry in the list
+     of names is the path to use to find objects, which the linker has
+     passed in as the libpath argument.  For some reason, the path
+     entry in the other import file names appears to always be empty.  */
+  output_bfd = ldinfo->output_bfd;
+  htab = xcoff_hash_table (ldinfo->info);
+  impsize = strlen (libpath) + 3;
+  impcount = 1;
+  for (fl = htab->imports; fl != NULL; fl = fl->next)
+    {
+      ++impcount;
+      impsize += (strlen (fl->path)
+                 + strlen (fl->file)
+                 + strlen (fl->member)
+                 + 3);
+    }
+
+  /* Set up the .loader section header.  */
+  ldhdr = &htab->ldhdr;
+  ldhdr->l_version = bfd_xcoff_ldhdr_version(output_bfd);
+  ldhdr->l_nsyms = ldinfo->ldsym_count;
+  ldhdr->l_nreloc = htab->ldrel_count;
+  ldhdr->l_istlen = impsize;
+  ldhdr->l_nimpid = impcount;
+  ldhdr->l_impoff = (bfd_xcoff_ldhdrsz (output_bfd)
+                    + ldhdr->l_nsyms * bfd_xcoff_ldsymsz (output_bfd)
+                    + ldhdr->l_nreloc * bfd_xcoff_ldrelsz (output_bfd));
+  ldhdr->l_stlen = ldinfo->string_size;
+  stoff = ldhdr->l_impoff + impsize;
+  if (ldinfo->string_size == 0)
+    ldhdr->l_stoff = 0;
+  else
+    ldhdr->l_stoff = stoff;
+
+  /* 64 bit elements to ldhdr
+     The swap out routine for 32 bit will ignore them.
+     Nothing fancy, symbols come after the header and relocs come
+     after symbols.  */
+  ldhdr->l_symoff = bfd_xcoff_ldhdrsz (output_bfd);
+  ldhdr->l_rldoff = (bfd_xcoff_ldhdrsz (output_bfd)
+                    + ldhdr->l_nsyms * bfd_xcoff_ldsymsz (output_bfd));
+
+  /* We now know the final size of the .loader section.  Allocate
+     space for it.  */
+  lsec = htab->loader_section;
+  lsec->size = stoff + ldhdr->l_stlen;
+  lsec->contents = bfd_zalloc (output_bfd, lsec->size);
+  if (lsec->contents == NULL)
+    return FALSE;
+
+  /* Set up the header.  */
+  bfd_xcoff_swap_ldhdr_out (output_bfd, ldhdr, lsec->contents);
+
+  /* Set up the import file names.  */
+  out = (char *) lsec->contents + ldhdr->l_impoff;
+  strcpy (out, libpath);
+  out += strlen (libpath) + 1;
+  *out++ = '\0';
+  *out++ = '\0';
+  for (fl = htab->imports; fl != NULL; fl = fl->next)
+    {
+      const char *s;
+
+      s = fl->path;
+      while ((*out++ = *s++) != '\0')
+       ;
+      s = fl->file;
+      while ((*out++ = *s++) != '\0')
+       ;
+      s = fl->member;
+      while ((*out++ = *s++) != '\0')
+       ;
+    }
+
+  BFD_ASSERT ((bfd_size_type) ((bfd_byte *) out - lsec->contents) == stoff);
+
+  /* Set up the symbol string table.  */
+  if (ldinfo->string_size > 0)
+    {
+      memcpy (out, ldinfo->strings, ldinfo->string_size);
+      free (ldinfo->strings);
+      ldinfo->strings = NULL;
+    }
+
+  /* We can't set up the symbol table or the relocs yet, because we
+     don't yet know the final position of the various sections.  The
+     .loader symbols are written out when the corresponding normal
+     symbols are written out in xcoff_link_input_bfd or
+     xcoff_write_global_symbol.  The .loader relocs are written out
+     when the corresponding normal relocs are handled in
+     xcoff_link_input_bfd.  */
+
+  return TRUE;
+}
+
 /* Build the .loader section.  This is called by the XCOFF linker
    emulation before_allocation routine.  We must set the size of the
    .loader section before the linker lays out the output file.
@@ -3277,14 +3418,8 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
                                 asection **special_sections,
                                 bfd_boolean rtld)
 {
-  asection *lsec;
   struct xcoff_loader_info ldinfo;
   int i;
-  size_t impsize, impcount;
-  struct xcoff_import_file *fl;
-  struct internal_ldhdr *ldhdr;
-  bfd_size_type stoff;
-  char *out;
   asection *sec;
   bfd *sub;
   struct bfd_strtab_hash *debug_strtab;
@@ -3316,7 +3451,8 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
   xcoff_hash_table (info)->rtld = rtld;
 
   /* __rtinit */
-  if (info->init_function || info->fini_function || rtld)
+  if (xcoff_hash_table (info)->loader_section
+      && (info->init_function || info->fini_function || rtld))
     {
       struct xcoff_link_hash_entry *hsym;
       struct internal_ldsym *ldsym;
@@ -3432,103 +3568,15 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
     /* I'm not sure what to do in this bizarre case.  */
     return TRUE;
 
-  xcoff_link_hash_traverse (xcoff_hash_table (info), xcoff_build_ldsyms,
+  xcoff_link_hash_traverse (xcoff_hash_table (info), xcoff_post_gc_symbol,
                            (void *) &ldinfo);
   if (ldinfo.failed)
     goto error_return;
 
-  /* Work out the size of the import file names.  Each import file ID
-     consists of three null terminated strings: the path, the file
-     name, and the archive member name.  The first entry in the list
-     of names is the path to use to find objects, which the linker has
-     passed in as the libpath argument.  For some reason, the path
-     entry in the other import file names appears to always be empty.  */
-  impsize = strlen (libpath) + 3;
-  impcount = 1;
-  for (fl = xcoff_hash_table (info)->imports; fl != NULL; fl = fl->next)
-    {
-      ++impcount;
-      impsize += (strlen (fl->path)
-                 + strlen (fl->file)
-                 + strlen (fl->member)
-                 + 3);
-    }
-
-  /* Set up the .loader section header.  */
-  ldhdr = &xcoff_hash_table (info)->ldhdr;
-  ldhdr->l_version = bfd_xcoff_ldhdr_version(output_bfd);
-  ldhdr->l_nsyms = ldinfo.ldsym_count;
-  ldhdr->l_nreloc = xcoff_hash_table (info)->ldrel_count;
-  ldhdr->l_istlen = impsize;
-  ldhdr->l_nimpid = impcount;
-  ldhdr->l_impoff = (bfd_xcoff_ldhdrsz(output_bfd)
-                    + ldhdr->l_nsyms * bfd_xcoff_ldsymsz(output_bfd)
-                    + ldhdr->l_nreloc * bfd_xcoff_ldrelsz(output_bfd));
-  ldhdr->l_stlen = ldinfo.string_size;
-  stoff = ldhdr->l_impoff + impsize;
-  if (ldinfo.string_size == 0)
-    ldhdr->l_stoff = 0;
-  else
-    ldhdr->l_stoff = stoff;
-
-  /* 64 bit elements to ldhdr
-     The swap out routine for 32 bit will ignore them.
-     Nothing fancy, symbols come after the header and relocs come
-     after symbols.  */
-  ldhdr->l_symoff = bfd_xcoff_ldhdrsz (output_bfd);
-  ldhdr->l_rldoff = (bfd_xcoff_ldhdrsz (output_bfd)
-                    + ldhdr->l_nsyms * bfd_xcoff_ldsymsz (output_bfd));
-
-  /* We now know the final size of the .loader section.  Allocate
-     space for it.  */
-  lsec = xcoff_hash_table (info)->loader_section;
-  lsec->size = stoff + ldhdr->l_stlen;
-  lsec->contents = bfd_zalloc (output_bfd, lsec->size);
-  if (lsec->contents == NULL)
+  if (xcoff_hash_table (info)->loader_section
+      && !xcoff_build_loader_section (&ldinfo, libpath))
     goto error_return;
 
-  /* Set up the header.  */
-  bfd_xcoff_swap_ldhdr_out (output_bfd, ldhdr, lsec->contents);
-
-  /* Set up the import file names.  */
-  out = (char *) lsec->contents + ldhdr->l_impoff;
-  strcpy (out, libpath);
-  out += strlen (libpath) + 1;
-  *out++ = '\0';
-  *out++ = '\0';
-  for (fl = xcoff_hash_table (info)->imports; fl != NULL; fl = fl->next)
-    {
-      const char *s;
-
-      s = fl->path;
-      while ((*out++ = *s++) != '\0')
-       ;
-      s = fl->file;
-      while ((*out++ = *s++) != '\0')
-       ;
-      s = fl->member;
-      while ((*out++ = *s++) != '\0')
-       ;
-    }
-
-  BFD_ASSERT ((bfd_size_type) ((bfd_byte *) out - lsec->contents) == stoff);
-
-  /* Set up the symbol string table.  */
-  if (ldinfo.string_size > 0)
-    {
-      memcpy (out, ldinfo.strings, ldinfo.string_size);
-      free (ldinfo.strings);
-      ldinfo.strings = NULL;
-    }
-
-  /* We can't set up the symbol table or the relocs yet, because we
-     don't yet know the final position of the various sections.  The
-     .loader symbols are written out when the corresponding normal
-     symbols are written out in xcoff_link_input_bfd or
-     xcoff_write_global_symbol.  The .loader relocs are written out
-     when the corresponding normal relocs are handled in
-     xcoff_link_input_bfd.  */
-
   /* Allocate space for the magic sections.  */
   sec = xcoff_hash_table (info)->linkage_section;
   if (sec->size > 0)
@@ -3743,6 +3791,91 @@ bfd_xcoff_link_generate_rtinit (bfd *abfd,
   return TRUE;
 }
 \f
+/* Return the section that defines H.  Return null if no section does.  */
+
+static asection *
+xcoff_symbol_section (struct xcoff_link_hash_entry *h)
+{
+  switch (h->root.type)
+    {
+    case bfd_link_hash_defined:
+    case bfd_link_hash_defweak:
+      return h->root.u.def.section;
+
+    case bfd_link_hash_common:
+      return h->root.u.c.p->section;
+
+    default:
+      return NULL;
+    }
+}
+
+/* Add a .loader relocation for input relocation IREL.  If the loader
+   relocation should be against an output section, HSEC points to the
+   input section that IREL is against, otherwise HSEC is null.  H is the
+   symbol that IREL is against, or null if it isn't against a global symbol.
+   REFERENCE_BFD is the bfd to use in error messages about the relocation.  */
+
+static bfd_boolean
+xcoff_create_ldrel (bfd *output_bfd, struct xcoff_final_link_info *finfo,
+                   asection *output_section, bfd *reference_bfd,
+                   struct internal_reloc *irel, asection *hsec,
+                   struct xcoff_link_hash_entry *h)
+{
+  struct internal_ldrel ldrel;
+
+  ldrel.l_vaddr = irel->r_vaddr;
+  if (hsec != NULL)
+    {
+      const char *secname;
+
+      secname = hsec->output_section->name;
+      if (strcmp (secname, ".text") == 0)
+       ldrel.l_symndx = 0;
+      else if (strcmp (secname, ".data") == 0)
+       ldrel.l_symndx = 1;
+      else if (strcmp (secname, ".bss") == 0)
+       ldrel.l_symndx = 2;
+      else
+       {
+         (*_bfd_error_handler)
+           (_("%B: loader reloc in unrecognized section `%s'"),
+            reference_bfd, secname);
+         bfd_set_error (bfd_error_nonrepresentable_section);
+         return FALSE;
+       }
+    }
+  else if (h != NULL)
+    {
+      if (h->ldindx < 0)
+       {
+         (*_bfd_error_handler)
+           (_("%B: `%s' in loader reloc but not loader sym"),
+            reference_bfd, h->root.root.string);
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+      ldrel.l_symndx = h->ldindx;
+    }
+  else
+    ldrel.l_symndx = -(bfd_size_type) 1;
+
+  ldrel.l_rtype = (irel->r_size << 8) | irel->r_type;
+  ldrel.l_rsecnm = output_section->target_index;
+  if (xcoff_hash_table (finfo->info)->textro
+      && strcmp (output_section->name, ".text") == 0)
+    {
+      (*_bfd_error_handler)
+       (_("%B: loader reloc in read-only section %A"),
+        reference_bfd, output_section);
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
+    }
+  bfd_xcoff_swap_ldrel_out (output_bfd, &ldrel, finfo->ldrel);
+  finfo->ldrel += bfd_xcoff_ldrelsz (output_bfd);
+  return TRUE;
+}
+
 /* Link an input file into the linker output file.  This function
    handles all the sections and relocations of the input file at once.  */
 
@@ -4455,7 +4588,6 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
          for (; irel < irelend; irel++, rel_hash++)
            {
              struct xcoff_link_hash_entry *h = NULL;
-             struct internal_ldrel ldrel;
 
              *rel_hash = NULL;
 
@@ -4588,97 +4720,20 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                    }
                }
 
-             switch (irel->r_type)
+             if (xcoff_need_ldrel_p (finfo->info, irel, h))
                {
-               default:
-                 if (h == NULL
-                     || h->root.type == bfd_link_hash_defined
-                     || h->root.type == bfd_link_hash_defweak
-                     || h->root.type == bfd_link_hash_common)
-                   break;
-                 /* Fall through.  */
-               case R_POS:
-               case R_NEG:
-               case R_RL:
-               case R_RLA:
-                 if (h != NULL
-                     && (h->root.type == bfd_link_hash_defined
-                         || h->root.type == bfd_link_hash_defweak)
-                     && bfd_is_abs_section (h->root.u.def.section))
-                   break;
-                 /* This reloc needs to be copied into the .loader
-                    section.  */
-                 ldrel.l_vaddr = irel->r_vaddr;
-                 if (r_symndx == -1)
-                   ldrel.l_symndx = -(bfd_size_type ) 1;
-                 else if (h == NULL
-                          || (h->root.type == bfd_link_hash_defined
-                              || h->root.type == bfd_link_hash_defweak
-                              || h->root.type == bfd_link_hash_common))
-                   {
-                     asection *sec;
+                 asection *sec;
 
-                     if (h == NULL)
-                       sec = xcoff_data (input_bfd)->csects[r_symndx];
-                     else if (h->root.type == bfd_link_hash_common)
-                       sec = h->root.u.c.p->section;
-                     else
-                       sec = h->root.u.def.section;
-                     sec = sec->output_section;
-
-                     if (strcmp (sec->name, ".text") == 0)
-                       ldrel.l_symndx = 0;
-                     else if (strcmp (sec->name, ".data") == 0)
-                       ldrel.l_symndx = 1;
-                     else if (strcmp (sec->name, ".bss") == 0)
-                       ldrel.l_symndx = 2;
-                     else
-                       {
-                         (*_bfd_error_handler)
-                           (_("%B: loader reloc in unrecognized section `%A'"),
-                            input_bfd, sec);
-                         bfd_set_error (bfd_error_nonrepresentable_section);
-                         return FALSE;
-                       }
-                   }
+                 if (r_symndx == -1)
+                   sec = NULL;
+                 else if (h == NULL)
+                   sec = xcoff_data (input_bfd)->csects[r_symndx];
                  else
-                   {
-                     if (h->ldindx < 0)
-                       {
-                         (*_bfd_error_handler)
-                           (_("%B: `%s' in loader reloc but not loader sym"),
-                            input_bfd,
-                            h->root.root.string);
-                         bfd_set_error (bfd_error_bad_value);
-                         return FALSE;
-                       }
-                     ldrel.l_symndx = h->ldindx;
-                   }
-                 ldrel.l_rtype = (irel->r_size << 8) | irel->r_type;
-                 ldrel.l_rsecnm = o->output_section->target_index;
-                 if (xcoff_hash_table (finfo->info)->textro
-                     && strcmp (o->output_section->name, ".text") == 0)
-                   {
-                     (*_bfd_error_handler)
-                       (_("%B: loader reloc in read-only section %A"),
-                        input_bfd, o->output_section);
-                     bfd_set_error (bfd_error_invalid_operation);
-                     return FALSE;
-                   }
-                 bfd_xcoff_swap_ldrel_out (output_bfd, &ldrel,
-                                           finfo->ldrel);
-
-                 finfo->ldrel += bfd_xcoff_ldrelsz(output_bfd);
-                 break;
-
-               case R_TOC:
-               case R_GL:
-               case R_TCL:
-               case R_TRL:
-               case R_TRLA:
-                 /* We should never need a .loader reloc for a TOC
-                    relative reloc.  */
-                 break;
+                   sec = xcoff_symbol_section (h);
+                 if (!xcoff_create_ldrel (output_bfd, finfo,
+                                          o->output_section, input_bfd,
+                                          irel, sec, h))
+                   return FALSE;
                }
            }
 
@@ -5035,7 +5090,6 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       asection *osec;
       int oindx;
       struct internal_reloc *irel;
-      struct internal_ldrel ldrel;
       struct internal_syment irsym;
       union internal_auxent iraux;
 
@@ -5088,12 +5142,9 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       finfo->section_info[oindx].rel_hashes[osec->reloc_count] = NULL;
       ++osec->reloc_count;
 
-      ldrel.l_vaddr = irel->r_vaddr;
-      ldrel.l_symndx = h->ldindx;
-      ldrel.l_rtype = (irel->r_size << 8) | R_POS;
-      ldrel.l_rsecnm = oindx;
-      bfd_xcoff_swap_ldrel_out (output_bfd, &ldrel, finfo->ldrel);
-      finfo->ldrel += bfd_xcoff_ldrelsz(output_bfd);
+      if (!xcoff_create_ldrel (output_bfd, finfo, osec,
+                              output_bfd, irel, NULL, h))
+       return FALSE;
 
       /* We need to emit a symbol to define a csect which holds
         the reloc.  */
@@ -5159,7 +5210,6 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       struct xcoff_link_hash_entry *hentry;
       asection *esec;
       struct internal_reloc *irel;
-      struct internal_ldrel ldrel;
       asection *tsec;
       unsigned int reloc_size, byte_size;
 
@@ -5197,26 +5247,9 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       finfo->section_info[oindx].rel_hashes[osec->reloc_count] = NULL;
       ++osec->reloc_count;
 
-      ldrel.l_vaddr = irel->r_vaddr;
-      if (strcmp (esec->output_section->name, ".text") == 0)
-       ldrel.l_symndx = 0;
-      else if (strcmp (esec->output_section->name, ".data") == 0)
-       ldrel.l_symndx = 1;
-      else if (strcmp (esec->output_section->name, ".bss") == 0)
-       ldrel.l_symndx = 2;
-      else
-       {
-         (*_bfd_error_handler)
-           (_("%s: loader reloc in unrecognized section `%s'"),
-            bfd_get_filename (output_bfd),
-            esec->output_section->name);
-         bfd_set_error (bfd_error_nonrepresentable_section);
-         return FALSE;
-       }
-      ldrel.l_rtype = (reloc_size << 8) | R_POS;
-      ldrel.l_rsecnm = oindx;
-      bfd_xcoff_swap_ldrel_out (output_bfd, &ldrel, finfo->ldrel);
-      finfo->ldrel += bfd_xcoff_ldrelsz(output_bfd);
+      if (!xcoff_create_ldrel (output_bfd, finfo, osec,
+                              output_bfd, irel, esec, NULL))
+       return FALSE;
 
       /* There are three items to write out,
         the address of the code
@@ -5259,26 +5292,9 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       finfo->section_info[oindx].rel_hashes[osec->reloc_count] = NULL;
       ++osec->reloc_count;
 
-      ldrel.l_vaddr = irel->r_vaddr;
-      if (strcmp (tsec->output_section->name, ".text") == 0)
-       ldrel.l_symndx = 0;
-      else if (strcmp (tsec->output_section->name, ".data") == 0)
-       ldrel.l_symndx = 1;
-      else if (strcmp (tsec->output_section->name, ".bss") == 0)
-       ldrel.l_symndx = 2;
-      else
-       {
-         (*_bfd_error_handler)
-           (_("%s: loader reloc in unrecognized section `%s'"),
-            bfd_get_filename (output_bfd),
-            tsec->output_section->name);
-         bfd_set_error (bfd_error_nonrepresentable_section);
-         return FALSE;
-       }
-      ldrel.l_rtype = (reloc_size << 8) | R_POS;
-      ldrel.l_rsecnm = oindx;
-      bfd_xcoff_swap_ldrel_out (output_bfd, &ldrel, finfo->ldrel);
-      finfo->ldrel += bfd_xcoff_ldrelsz(output_bfd);
+      if (!xcoff_create_ldrel (output_bfd, finfo, osec,
+                              output_bfd, irel, tsec, NULL))
+       return FALSE;
     }
 
   if (h->indx >= 0 || finfo->info->strip == strip_all)
@@ -5440,7 +5456,6 @@ xcoff_reloc_link_order (bfd *output_bfd,
   bfd_vma addend;
   struct internal_reloc *irel;
   struct xcoff_link_hash_entry **rel_hash_ptr;
-  struct internal_ldrel ldrel;
 
   if (link_order->type == bfd_section_reloc_link_order)
     /* We need to somehow locate a symbol in the right section.  The
@@ -5468,22 +5483,12 @@ xcoff_reloc_link_order (bfd *output_bfd,
       return TRUE;
     }
 
-  if (h->root.type == bfd_link_hash_common)
-    {
-      hsec = h->root.u.c.p->section;
-      hval = 0;
-    }
-  else if (h->root.type == bfd_link_hash_defined
-          || h->root.type == bfd_link_hash_defweak)
-    {
-      hsec = h->root.u.def.section;
-      hval = h->root.u.def.value;
-    }
+  hsec = xcoff_symbol_section (h);
+  if (h->root.type == bfd_link_hash_defined
+      || h->root.type == bfd_link_hash_defweak)
+    hval = h->root.u.def.value;
   else
-    {
-      hsec = NULL;
-      hval = 0;
-    }
+    hval = 0;
 
   addend = link_order->u.reloc.p->addend;
   if (hsec != NULL)
@@ -5558,49 +5563,13 @@ xcoff_reloc_link_order (bfd *output_bfd,
   ++output_section->reloc_count;
 
   /* Now output the reloc to the .loader section.  */
-
-  ldrel.l_vaddr = irel->r_vaddr;
-
-  if (hsec != NULL)
+  if (xcoff_hash_table (finfo->info)->loader_section)
     {
-      const char *secname;
-
-      secname = hsec->output_section->name;
-
-      if (strcmp (secname, ".text") == 0)
-       ldrel.l_symndx = 0;
-      else if (strcmp (secname, ".data") == 0)
-       ldrel.l_symndx = 1;
-      else if (strcmp (secname, ".bss") == 0)
-       ldrel.l_symndx = 2;
-      else
-       {
-         (*_bfd_error_handler)
-           (_("%s: loader reloc in unrecognized section `%s'"),
-            bfd_get_filename (output_bfd), secname);
-         bfd_set_error (bfd_error_nonrepresentable_section);
-         return FALSE;
-       }
-    }
-  else
-    {
-      if (h->ldindx < 0)
-       {
-         (*_bfd_error_handler)
-           (_("%s: `%s' in loader reloc but not loader sym"),
-            bfd_get_filename (output_bfd),
-            h->root.root.string);
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
-       }
-      ldrel.l_symndx = h->ldindx;
+      if (!xcoff_create_ldrel (output_bfd, finfo, output_section,
+                              output_bfd, irel, hsec, h))
+       return FALSE;
     }
 
-  ldrel.l_rtype = (irel->r_size << 8) | irel->r_type;
-  ldrel.l_rsecnm = output_section->target_index;
-  bfd_xcoff_swap_ldrel_out (output_bfd, &ldrel, finfo->ldrel);
-  finfo->ldrel += bfd_xcoff_ldrelsz(output_bfd);
-
   return TRUE;
 }
 
@@ -5646,12 +5615,20 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
   finfo.contents = NULL;
   finfo.external_relocs = NULL;
 
-  finfo.ldsym = (xcoff_hash_table (info)->loader_section->contents
-                + bfd_xcoff_ldhdrsz (abfd));
-  finfo.ldrel = (xcoff_hash_table (info)->loader_section->contents
-                + bfd_xcoff_ldhdrsz(abfd)
-                + (xcoff_hash_table (info)->ldhdr.l_nsyms
-                   * bfd_xcoff_ldsymsz(abfd)));
+  if (xcoff_hash_table (info)->loader_section)
+    {
+      finfo.ldsym = (xcoff_hash_table (info)->loader_section->contents
+                    + bfd_xcoff_ldhdrsz (abfd));
+      finfo.ldrel = (xcoff_hash_table (info)->loader_section->contents
+                    + bfd_xcoff_ldhdrsz (abfd)
+                    + (xcoff_hash_table (info)->ldhdr.l_nsyms
+                       * bfd_xcoff_ldsymsz (abfd)));
+    }
+  else
+    {
+      finfo.ldsym = NULL;
+      finfo.ldrel = NULL;
+    }
 
   xcoff_data (abfd)->coff.link_info = info;
 
@@ -6138,13 +6115,16 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
     }
 
   /* Write out the loader section contents.  */
-  BFD_ASSERT ((bfd_byte *) finfo.ldrel
-             == (xcoff_hash_table (info)->loader_section->contents
-                 + xcoff_hash_table (info)->ldhdr.l_impoff));
   o = xcoff_hash_table (info)->loader_section;
-  if (! bfd_set_section_contents (abfd, o->output_section, o->contents,
-                                 (file_ptr) o->output_offset, o->size))
-    goto error_return;
+  if (o)
+    {
+      BFD_ASSERT ((bfd_byte *) finfo.ldrel
+                 == (xcoff_hash_table (info)->loader_section->contents
+                     + xcoff_hash_table (info)->ldhdr.l_impoff));
+      if (!bfd_set_section_contents (abfd, o->output_section, o->contents,
+                                    (file_ptr) o->output_offset, o->size))
+       goto error_return;
+    }
 
   /* Write out the magic sections.  */
   o = xcoff_hash_table (info)->linkage_section;