diff options
Diffstat (limited to 'contrib/binutils/ld/emultempl/elf32.em')
-rw-r--r-- | contrib/binutils/ld/emultempl/elf32.em | 244 |
1 files changed, 198 insertions, 46 deletions
diff --git a/contrib/binutils/ld/emultempl/elf32.em b/contrib/binutils/ld/emultempl/elf32.em index f02775e..c4debe2 100644 --- a/contrib/binutils/ld/emultempl/elf32.em +++ b/contrib/binutils/ld/emultempl/elf32.em @@ -7,7 +7,7 @@ cat >e${EMULATION_NAME}.c <<EOF /* This file is is generated by a shell script. DO NOT EDIT! */ /* ${ELFSIZE} bit ELF emulation code for ${EMULATION_NAME} - Copyright (C) 1991, 93, 94, 95, 96, 1997 Free Software Foundation, Inc. + Copyright (C) 1991, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc. Written by Steve Chamberlain <sac@cygnus.com> ELF support by Ian Lance Taylor <ian@cygnus.com> @@ -54,8 +54,10 @@ static void gld${EMULATION_NAME}_check_needed static void gld${EMULATION_NAME}_stat_needed PARAMS ((lang_input_statement_type *)); static boolean gld${EMULATION_NAME}_search_needed - PARAMS ((const char *, const char *)); -static boolean gld${EMULATION_NAME}_try_needed PARAMS ((const char *)); + PARAMS ((const char *, const char *, int)); +static boolean gld${EMULATION_NAME}_try_needed PARAMS ((const char *, int)); +static void gld${EMULATION_NAME}_vercheck + 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 *)); @@ -143,11 +145,13 @@ cat >>e${EMULATION_NAME}.c <<EOF in which we may find shared libraries. /etc/ld.so.conf is really only meaningful on Linux, but we check it on other systems anyhow. */ -static boolean gld${EMULATION_NAME}_check_ld_so_conf PARAMS ((const char *)); +static boolean gld${EMULATION_NAME}_check_ld_so_conf + PARAMS ((const char *, int)); static boolean -gld${EMULATION_NAME}_check_ld_so_conf (name) +gld${EMULATION_NAME}_check_ld_so_conf (name, force) const char *name; + int force; { static boolean initialized; static char *ld_so_conf; @@ -215,7 +219,7 @@ gld${EMULATION_NAME}_check_ld_so_conf (name) if (ld_so_conf == NULL) return false; - return gld${EMULATION_NAME}_search_needed (ld_so_conf, name); + return gld${EMULATION_NAME}_search_needed (ld_so_conf, name, force); } EOF @@ -224,11 +228,13 @@ 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. */ + 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; /* This is called after all the input files have been opened. */ @@ -254,9 +260,7 @@ gld${EMULATION_NAME}_after_open () for (l = needed; l != NULL; l = l->next) { struct bfd_link_needed_list *ll; - const char *lib_path; - size_t len; - search_dirs_type *search; + int force; /* If we've already seen this file, skip it. */ for (ll = needed; ll != l; ll = ll->next) @@ -277,55 +281,71 @@ gld${EMULATION_NAME}_after_open () linker will search. That means that we want to use rpath_link, rpath, then the environment variable LD_LIBRARY_PATH (native only), then the linker script - LIB_SEARCH_DIRS. We do not search using the -L arguments. */ - if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link, - l->name)) - continue; - if (gld${EMULATION_NAME}_search_needed (command_line.rpath, l->name)) - continue; - if (command_line.rpath_link == NULL - && command_line.rpath == NULL) + LIB_SEARCH_DIRS. We do not search using the -L arguments. + + We search twice. The first time, we skip objects which may + introduce version mismatches. The second time, we force + their use. See gld${EMULATION_NAME}_vercheck comment. */ + for (force = 0; force < 2; force++) { - lib_path = (const char *) getenv ("LD_RUN_PATH"); - if (gld${EMULATION_NAME}_search_needed (lib_path, l->name)) - continue; - } + const char *lib_path; + size_t len; + search_dirs_type *search; + + if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link, + l->name, force)) + break; + if (gld${EMULATION_NAME}_search_needed (command_line.rpath, + l->name, force)) + break; + if (command_line.rpath_link == NULL + && command_line.rpath == NULL) + { + lib_path = (const char *) getenv ("LD_RUN_PATH"); + if (gld${EMULATION_NAME}_search_needed (lib_path, l->name, + force)) + break; + } EOF if [ "x${host}" = "x${target}" ] ; then if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then cat >>e${EMULATION_NAME}.c <<EOF - lib_path = (const char *) getenv ("LD_LIBRARY_PATH"); - if (gld${EMULATION_NAME}_search_needed (lib_path, l->name)) - continue; + lib_path = (const char *) getenv ("LD_LIBRARY_PATH"); + if (gld${EMULATION_NAME}_search_needed (lib_path, l->name, force)) + break; EOF fi fi cat >>e${EMULATION_NAME}.c <<EOF - len = strlen (l->name); - for (search = search_head; search != NULL; search = search->next) - { - char *filename; - - if (search->cmdline) - continue; - filename = (char *) xmalloc (strlen (search->name) + len + 2); - sprintf (filename, "%s/%s", search->name, l->name); - if (gld${EMULATION_NAME}_try_needed (filename)) + len = strlen (l->name); + for (search = search_head; search != NULL; search = search->next) + { + char *filename; + + if (search->cmdline) + continue; + filename = (char *) xmalloc (strlen (search->name) + len + 2); + sprintf (filename, "%s/%s", search->name, l->name); + if (gld${EMULATION_NAME}_try_needed (filename, force)) + break; + free (filename); + } + if (search != NULL) break; - free (filename); - } - if (search != NULL) - continue; EOF if [ "x${host}" = "x${target}" ] ; then if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then cat >>e${EMULATION_NAME}.c <<EOF - if (gld${EMULATION_NAME}_check_ld_so_conf (l->name)) - continue; + if (gld${EMULATION_NAME}_check_ld_so_conf (l->name, force)) + break; EOF fi fi cat >>e${EMULATION_NAME}.c <<EOF + } + + if (force < 2) + continue; einfo ("%P: warning: %s, needed by %B, not found (try using --rpath)\n", l->name, l->by); @@ -335,9 +355,10 @@ cat >>e${EMULATION_NAME}.c <<EOF /* Search for a needed file in a path. */ static boolean -gld${EMULATION_NAME}_search_needed (path, name) +gld${EMULATION_NAME}_search_needed (path, name, force) const char *path; const char *name; + int force; { const char *s; size_t len; @@ -364,7 +385,7 @@ gld${EMULATION_NAME}_search_needed (path, name) } strcpy (sset, name); - if (gld${EMULATION_NAME}_try_needed (filename)) + if (gld${EMULATION_NAME}_try_needed (filename, force)) return true; free (filename); @@ -378,11 +399,13 @@ gld${EMULATION_NAME}_search_needed (path, name) } /* This function is called for each possible name for a dynamic object - named by a DT_NEEDED entry. */ + 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) +gld${EMULATION_NAME}_try_needed (name, force) const char *name; + int force; { bfd *abfd; @@ -400,6 +423,62 @@ gld${EMULATION_NAME}_try_needed (name) 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 @@ -538,6 +617,78 @@ gld${EMULATION_NAME}_stat_needed (s) 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. */ + +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; + } + } +} + /* This is called after the sections have been attached to output sections, but before any sizes or addresses have been set. */ @@ -844,7 +995,7 @@ gld${EMULATION_NAME}_place_orphan (file, s) /* If the name of the section is representable in C, then create symbols to mark the start and the end of the section. */ for (ps = outsecname; *ps != '\0'; ps++) - if (! isalnum (*ps) && *ps != '_') + if (! isalnum ((unsigned char) *ps) && *ps != '_') break; if (*ps == '\0' && config.build_constructors) { @@ -907,6 +1058,7 @@ gld${EMULATION_NAME}_place_section (s) os = &s->output_section_statement; if (strcmp (os->name, hold_section->name) == 0 + && os->bfd_section != NULL && ((hold_section->flags & (SEC_LOAD | SEC_ALLOC)) == (os->bfd_section->flags & (SEC_LOAD | SEC_ALLOC)))) hold_use = os; |