diff --git a/ld/ChangeLog b/ld/ChangeLog index a2a83739b7..3e28c9bbc8 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,30 @@ +Thu Jun 2 17:24:08 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com) + + Add support for SunOS shared libraries. + * aout.sc: Don't define __DYNAMIC here. Add new sections used by + shared library support code. + * emultempl/sunos.em: New file. + * emulparams/sun4.sh (TEMPLATE_NAME): Define as sunos. + * Makefile.in (esun4.c): Depend upon sunos.em, not generic.em. + + * ldlang.c: Minor formatting cleanups. + (lang_for_each_input_file): New function. + * ldlang.h (lang_for_each_input_file): Declare. + + * ldfile.h (search_dirs_type): Move from ldfile.c, and add cmdline + field. + (search_head): Declare. + (ldfile_add_library_path): Add new cmdline argument in prototype. + * ldfile.c (search_head): Make non-static. + (search_dirs_type): Move to ldfile.h. + (ldfile_add_library_path): Accept cmdline argument, and save it. + * lexsup.c (parse_args): Pass true for new cmdline argument of + ldfile_add_library_path. + (set_default_dirlist): Likewise. + * ldmain.c (check_for_scripts_dir): Pass false for new cmdline + argument of ldfile_add_library_path. + * ldgram.y (ifile_p1): Likewise. + Wed Jun 1 14:24:08 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com) * ldlang.h (lang_input_statement_type): Remove fields subfiles, diff --git a/ld/Makefile.in b/ld/Makefile.in index e1c8701313..a80cb4b44f 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -252,7 +252,7 @@ GENSCRIPTS = $(SHELL) $(srcdir)/genscripts.sh ${srcdir} ${libdir} ${host_alias} GEN_DEPENDS = $(srcdir)/genscripts.sh $(srcdir)/emultempl/stringify.sed esun4.c: $(srcdir)/emulparams/sun4.sh \ - $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} + $(srcdir)/emultempl/sunos.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} ${GENSCRIPTS} sun4 esun3.c: $(srcdir)/emulparams/sun3.sh \ $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} diff --git a/ld/emultempl/.Sanitize b/ld/emultempl/.Sanitize index 8cf22c1d7f..b4bab8beb9 100644 --- a/ld/emultempl/.Sanitize +++ b/ld/emultempl/.Sanitize @@ -33,6 +33,7 @@ gld960c.em hppaelf.em lnk960.em m88kbcs.em +sunos.em vanilla.em Things-to-lose: diff --git a/ld/emultempl/sunos.em b/ld/emultempl/sunos.em new file mode 100644 index 0000000000..55bcd6c925 --- /dev/null +++ b/ld/emultempl/sunos.em @@ -0,0 +1,564 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +cat >e${EMULATION_NAME}.c < + SunOS shared library support by Ian Lance Taylor + +This file is part of GLD, the Gnu Linker. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include +#include + +/* FIXME: On some hosts we will need to include a different file. + This is correct for SunOS, which is the only place this file will + typically be compiled. However, if somebody configures the linker + for all targets, they will run into trouble here. */ +#include + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" + +#include "ld.h" +#include "config.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" + +static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); +static void gld${EMULATION_NAME}_create_output_section_statements + PARAMS ((void)); +static void gld${EMULATION_NAME}_find_so + PARAMS ((lang_input_statement_type *)); +static void gld${EMULATION_NAME}_before_allocation PARAMS ((void)); +static void gld${EMULATION_NAME}_find_statement_assignment + PARAMS ((lang_statement_union_type *)); +static void gld${EMULATION_NAME}_find_exp_assignment PARAMS ((etree_type *)); +static void gld${EMULATION_NAME}_count_need + PARAMS ((lang_input_statement_type *)); +static void gld${EMULATION_NAME}_set_need + PARAMS ((lang_input_statement_type *)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +static void +gld${EMULATION_NAME}_before_parse() +{ + ldfile_output_architecture = bfd_arch_${ARCH}; + config.dynamic_link = true; +} + +/* Despite the name, we use this routine to search for dynamic + libraries. On SunOS this requires a directory search. We need to + find the .so file with the highest version number. The user may + restrict the major version by saying, e.g., -lc.1. Also, if we + find a .so file, we need to look for a the same file after + replacing .so with .sa; if it exists, it will be an archive which + provide some initializations for data symbols, and we need to + search it after including the .so file. */ + +static void +gld${EMULATION_NAME}_create_output_section_statements () +{ + lang_for_each_input_file (gld${EMULATION_NAME}_find_so); +} + +/* Search the directory for a .so file for each library search. */ + +static void +gld${EMULATION_NAME}_find_so (inp) + lang_input_statement_type *inp; +{ + search_dirs_type *search; + const char *filename; + const char *dot; + int force_maj; + unsigned int len; + char *alc; + int max_maj, max_min; + char *found; + struct stat st; + + if (! inp->search_dirs_flag + || ! inp->is_archive) + return; + + ASSERT (strncmp (inp->local_sym_name, "-l", 2) == 0); + + filename = inp->filename; + force_maj = -1; + dot = strchr (filename, '.'); + if (dot == NULL) + len = strlen (filename); + else + { + len = dot - filename; + alc = (char *) alloca (len + 1); + strncpy (alc, filename, len); + alc[len] = '\0'; + filename = alc; + } + + found = NULL; + max_maj = max_min = 0; + for (search = search_head; search != NULL; search = search->next) + { + DIR *dir; + struct dirent *entry; + + dir = opendir (search->name); + if (dir == NULL) + continue; + + while ((entry = readdir (dir)) != NULL) + { + int found_maj, found_min; + + if (strncmp (entry->d_name, "lib", 3) != 0 + || strncmp (entry->d_name + 3, inp->filename, len) != 0 + || strncmp (entry->d_name + 3 + len, ".so", 3) != 0) + continue; + + /* We've found a .so file. Work out the major and minor + version numbers. */ + found_maj = 0; + found_min = 0; + sscanf (entry->d_name + 3 + len, ".so.%d.%d", + &found_maj, &found_min); + + if (force_maj != -1 && force_maj != found_maj) + continue; + + /* We've found a match for the name we are searching for. + See if this is the version we should use. If the major + and minor versions match, we use the last entry in + alphabetical order; I don't know if this is how SunOS + distinguishes libc.so.1.8 from libc.so.1.8.1, but it + ought to suffice. */ + if (found == NULL + || (found_maj > max_maj) + || (found_maj == max_maj + && (found_min > max_min + || (found_min == max_min + && strcmp (entry->d_name, found) > 0)))) + { + if (found != NULL) + free (found); + found = (char *) xmalloc (strlen (entry->d_name) + 1); + strcpy (found, entry->d_name); + max_maj = found_maj; + max_min = found_min; + } + } + + closedir (dir); + + if (found != NULL) + break; + } + + if (found == NULL) + { + /* We did not find a matching .so file. This isn't an error, + since there might still be a matching .a file, which will be + found by the usual search. */ + return; + } + + /* Replace the filename with the one we have found. */ + alc = (char *) xmalloc (strlen (search->name) + strlen (found) + 2); + sprintf (alc, "%s/%s", search->name, found); + inp->filename = alc; + + /* Turn off the search_dirs_flag to prevent ldfile_open_file from + searching for this file again. */ + inp->search_dirs_flag = false; + + free (found); + + /* Now look for the same file name, but with .sa instead of .so. If + found, add it to the list of input files. */ + alc = (char *) alloca (strlen (inp->filename) + 1); + strcpy (alc, inp->filename); + strstr (alc, ".so.")[2] = 'a'; + if (stat (alc, &st) == 0) + { + lang_input_statement_type *sa; + char *a; + + /* Add the .sa file to the statement list just after the .so + file. This is really a hack. */ + sa = ((lang_input_statement_type *) + xmalloc (sizeof (lang_input_statement_type))); + sa->header.next = inp->header.next; + sa->header.type = lang_input_statement_enum; + a = (char *) xmalloc (strlen (alc) + 1); + strcpy (a, alc); + sa->filename = a; + sa->local_sym_name = a; + sa->the_bfd = NULL; + sa->asymbols = NULL; + sa->symbol_count = 0; + sa->next = inp->next; + sa->next_real_file = inp->next_real_file; + sa->is_archive = false; + sa->search_dirs_flag = false; + sa->just_syms_flag = false; + sa->loaded = false; + + inp->header.next = (lang_statement_union_type *) sa; + inp->next = (lang_statement_union_type *) sa; + inp->next_real_file = (lang_statement_union_type *) sa; + } +} + +/* We need to use static variables to pass information around the call + to lang_for_each_input_file. Ick. */ + +static bfd_size_type need_size; +static bfd_size_type need_entries; +static bfd_byte *need_contents; +static bfd_byte *need_pinfo; +static bfd_byte *need_pnames; + +/* The size of one entry in the .need section, not including the file + name. */ + +#define NEED_ENTRY_SIZE (16) + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +static void +gld${EMULATION_NAME}_before_allocation () +{ + struct bfd_link_hash_entry *h = NULL; + asection *sneed; + asection *srules; + asection *sdyn; + + /* We need to create a __DYNAMIC symbol. We don't do this in the + linker script because we want to set the value to the start of + the dynamic section if there is one, or to zero if there isn't + one. We need to create the symbol before calling + size_dynamic_sections, although we can't set the value until + afterward. */ + if (! link_info.relocateable) + { + h = bfd_link_hash_lookup (link_info.hash, "__DYNAMIC", true, false, + false); + if (h == NULL) + einfo ("%P%F: bfd_link_hash_lookup: %E\n"); + if (! bfd_sunos_record_link_assignment (output_bfd, &link_info, + "__DYNAMIC")) + einfo ("%P%F: failed to record assignment to __DYNAMIC: %E\n"); + } + + /* If we are going to make any variable assignments, we need to let + the backend linker know about them in case the variables are + referred to by dynamic objects. */ + lang_for_each_statement (gld${EMULATION_NAME}_find_statement_assignment); + + /* Let the backend linker work out the sizes of any sections + required by dynamic linking. */ + if (! bfd_sunos_size_dynamic_sections (output_bfd, &link_info, &sdyn, + &sneed, &srules)) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); + + if (sneed != NULL) + { + /* Set up the .need section. See the description of the ld_need + field in include/sun4/aout.h. */ + + need_entries = 0; + need_size = 0; + + lang_for_each_input_file (gld${EMULATION_NAME}_count_need); + + /* We should only have a .need section if we have at least one + dynamic object. */ + ASSERT (need_entries != 0); + + sneed->_raw_size = need_size; + sneed->contents = (bfd_byte *) xmalloc (need_size); + + need_contents = sneed->contents; + need_pinfo = sneed->contents; + need_pnames = sneed->contents + need_entries * 16; + + lang_for_each_input_file (gld${EMULATION_NAME}_set_need); + + ASSERT (need_pnames - sneed->contents == need_size); + } + + if (srules != NULL) + { + unsigned int size; + search_dirs_type *search; + + /* Set up the .rules section. This is just a PATH like string + of the -L arguments given on the command line. */ + size = 0; + for (search = search_head; search != NULL; search = search->next) + if (search->cmdline) + size += strlen (search->name) + 1; + srules->_raw_size = size; + if (size > 0) + { + char *p; + + srules->contents = (bfd_byte *) xmalloc (size); + p = (char *) srules->contents; + *p = '\0'; + for (search = search_head; search != NULL; search = search->next) + { + if (search->cmdline) + { + if (p != (char *) srules->contents) + *p++ = ':'; + strcpy (p, search->name); + p += strlen (p); + } + } + } + } + + /* We must assign a value to __DYNAMIC. It should be zero if we are + not doing a dynamic link, or the start of the .dynamic section if + we are doing one. */ + if (! link_info.relocateable) + { + h->type = bfd_link_hash_defined; + h->u.def.value = 0; + if (sdyn != NULL) + h->u.def.section = sdyn; + else + h->u.def.section = &bfd_abs_section; + } +} + +/* This is called by the before_allocation routine via + lang_for_each_statement. It locates any assignment statements, and + tells the backend linker about them, in case they are assignments + to symbols which are referred to by dynamic objects. */ + +static void +gld${EMULATION_NAME}_find_statement_assignment (s) + lang_statement_union_type *s; +{ + if (s->header.type == lang_assignment_statement_enum) + gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp); +} + +/* Look through an expression for an assignment statement. */ + +static void +gld${EMULATION_NAME}_find_exp_assignment (exp) + etree_type *exp; +{ + switch (exp->type.node_class) + { + case etree_assign: + if (strcmp (exp->assign.dst, ".") != 0) + { + if (! bfd_sunos_record_link_assignment (output_bfd, &link_info, + exp->assign.dst)) + einfo ("%P%F: failed to record assignment to %s: %E\n", + exp->assign.dst); + } + gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src); + break; + + case etree_binary: + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs); + break; + + case etree_trinary: + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs); + break; + + case etree_unary: + gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child); + break; + + default: + break; + } +} + +/* Work out the size of the .need section, and the number of entries. + The backend will set the ld_need field of the dynamic linking + information to point to the .need section. See include/aout/sun4.h + for more information. */ + +static void +gld${EMULATION_NAME}_count_need (inp) + lang_input_statement_type *inp; +{ + if (inp->the_bfd != NULL + && (inp->the_bfd->flags & DYNAMIC) != 0) + { + ++need_entries; + need_size += NEED_ENTRY_SIZE; + if (! inp->is_archive) + need_size += strlen (inp->filename) + 1; + else + { + ASSERT (inp->local_sym_name[0] == '-' + && inp->local_sym_name[1] == 'l'); + need_size += strlen (inp->local_sym_name + 2) + 1; + } + } +} + +/* Fill in the contents of the .need section. */ + +static void +gld${EMULATION_NAME}_set_need (inp) + lang_input_statement_type *inp; +{ + if (inp->the_bfd != NULL + && (inp->the_bfd->flags & DYNAMIC) != 0) + { + bfd_size_type c; + + /* To really fill in the .need section contents, we need to know + the final file position of the section, but we don't. + Instead, we use offsets, and rely on the BFD backend to + finish the section up correctly. FIXME: Talk about lack of + referential locality. */ + bfd_put_32 (output_bfd, need_pnames - need_contents, need_pinfo); + if (! inp->is_archive) + { + bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 4); + bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 8); + bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 10); + strcpy (need_pnames, inp->filename); + } + else + { + char *verstr; + int maj, min; + + bfd_put_32 (output_bfd, (bfd_vma) 0x80000000, need_pinfo + 4); + maj = 0; + min = 0; + verstr = strstr (inp->filename, ".so."); + if (verstr != NULL) + sscanf (verstr, ".so.%d.%d", &maj, &min); + bfd_put_16 (output_bfd, (bfd_vma) maj, need_pinfo + 8); + bfd_put_16 (output_bfd, (bfd_vma) min, need_pinfo + 10); + strcpy (need_pnames, inp->local_sym_name + 2); + } + + c = (need_pinfo - need_contents) / NEED_ENTRY_SIZE; + if (c + 1 >= need_entries) + bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 12); + else + bfd_put_32 (output_bfd, (bfd_vma) (c + 1) * NEED_ENTRY_SIZE, + need_pinfo + 12); + + need_pinfo += NEED_ENTRY_SIZE; + need_pnames += strlen (need_pnames) + 1; + } +} + +static char * +gld${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <