#include "coff/xcoff.h"
#include "libcoff.h"
#include "libxcoff.h"
+#include "libiberty.h"
/* This file holds the XCOFF linker code. */
} *toc_rel_hashes;
};
+/* Information that the XCOFF linker collects about an archive. */
+struct xcoff_archive_info
+{
+ /* The archive described by this entry. */
+ bfd *archive;
+
+ /* The import path and import filename to use when referring to
+ this archive in the .loader section. */
+ const char *imppath;
+ const char *impfile;
+
+ /* True if the archive contains a dynamic object. */
+ unsigned int contains_shared_object_p : 1;
+
+ /* True if the previous field is valid. */
+ unsigned int know_contains_shared_object_p : 1;
+};
+
struct xcoff_link_hash_table
{
struct bfd_link_hash_table root;
}
*size_list;
+ /* Information about archives. */
+ htab_t archive_info;
+
/* Magic sections: _text, _etext, _data, _edata, _end, end. */
asection *special_sections[XCOFF_NUMBER_OF_SPECIAL_SECTIONS];
};
return ldhdr.l_nreloc;
}
\f
+/* Hash functions for xcoff_link_hash_table's archive_info. */
+
+static hashval_t
+xcoff_archive_info_hash (const void *data)
+{
+ const struct xcoff_archive_info *info;
+
+ info = (const struct xcoff_archive_info *) data;
+ return htab_hash_pointer (info->archive);
+}
+
+static int
+xcoff_archive_info_eq (const void *data1, const void *data2)
+{
+ const struct xcoff_archive_info *info1;
+ const struct xcoff_archive_info *info2;
+
+ info1 = (const struct xcoff_archive_info *) data1;
+ info2 = (const struct xcoff_archive_info *) data2;
+ return info1->archive == info2->archive;
+}
+
+/* Return information about archive ARCHIVE. Return NULL on error. */
+
+static struct xcoff_archive_info *
+xcoff_get_archive_info (struct bfd_link_info *info, bfd *archive)
+{
+ struct xcoff_link_hash_table *htab;
+ struct xcoff_archive_info *entryp, entry;
+ void **slot;
+
+ htab = xcoff_hash_table (info);
+ entry.archive = archive;
+ slot = htab_find_slot (htab->archive_info, &entry, INSERT);
+ if (!slot)
+ return NULL;
+
+ entryp = *slot;
+ if (!entryp)
+ {
+ entryp = bfd_zalloc (archive, sizeof (entry));
+ if (!entryp)
+ return NULL;
+
+ entryp->archive = archive;
+ *slot = entryp;
+ }
+ return entryp;
+}
+\f
/* Routine to create an entry in an XCOFF link hash table. */
static struct bfd_hash_entry *
ret->file_align = 0;
ret->textro = FALSE;
ret->gc = FALSE;
+ ret->archive_info = htab_create (37, xcoff_archive_info_hash,
+ xcoff_archive_info_eq, NULL);
memset (ret->special_sections, 0, sizeof ret->special_sections);
/* The linker will always generate a full a.out header. We need to
require_internal, internal_relocs);
}
\f
+/* Split FILENAME into an import path and an import filename,
+ storing them in *IMPPATH and *IMPFILE respectively. */
+
+bfd_boolean
+bfd_xcoff_split_import_path (bfd *abfd, const char *filename,
+ const char **imppath, const char **impfile)
+{
+ const char *basename;
+ size_t length;
+ char *path;
+
+ basename = lbasename (filename);
+ length = basename - filename;
+ if (length == 0)
+ /* The filename has no directory component, so use an empty path. */
+ *imppath = "";
+ else if (length == 1)
+ /* The filename is in the root directory. */
+ *imppath = "/";
+ else
+ {
+ /* Extract the (non-empty) directory part. Note that we don't
+ need to strip duplicate directory separators from any part
+ of the string; the native linker doesn't do that either. */
+ path = bfd_alloc (abfd, length);
+ if (path == NULL)
+ return FALSE;
+ memcpy (path, filename, length - 1);
+ path[length - 1] = 0;
+ *imppath = path;
+ }
+ *impfile = basename;
+ return TRUE;
+}
+
+/* Set ARCHIVE's import path as though its filename had been given
+ as FILENAME. */
+
+bfd_boolean
+bfd_xcoff_set_archive_import_path (struct bfd_link_info *info,
+ bfd *archive, const char *filename)
+{
+ struct xcoff_archive_info *archive_info;
+
+ archive_info = xcoff_get_archive_info (info, archive);
+ return (archive_info != NULL
+ && bfd_xcoff_split_import_path (archive, filename,
+ &archive_info->imppath,
+ &archive_info->impfile));
+}
+
+/* H is an imported symbol. Set the import module's path, file and member
+ to IMPATH, IMPFILE and IMPMEMBER respectively. All three are null if
+ no specific import module is specified. */
+
+static bfd_boolean
+xcoff_set_import_path (struct bfd_link_info *info,
+ struct xcoff_link_hash_entry *h,
+ const char *imppath, const char *impfile,
+ const char *impmember)
+{
+ unsigned int c;
+ struct xcoff_import_file **pp;
+
+ /* We overload the ldindx field to hold the l_ifile value for this
+ symbol. */
+ BFD_ASSERT (h->ldsym == NULL);
+ BFD_ASSERT ((h->flags & XCOFF_BUILT_LDSYM) == 0);
+ if (imppath == NULL)
+ h->ldindx = -1;
+ else
+ {
+ /* We start c at 1 because the first entry in the import list is
+ reserved for the library search path. */
+ for (pp = &xcoff_hash_table (info)->imports, c = 1;
+ *pp != NULL;
+ pp = &(*pp)->next, ++c)
+ {
+ if (strcmp ((*pp)->path, imppath) == 0
+ && strcmp ((*pp)->file, impfile) == 0
+ && strcmp ((*pp)->member, impmember) == 0)
+ break;
+ }
+
+ if (*pp == NULL)
+ {
+ struct xcoff_import_file *n;
+ bfd_size_type amt = sizeof (* n);
+
+ n = bfd_alloc (info->output_bfd, amt);
+ if (n == NULL)
+ return FALSE;
+ n->next = NULL;
+ n->path = imppath;
+ n->file = impfile;
+ n->member = impmember;
+ *pp = n;
+ }
+ h->ldindx = c;
+ }
+ return TRUE;
+}
+\f
/* H is the bfd symbol associated with exported .loader symbol LDSYM.
Return true if LDSYM defines H. */
const char *strings;
bfd_byte *elsym, *elsymend;
struct xcoff_import_file *n;
- const char *bname;
- const char *mname;
- const char *s;
unsigned int c;
struct xcoff_import_file **pp;
return FALSE;
n->next = NULL;
- /* For some reason, the path entry in the import file list for a
- shared object appears to always be empty. The file name is the
- base name. */
- n->path = "";
if (abfd->my_archive == NULL)
{
- bname = bfd_get_filename (abfd);
- mname = "";
+ if (!bfd_xcoff_split_import_path (abfd, abfd->filename,
+ &n->path, &n->file))
+ return FALSE;
+ n->member = "";
}
else
{
- bname = bfd_get_filename (abfd->my_archive);
- mname = bfd_get_filename (abfd);
+ struct xcoff_archive_info *archive_info;
+
+ archive_info = xcoff_get_archive_info (info, abfd->my_archive);
+ if (!archive_info->impfile)
+ {
+ if (!bfd_xcoff_split_import_path (archive_info->archive,
+ archive_info->archive->filename,
+ &archive_info->imppath,
+ &archive_info->impfile))
+ return FALSE;
+ }
+ n->path = archive_info->imppath;
+ n->file = archive_info->impfile;
+ n->member = bfd_get_filename (abfd);
}
- s = strrchr (bname, '/');
- if (s != NULL)
- bname = s + 1;
- n->file = bname;
- n->member = mname;
/* We start c at 1 because the first import file number is reserved
for LIBPATH. */
}
}
\f
+bfd_boolean
+_bfd_xcoff_define_common_symbol (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ struct bfd_link_hash_entry *harg)
+{
+ struct xcoff_link_hash_entry *h;
+
+ if (!bfd_generic_define_common_symbol (output_bfd, info, harg))
+ return FALSE;
+
+ h = (struct xcoff_link_hash_entry *) harg;
+ h->flags |= XCOFF_DEF_REGULAR;
+ return TRUE;
+}
+\f
/* If symbol H has not been interpreted as a function descriptor,
see whether it should be. Set up its descriptor information if so. */
}
return TRUE;
}
-
-/* H is an imported symbol. Set the import module's path, file and member
- to IMPATH, IMPFILE and IMPMEMBER respectively. All three are null if
- no specific import module is specified. */
-
-static bfd_boolean
-xcoff_set_import_path (struct bfd_link_info *info,
- struct xcoff_link_hash_entry *h,
- const char *imppath, const char *impfile,
- const char *impmember)
-{
- unsigned int c;
- struct xcoff_import_file **pp;
-
- /* We overload the ldindx field to hold the l_ifile value for this
- symbol. */
- BFD_ASSERT (h->ldsym == NULL);
- BFD_ASSERT ((h->flags & XCOFF_BUILT_LDSYM) == 0);
- if (imppath == NULL)
- h->ldindx = -1;
- else
- {
- /* We start c at 1 because the first entry in the import list is
- reserved for the library search path. */
- for (pp = &xcoff_hash_table (info)->imports, c = 1;
- *pp != NULL;
- pp = &(*pp)->next, ++c)
- {
- if (strcmp ((*pp)->path, imppath) == 0
- && strcmp ((*pp)->file, impfile) == 0
- && strcmp ((*pp)->member, impmember) == 0)
- break;
- }
-
- if (*pp == NULL)
- {
- struct xcoff_import_file *n;
- bfd_size_type amt = sizeof (* n);
-
- n = bfd_alloc (info->output_bfd, amt);
- if (n == NULL)
- return FALSE;
- n->next = NULL;
- n->path = imppath;
- n->file = impfile;
- n->member = impmember;
- *pp = n;
- }
- h->ldindx = c;
- }
- return TRUE;
-}
\f
/* Return true if the given bfd contains at least one shared object. */
static bfd_boolean
-xcoff_archive_contains_shared_object_p (bfd *archive)
+xcoff_archive_contains_shared_object_p (struct bfd_link_info *info,
+ bfd *archive)
{
+ struct xcoff_archive_info *archive_info;
bfd *member;
- member = bfd_openr_next_archived_file (archive, NULL);
- while (member != NULL && (member->flags & DYNAMIC) == 0)
- member = bfd_openr_next_archived_file (archive, member);
- return member != NULL;
+ archive_info = xcoff_get_archive_info (info, archive);
+ if (!archive_info->know_contains_shared_object_p)
+ {
+ member = bfd_openr_next_archived_file (archive, NULL);
+ while (member != NULL && (member->flags & DYNAMIC) == 0)
+ member = bfd_openr_next_archived_file (archive, member);
+
+ archive_info->contains_shared_object_p = (member != NULL);
+ archive_info->know_contains_shared_object_p = 1;
+ }
+ return archive_info->contains_shared_object_p;
}
/* Symbol H qualifies for export by -bexpfull. Return true if it also
specified by AUTO_EXPORT_FLAGS. */
static bfd_boolean
-xcoff_auto_export_p (struct xcoff_link_hash_entry *h,
+xcoff_auto_export_p (struct bfd_link_info *info,
+ struct xcoff_link_hash_entry *h,
unsigned int auto_export_flags)
{
/* Don't automatically export things that were explicitly exported. */
owner = h->root.u.def.section->owner;
if (owner != NULL
&& owner->my_archive != NULL
- && xcoff_archive_contains_shared_object_p (owner->my_archive))
+ && xcoff_archive_contains_shared_object_p (info, owner->my_archive))
return FALSE;
}
struct xcoff_loader_info *ldinfo;
ldinfo = (struct xcoff_loader_info *) data;
- if (xcoff_auto_export_p (h, ldinfo->auto_export_flags))
+ if (xcoff_auto_export_p (ldinfo->info, h, ldinfo->auto_export_flags))
{
if (!xcoff_mark_symbol (ldinfo->info, h))
ldinfo->failed = TRUE;
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
if (xcoff_hash_table (ldinfo->info)->loader_section)
{
- if (xcoff_auto_export_p (h, ldinfo->auto_export_flags))
+ if (xcoff_auto_export_p (ldinfo->info, h, ldinfo->auto_export_flags))
h->flags |= XCOFF_EXPORT;
if (!xcoff_build_ldsym (ldinfo, h))
esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
+ sym_hash = obj_xcoff_sym_hashes (input_bfd);
isymp = finfo->internal_syms;
indexp = finfo->sym_indices;
csectpp = xcoff_data (input_bfd)->csects;
}
}
+ /* Make __rtinit C_HIDEXT rather than C_EXT. This avoids
+ multiple definition problems when linking a shared object
+ statically. (The native linker doesn't enter __rtinit into
+ the normal table at all, but having a local symbol can make
+ the objdump output easier to read.) */
+ if (isym.n_sclass == C_EXT
+ && *sym_hash
+ && ((*sym_hash)->flags & XCOFF_RTINIT) != 0)
+ isym.n_sclass = C_HIDEXT;
+
/* The value of a C_FILE symbol is the symbol index of the
next C_FILE symbol. The value of the last C_FILE symbol
is -1. We try to get this right, below, just before we
}
}
+ sym_hash += add;
indexp += add;
isymp += add;
csectpp += add;