diff options
author | Alan Modra <amodra@gmail.com> | 2000-07-28 01:07:09 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2000-07-28 01:07:09 +0000 |
commit | 04925e1e0b21992039d1c3923fa47896fac013a6 (patch) | |
tree | 918d8c0e3e1df7e9b3b2ffd72d1d6a164ad6252f /ld/emultempl/elf32.em | |
parent | 88cdf2977590c1a5405c709b41f7d916f1c129f0 (diff) | |
download | gdb-04925e1e0b21992039d1c3923fa47896fac013a6.zip gdb-04925e1e0b21992039d1c3923fa47896fac013a6.tar.gz gdb-04925e1e0b21992039d1c3923fa47896fac013a6.tar.bz2 |
Move functions around in preparation for next patch
Diffstat (limited to 'ld/emultempl/elf32.em')
-rw-r--r-- | ld/emultempl/elf32.em | 967 |
1 files changed, 491 insertions, 476 deletions
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em index 1203e30..f1e23fd 100644 --- a/ld/emultempl/elf32.em +++ b/ld/emultempl/elf32.em @@ -48,25 +48,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "elf/common.h" static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); -static boolean gld${EMULATION_NAME}_open_dynamic_archive - PARAMS ((const char *, search_dirs_type *, lang_input_statement_type *)); -static void gld${EMULATION_NAME}_after_open PARAMS ((void)); -static void gld${EMULATION_NAME}_check_needed +static void gld${EMULATION_NAME}_vercheck PARAMS ((lang_input_statement_type *)); static void gld${EMULATION_NAME}_stat_needed PARAMS ((lang_input_statement_type *)); +static boolean gld${EMULATION_NAME}_try_needed PARAMS ((const char *, int)); static boolean gld${EMULATION_NAME}_search_needed PARAMS ((const char *, const char *, int)); -static boolean gld${EMULATION_NAME}_try_needed PARAMS ((const char *, int)); -static void gld${EMULATION_NAME}_vercheck +static void gld${EMULATION_NAME}_check_needed PARAMS ((lang_input_statement_type *)); -static void gld${EMULATION_NAME}_before_allocation PARAMS ((void)); +static void gld${EMULATION_NAME}_after_open PARAMS ((void)); +static void gld${EMULATION_NAME}_find_exp_assignment PARAMS ((etree_type *)); 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}_before_allocation PARAMS ((void)); +static boolean gld${EMULATION_NAME}_open_dynamic_archive + PARAMS ((const char *, search_dirs_type *, lang_input_statement_type *)); +static lang_output_section_statement_type *output_rel_find PARAMS ((void)); static boolean gld${EMULATION_NAME}_place_orphan PARAMS ((lang_input_statement_type *, asection *)); -static lang_output_section_statement_type *output_rel_find PARAMS ((void)); static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); static void @@ -77,86 +77,322 @@ gld${EMULATION_NAME}_before_parse() config.has_shared = `if test -n "$GENERATE_SHLIB_SCRIPT" ; then echo true ; else echo false ; fi`; } -/* Try to open a dynamic archive. This is where we know that ELF - dynamic libraries have an extension of .so (or .sl on oddball systems - like hpux). */ + +/* These variables are required to pass information back and forth + between after_open and check_needed and stat_needed and vercheck. */ + +static struct bfd_link_needed_list *global_needed; +static struct stat global_stat; +static boolean global_found; +static struct bfd_link_needed_list *global_vercheck_needed; +static boolean global_vercheck_failed; + + +/* On Linux, it's possible to have different versions of the same + shared library linked against different versions of libc. The + dynamic linker somehow tags which libc version to use in + /etc/ld.so.cache, and, based on the libc that it sees in the + executable, chooses which version of the shared library to use. + + We try to do a similar check here by checking whether this shared + library needs any other shared libraries which may conflict with + libraries we have already included in the link. If it does, we + skip it, and try to find another shared library farther on down the + link path. + + This is called via lang_for_each_input_file. + GLOBAL_VERCHECK_NEEDED is the list of objects needed by the object + which we ar checking. This sets GLOBAL_VERCHECK_FAILED if we find + a conflicting version. */ + +static void +gld${EMULATION_NAME}_vercheck (s) + lang_input_statement_type *s; +{ + const char *soname, *f; + struct bfd_link_needed_list *l; + + if (global_vercheck_failed) + return; + if (s->the_bfd == NULL + || (bfd_get_file_flags (s->the_bfd) & DYNAMIC) == 0) + return; + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname == NULL) + soname = bfd_get_filename (s->the_bfd); + + f = strrchr (soname, '/'); + if (f != NULL) + ++f; + else + f = soname; + + for (l = global_vercheck_needed; l != NULL; l = l->next) + { + const char *suffix; + + if (strcmp (f, l->name) == 0) + { + /* Probably can't happen, but it's an easy check. */ + continue; + } + + if (strchr (l->name, '/') != NULL) + continue; + + suffix = strstr (l->name, ".so."); + if (suffix == NULL) + continue; + + suffix += sizeof ".so." - 1; + + if (strncmp (f, l->name, suffix - l->name) == 0) + { + /* Here we know that S is a dynamic object FOO.SO.VER1, and + the object we are considering needs a dynamic object + FOO.SO.VER2, and VER1 and VER2 are different. This + appears to be a version mismatch, so we tell the caller + to try a different version of this library. */ + global_vercheck_failed = true; + return; + } + } +} + + +/* See if an input file matches a DT_NEEDED entry by running stat on + the file. */ + +static void +gld${EMULATION_NAME}_stat_needed (s) + lang_input_statement_type *s; +{ + struct stat st; + const char *suffix; + const char *soname; + const char *f; + + if (global_found) + return; + if (s->the_bfd == NULL) + return; + + if (bfd_stat (s->the_bfd, &st) != 0) + { + einfo ("%P:%B: bfd_stat failed: %E\n", s->the_bfd); + return; + } + + if (st.st_dev == global_stat.st_dev + && st.st_ino == global_stat.st_ino) + { + global_found = true; + return; + } + + /* We issue a warning if it looks like we are including two + different versions of the same shared library. For example, + there may be a problem if -lc picks up libc.so.6 but some other + shared library has a DT_NEEDED entry of libc.so.5. This is a + hueristic test, and it will only work if the name looks like + NAME.so.VERSION. FIXME: Depending on file names is error-prone. + If we really want to issue warnings about mixing version numbers + of shared libraries, we need to find a better way. */ + + if (strchr (global_needed->name, '/') != NULL) + return; + suffix = strstr (global_needed->name, ".so."); + if (suffix == NULL) + return; + suffix += sizeof ".so." - 1; + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname == NULL) + soname = s->filename; + + f = strrchr (soname, '/'); + if (f != NULL) + ++f; + else + f = soname; + + if (strncmp (f, global_needed->name, suffix - global_needed->name) == 0) + einfo ("%P: warning: %s, needed by %B, may conflict with %s\n", + global_needed->name, global_needed->by, f); +} + + +/* This function is called for each possible name for a dynamic object + named by a DT_NEEDED entry. The FORCE parameter indicates whether + to skip the check for a conflicting version. */ static boolean -gld${EMULATION_NAME}_open_dynamic_archive (arch, search, entry) - const char *arch; - search_dirs_type *search; - lang_input_statement_type *entry; +gld${EMULATION_NAME}_try_needed (name, force) + const char *name; + int force; { - const char *filename; - char *string; + bfd *abfd; - if (! entry->is_archive) + abfd = bfd_openr (name, bfd_get_target (output_bfd)); + if (abfd == NULL) return false; + if (! bfd_check_format (abfd, bfd_object)) + { + (void) bfd_close (abfd); + return false; + } + if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0) + { + (void) bfd_close (abfd); + return false; + } - filename = entry->filename; + /* Check whether this object would include any conflicting library + versions. If FORCE is set, then we skip this check; we use this + the second time around, if we couldn't find any compatible + instance of the shared library. */ - /* This allocates a few bytes too many when EXTRA_SHLIB_EXTENSION - is defined, but it does not seem worth the headache to optimize - away those two bytes of space. */ - string = (char *) xmalloc (strlen (search->name) - + strlen (filename) - + strlen (arch) -#ifdef EXTRA_SHLIB_EXTENSION - + strlen (EXTRA_SHLIB_EXTENSION) -#endif - + sizeof "/lib.so"); + if (! force) + { + struct bfd_link_needed_list *needed; - sprintf (string, "%s/lib%s%s.so", search->name, filename, arch); + if (! bfd_elf_get_bfd_needed_list (abfd, &needed)) + einfo ("%F%P:%B: bfd_elf_get_bfd_needed_list failed: %E\n", abfd); -#ifdef EXTRA_SHLIB_EXTENSION - /* Try the .so extension first. If that fails build a new filename - using EXTRA_SHLIB_EXTENSION. */ - if (! ldfile_try_open_bfd (string, entry)) - sprintf (string, "%s/lib%s%s%s", search->name, - filename, arch, EXTRA_SHLIB_EXTENSION); -#endif + if (needed != NULL) + { + global_vercheck_needed = needed; + global_vercheck_failed = false; + lang_for_each_input_file (gld${EMULATION_NAME}_vercheck); + if (global_vercheck_failed) + { + (void) bfd_close (abfd); + /* Return false to force the caller to move on to try + another file on the search path. */ + return false; + } - if (! ldfile_try_open_bfd (string, entry)) + /* But wait! It gets much worse. On Linux, if a shared + library does not use libc at all, we are supposed to skip + it the first time around in case we encounter a shared + library later on with the same name which does use the + version of libc that we want. This is much too horrible + to use on any system other than Linux. */ + +EOF +case ${target} in + *-*-linux-gnu*) + cat >>e${EMULATION_NAME}.c <<EOF + { + struct bfd_link_needed_list *l; + + for (l = needed; l != NULL; l = l->next) + if (strncmp (l->name, "libc.so", 7) == 0) + break; + if (l == NULL) + { + (void) bfd_close (abfd); + return false; + } + } + +EOF + ;; +esac +cat >>e${EMULATION_NAME}.c <<EOF + } + } + + /* We've found a dynamic object matching the DT_NEEDED entry. */ + + /* We have already checked that there is no other input file of the + same name. We must now check again that we are not including the + same file twice. We need to do this because on many systems + libc.so is a symlink to, e.g., libc.so.1. The SONAME entry will + reference libc.so.1. If we have already included libc.so, we + don't want to include libc.so.1 if they are the same file, and we + can only check that using stat. */ + + if (bfd_stat (abfd, &global_stat) != 0) + einfo ("%F%P:%B: bfd_stat failed: %E\n", abfd); + global_found = false; + lang_for_each_input_file (gld${EMULATION_NAME}_stat_needed); + if (global_found) { - free (string); - return false; + /* Return true to indicate that we found the file, even though + we aren't going to do anything with it. */ + return true; } - entry->filename = string; + /* Tell the ELF backend that don't want the output file to have a + DT_NEEDED entry for this file. */ + bfd_elf_set_dt_needed_name (abfd, ""); - /* We have found a dynamic object to include in the link. The ELF - backend linker will create a DT_NEEDED entry in the .dynamic - section naming this file. If this file includes a DT_SONAME - entry, it will be used. Otherwise, the ELF linker will just use - the name of the file. For an archive found by searching, like - this one, the DT_NEEDED entry should consist of just the name of - the file, without the path information used to find it. Note - that we only need to do this if we have a dynamic object; an - archive will never be referenced by a DT_NEEDED entry. + /* First strip off everything before the last '/'. */ + name = strrchr (abfd->filename, '/'); + if (name) + name++; + else + name = abfd->filename; - FIXME: This approach--using bfd_elf_set_dt_needed_name--is not - very pretty. I haven't been able to think of anything that is - pretty, though. */ - if (bfd_check_format (entry->the_bfd, bfd_object) - && (entry->the_bfd->flags & DYNAMIC) != 0) + /* Tell the ELF backend that the output file needs a DT_NEEDED + entry for this file if it is used to resolve the reference in + a regular object. */ + bfd_elf_set_dt_needed_soname (abfd, name); + + /* Add this file into the symbol table. */ + if (! bfd_link_add_symbols (abfd, &link_info)) + einfo ("%F%B: could not read symbols: %E\n", abfd); + + return true; +} + + +/* Search for a needed file in a path. */ + +static boolean +gld${EMULATION_NAME}_search_needed (path, name, force) + const char *path; + const char *name; + int force; +{ + const char *s; + size_t len; + + if (path == NULL || *path == '\0') + return false; + len = strlen (name); + while (1) { - char *needed_name; + char *filename, *sset; - ASSERT (entry->is_archive && entry->search_dirs_flag); + s = strchr (path, ':'); + if (s == NULL) + s = path + strlen (path); - /* Rather than duplicating the logic above. Just use the - filename we recorded earlier. + filename = (char *) xmalloc (s - path + len + 2); + if (s == path) + sset = filename; + else + { + memcpy (filename, path, s - path); + filename[s - path] = '/'; + sset = filename + (s - path) + 1; + } + strcpy (sset, name); - First strip off everything before the last '/'. */ - filename = strrchr (entry->filename, '/'); - filename++; + if (gld${EMULATION_NAME}_try_needed (filename, force)) + return true; - needed_name = (char *) xmalloc (strlen (filename) + 1); - strcpy (needed_name, filename); - bfd_elf_set_dt_needed_name (entry->the_bfd, needed_name); + free (filename); + + if (*s == '\0') + break; + path = s + 1; } - return true; + return false; } EOF @@ -252,14 +488,51 @@ EOF fi cat >>e${EMULATION_NAME}.c <<EOF -/* These variables are required to pass information back and forth - between after_open and check_needed and stat_needed and vercheck. */ +/* See if an input file matches a DT_NEEDED entry by name. */ + +static void +gld${EMULATION_NAME}_check_needed (s) + lang_input_statement_type *s; +{ + if (global_found) + return; + + if (s->filename != NULL + && strcmp (s->filename, global_needed->name) == 0) + { + global_found = true; + return; + } + + if (s->the_bfd != NULL) + { + const char *soname; + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname != NULL + && strcmp (soname, global_needed->name) == 0) + { + global_found = true; + return; + } + } + + if (s->search_dirs_flag + && s->filename != NULL + && strchr (global_needed->name, '/') == NULL) + { + const char *f; + + f = strrchr (s->filename, '/'); + if (f != NULL + && strcmp (f + 1, global_needed->name) == 0) + { + global_found = true; + return; + } + } +} -static struct bfd_link_needed_list *global_needed; -static struct stat global_stat; -static boolean global_found; -static struct bfd_link_needed_list *global_vercheck_needed; -static boolean global_vercheck_failed; /* This is called after all the input files have been opened. */ @@ -381,355 +654,79 @@ cat >>e${EMULATION_NAME}.c <<EOF } } -/* Search for a needed file in a path. */ -static boolean -gld${EMULATION_NAME}_search_needed (path, name, force) - const char *path; - const char *name; - int force; -{ - const char *s; - size_t len; - - if (path == NULL || *path == '\0') - return false; - len = strlen (name); - while (1) - { - char *filename, *sset; - - s = strchr (path, ':'); - if (s == NULL) - s = path + strlen (path); - - filename = (char *) xmalloc (s - path + len + 2); - if (s == path) - sset = filename; - else - { - memcpy (filename, path, s - path); - filename[s - path] = '/'; - sset = filename + (s - path) + 1; - } - strcpy (sset, name); - - if (gld${EMULATION_NAME}_try_needed (filename, force)) - return true; - - free (filename); - - if (*s == '\0') - break; - path = s + 1; - } - - return false; -} - -/* This function is called for each possible name for a dynamic object - named by a DT_NEEDED entry. The FORCE parameter indicates whether - to skip the check for a conflicting version. */ - -static boolean -gld${EMULATION_NAME}_try_needed (name, force) - const char *name; - int force; -{ - bfd *abfd; - - abfd = bfd_openr (name, bfd_get_target (output_bfd)); - if (abfd == NULL) - return false; - if (! bfd_check_format (abfd, bfd_object)) - { - (void) bfd_close (abfd); - return false; - } - if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0) - { - (void) bfd_close (abfd); - return false; - } - - /* Check whether this object would include any conflicting library - versions. If FORCE is set, then we skip this check; we use this - the second time around, if we couldn't find any compatible - instance of the shared library. */ - - if (! force) - { - struct bfd_link_needed_list *needed; - - if (! bfd_elf_get_bfd_needed_list (abfd, &needed)) - einfo ("%F%P:%B: bfd_elf_get_bfd_needed_list failed: %E\n", abfd); - - if (needed != NULL) - { - global_vercheck_needed = needed; - global_vercheck_failed = false; - lang_for_each_input_file (gld${EMULATION_NAME}_vercheck); - if (global_vercheck_failed) - { - (void) bfd_close (abfd); - /* Return false to force the caller to move on to try - another file on the search path. */ - return false; - } - - /* But wait! It gets much worse. On Linux, if a shared - library does not use libc at all, we are supposed to skip - it the first time around in case we encounter a shared - library later on with the same name which does use the - version of libc that we want. This is much too horrible - to use on any system other than Linux. */ - -EOF -case ${target} in - *-*-linux-gnu*) - cat >>e${EMULATION_NAME}.c <<EOF - { - struct bfd_link_needed_list *l; - - for (l = needed; l != NULL; l = l->next) - if (strncmp (l->name, "libc.so", 7) == 0) - break; - if (l == NULL) - { - (void) bfd_close (abfd); - return false; - } - } - -EOF - ;; -esac -cat >>e${EMULATION_NAME}.c <<EOF - } - } - - /* We've found a dynamic object matching the DT_NEEDED entry. */ - - /* We have already checked that there is no other input file of the - same name. We must now check again that we are not including the - same file twice. We need to do this because on many systems - libc.so is a symlink to, e.g., libc.so.1. The SONAME entry will - reference libc.so.1. If we have already included libc.so, we - don't want to include libc.so.1 if they are the same file, and we - can only check that using stat. */ - - if (bfd_stat (abfd, &global_stat) != 0) - einfo ("%F%P:%B: bfd_stat failed: %E\n", abfd); - global_found = false; - lang_for_each_input_file (gld${EMULATION_NAME}_stat_needed); - if (global_found) - { - /* Return true to indicate that we found the file, even though - we aren't going to do anything with it. */ - return true; - } - - /* Tell the ELF backend that don't want the output file to have a - DT_NEEDED entry for this file. */ - bfd_elf_set_dt_needed_name (abfd, ""); - - /* First strip off everything before the last '/'. */ - name = strrchr (abfd->filename, '/'); - if (name) - name++; - else - name = abfd->filename; - - /* Tell the ELF backend that the output file needs a DT_NEEDED - entry for this file if it is used to resolve the reference in - a regular object. */ - bfd_elf_set_dt_needed_soname (abfd, name); - - /* Add this file into the symbol table. */ - if (! bfd_link_add_symbols (abfd, &link_info)) - einfo ("%F%B: could not read symbols: %E\n", abfd); - - return true; -} - -/* See if an input file matches a DT_NEEDED entry by name. */ +/* Look through an expression for an assignment statement. */ static void -gld${EMULATION_NAME}_check_needed (s) - lang_input_statement_type *s; +gld${EMULATION_NAME}_find_exp_assignment (exp) + etree_type *exp; { - if (global_found) - return; - - if (s->filename != NULL - && strcmp (s->filename, global_needed->name) == 0) - { - global_found = true; - return; - } + struct bfd_link_hash_entry *h; - if (s->the_bfd != NULL) + switch (exp->type.node_class) { - const char *soname; + case etree_provide: + h = bfd_link_hash_lookup (link_info.hash, exp->assign.dst, + false, false, false); + if (h == NULL) + break; - soname = bfd_elf_get_dt_soname (s->the_bfd); - if (soname != NULL - && strcmp (soname, global_needed->name) == 0) - { - global_found = true; - return; - } - } - - if (s->search_dirs_flag - && s->filename != NULL - && strchr (global_needed->name, '/') == NULL) - { - const char *f; + /* We call record_link_assignment even if the symbol is defined. + This is because if it is defined by a dynamic object, we + actually want to use the value defined by the linker script, + not the value from the dynamic object (because we are setting + symbols like etext). If the symbol is defined by a regular + object, then, as it happens, calling record_link_assignment + will do no harm. */ - f = strrchr (s->filename, '/'); - if (f != NULL - && strcmp (f + 1, global_needed->name) == 0) + /* Fall through. */ + case etree_assign: + if (strcmp (exp->assign.dst, ".") != 0) { - global_found = true; - return; + if (! (bfd_elf${ELFSIZE}_record_link_assignment + (output_bfd, &link_info, exp->assign.dst, + exp->type.node_class == etree_provide ? true : false))) + einfo ("%P%F: failed to record assignment to %s: %E\n", + exp->assign.dst); } - } -} - -/* See if an input file matches a DT_NEEDED entry by running stat on - the file. */ + gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src); + break; -static void -gld${EMULATION_NAME}_stat_needed (s) - lang_input_statement_type *s; -{ - struct stat st; - const char *suffix; - const char *soname; - const char *f; + case etree_binary: + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs); + break; - if (global_found) - return; - if (s->the_bfd == NULL) - return; + case etree_trinary: + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.cond); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs); + break; - if (bfd_stat (s->the_bfd, &st) != 0) - { - einfo ("%P:%B: bfd_stat failed: %E\n", s->the_bfd); - return; - } + case etree_unary: + gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child); + break; - if (st.st_dev == global_stat.st_dev - && st.st_ino == global_stat.st_ino) - { - global_found = true; - return; + default: + break; } - - /* We issue a warning if it looks like we are including two - different versions of the same shared library. For example, - there may be a problem if -lc picks up libc.so.6 but some other - shared library has a DT_NEEDED entry of libc.so.5. This is a - hueristic test, and it will only work if the name looks like - NAME.so.VERSION. FIXME: Depending on file names is error-prone. - If we really want to issue warnings about mixing version numbers - of shared libraries, we need to find a better way. */ - - if (strchr (global_needed->name, '/') != NULL) - return; - suffix = strstr (global_needed->name, ".so."); - if (suffix == NULL) - return; - suffix += sizeof ".so." - 1; - - soname = bfd_elf_get_dt_soname (s->the_bfd); - if (soname == NULL) - soname = s->filename; - - f = strrchr (soname, '/'); - if (f != NULL) - ++f; - else - f = soname; - - if (strncmp (f, global_needed->name, suffix - global_needed->name) == 0) - einfo ("%P: warning: %s, needed by %B, may conflict with %s\n", - global_needed->name, global_needed->by, f); } -/* On Linux, it's possible to have different versions of the same - shared library linked against different versions of libc. The - dynamic linker somehow tags which libc version to use in - /etc/ld.so.cache, and, based on the libc that it sees in the - executable, chooses which version of the shared library to use. - - We try to do a similar check here by checking whether this shared - library needs any other shared libraries which may conflict with - libraries we have already included in the link. If it does, we - skip it, and try to find another shared library farther on down the - link path. - This is called via lang_for_each_input_file. - GLOBAL_VERCHECK_NEEDED is the list of objects needed by the object - which we ar checking. This sets GLOBAL_VERCHECK_FAILED if we find - a conflicting version. */ +/* This is called by the before_allocation routine via + lang_for_each_statement. It locates any assignment statements, and + tells the ELF backend about them, in case they are assignments to + symbols which are referred to by dynamic objects. */ static void -gld${EMULATION_NAME}_vercheck (s) - lang_input_statement_type *s; +gld${EMULATION_NAME}_find_statement_assignment (s) + lang_statement_union_type *s; { - const char *soname, *f; - struct bfd_link_needed_list *l; - - if (global_vercheck_failed) - return; - if (s->the_bfd == NULL - || (bfd_get_file_flags (s->the_bfd) & DYNAMIC) == 0) - return; - - soname = bfd_elf_get_dt_soname (s->the_bfd); - if (soname == NULL) - soname = bfd_get_filename (s->the_bfd); - - f = strrchr (soname, '/'); - if (f != NULL) - ++f; - else - f = soname; - - for (l = global_vercheck_needed; l != NULL; l = l->next) - { - const char *suffix; - - if (strcmp (f, l->name) == 0) - { - /* Probably can't happen, but it's an easy check. */ - continue; - } - - if (strchr (l->name, '/') != NULL) - continue; - - suffix = strstr (l->name, ".so."); - if (suffix == NULL) - continue; - - suffix += sizeof ".so." - 1; - - if (strncmp (f, l->name, suffix - l->name) == 0) - { - /* Here we know that S is a dynamic object FOO.SO.VER1, and - the object we are considering needs a dynamic object - FOO.SO.VER2, and VER1 and VER2 are different. This - appears to be a version mismatch, so we tell the caller - to try a different version of this library. */ - global_vercheck_failed = true; - return; - } - } + if (s->header.type == lang_assignment_statement_enum) + gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp); } + /* This is called after the sections have been attached to output sections, but before any sizes or addresses have been set. */ @@ -804,76 +801,113 @@ gld${EMULATION_NAME}_before_allocation () } } -/* This is called by the before_allocation routine via - lang_for_each_statement. It locates any assignment statements, and - tells the ELF backend 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; +/* Try to open a dynamic archive. This is where we know that ELF + dynamic libraries have an extension of .so (or .sl on oddball systems + like hpux). */ + +static boolean +gld${EMULATION_NAME}_open_dynamic_archive (arch, search, entry) + const char *arch; + search_dirs_type *search; + lang_input_statement_type *entry; { - if (s->header.type == lang_assignment_statement_enum) - gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp); -} + const char *filename; + char *string; -/* Look through an expression for an assignment statement. */ + if (! entry->is_archive) + return false; -static void -gld${EMULATION_NAME}_find_exp_assignment (exp) - etree_type *exp; -{ - struct bfd_link_hash_entry *h; + filename = entry->filename; - switch (exp->type.node_class) + /* This allocates a few bytes too many when EXTRA_SHLIB_EXTENSION + is defined, but it does not seem worth the headache to optimize + away those two bytes of space. */ + string = (char *) xmalloc (strlen (search->name) + + strlen (filename) + + strlen (arch) +#ifdef EXTRA_SHLIB_EXTENSION + + strlen (EXTRA_SHLIB_EXTENSION) +#endif + + sizeof "/lib.so"); + + sprintf (string, "%s/lib%s%s.so", search->name, filename, arch); + +#ifdef EXTRA_SHLIB_EXTENSION + /* Try the .so extension first. If that fails build a new filename + using EXTRA_SHLIB_EXTENSION. */ + if (! ldfile_try_open_bfd (string, entry)) + sprintf (string, "%s/lib%s%s%s", search->name, + filename, arch, EXTRA_SHLIB_EXTENSION); +#endif + + if (! ldfile_try_open_bfd (string, entry)) { - case etree_provide: - h = bfd_link_hash_lookup (link_info.hash, exp->assign.dst, - false, false, false); - if (h == NULL) - break; + free (string); + return false; + } - /* We call record_link_assignment even if the symbol is defined. - This is because if it is defined by a dynamic object, we - actually want to use the value defined by the linker script, - not the value from the dynamic object (because we are setting - symbols like etext). If the symbol is defined by a regular - object, then, as it happens, calling record_link_assignment - will do no harm. */ + entry->filename = string; - /* Fall through. */ - case etree_assign: - if (strcmp (exp->assign.dst, ".") != 0) - { - if (! (bfd_elf${ELFSIZE}_record_link_assignment - (output_bfd, &link_info, exp->assign.dst, - exp->type.node_class == etree_provide ? true : false))) - einfo ("%P%F: failed to record assignment to %s: %E\n", - exp->assign.dst); - } - gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src); - break; + /* We have found a dynamic object to include in the link. The ELF + backend linker will create a DT_NEEDED entry in the .dynamic + section naming this file. If this file includes a DT_SONAME + entry, it will be used. Otherwise, the ELF linker will just use + the name of the file. For an archive found by searching, like + this one, the DT_NEEDED entry should consist of just the name of + the file, without the path information used to find it. Note + that we only need to do this if we have a dynamic object; an + archive will never be referenced by a DT_NEEDED entry. - case etree_binary: - gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs); - gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs); - break; + FIXME: This approach--using bfd_elf_set_dt_needed_name--is not + very pretty. I haven't been able to think of anything that is + pretty, though. */ + if (bfd_check_format (entry->the_bfd, bfd_object) + && (entry->the_bfd->flags & DYNAMIC) != 0) + { + char *needed_name; - case etree_trinary: - gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.cond); - gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); - gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs); - break; + ASSERT (entry->is_archive && entry->search_dirs_flag); - case etree_unary: - gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child); - break; + /* Rather than duplicating the logic above. Just use the + filename we recorded earlier. - default: - break; + First strip off everything before the last '/'. */ + filename = strrchr (entry->filename, '/'); + filename++; + + needed_name = (char *) xmalloc (strlen (filename) + 1); + strcpy (needed_name, filename); + bfd_elf_set_dt_needed_name (entry->the_bfd, needed_name); + } + + return true; +} + + +/* A variant of lang_output_section_find. */ +static lang_output_section_statement_type * +output_rel_find () +{ + lang_statement_union_type *u; + lang_output_section_statement_type *lookup; + + for (u = lang_output_section_statement.head; + u != (lang_statement_union_type *) NULL; + u = lookup->next) + { + lookup = &u->output_section_statement; + if (strncmp (".rel", lookup->name, 4) == 0 + && lookup->bfd_section != NULL + && (lookup->bfd_section->flags & SEC_ALLOC) != 0) + { + return lookup; + } } + return (lang_output_section_statement_type *) NULL; } + /* Place an orphan section. We use this to put random SHF_ALLOC sections in the right segment. */ @@ -1103,27 +1137,6 @@ gld${EMULATION_NAME}_place_orphan (file, s) return true; } -/* A variant of lang_output_section_find. */ -static lang_output_section_statement_type * -output_rel_find () -{ - lang_statement_union_type *u; - lang_output_section_statement_type *lookup; - - for (u = lang_output_section_statement.head; - u != (lang_statement_union_type *) NULL; - u = lookup->next) - { - lookup = &u->output_section_statement; - if (strncmp (".rel", lookup->name, 4) == 0 - && lookup->bfd_section != NULL - && (lookup->bfd_section->flags & SEC_ALLOC) != 0) - { - return lookup; - } - } - return (lang_output_section_statement_type *) NULL; -} static char * gld${EMULATION_NAME}_get_script(isfile) @@ -1243,6 +1256,7 @@ cat >>e${EMULATION_NAME}.c <<EOF {NULL, no_argument, NULL, 0} }; + static int gld_${EMULATION_NAME}_parse_args (argc, argv) int argc; @@ -1325,6 +1339,7 @@ cat >>e${EMULATION_NAME}.c <<EOF return 1; } + static void gld_${EMULATION_NAME}_list_options (file) FILE * file; |