Index: rtld.c =================================================================== RCS file: /home/iedowse/CVS/src/libexec/rtld-elf/rtld.c,v retrieving revision 1.69 diff -u -r1.69 rtld.c --- rtld.c 23 Oct 2002 01:43:29 -0000 1.69 +++ rtld.c 10 Nov 2002 02:20:02 -0000 @@ -72,6 +72,7 @@ * Function declarations. */ static const char *basename(const char *); +static int check_libversion(const char *, Obj_Entry **); static void die(void); static void digest_dynamic(Obj_Entry *, int); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); @@ -79,7 +80,7 @@ static bool donelist_check(DoneList *, const Obj_Entry *); static void errmsg_restore(char *); static char *errmsg_save(void); -static char *find_library(const char *, const Obj_Entry *); +static char *find_library(const char *, const Obj_Entry *, int); static const char *gethints(void); static void init_dag(Obj_Entry *); static void init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *); @@ -105,8 +106,10 @@ static void objlist_remove(Objlist *, Obj_Entry *); static void objlist_remove_unref(Objlist *); static int relocate_objects(Obj_Entry *, bool, Obj_Entry *); +static void remove_conflicting_lib(Obj_Entry *); +static void remove_objref(Obj_Entry *); static void rtld_exit(void); -static char *search_library_path(const char *, const char *); +static char *search_library_path(const char *, const char *, int *); static const void **get_program_var_addr(const char *name); static void set_program_var(const char *, const void *); static const Elf_Sym *symlook_default(const char *, unsigned long hash, @@ -593,6 +596,7 @@ if (!obj->rtld) { Needed_Entry *nep = NEW(Needed_Entry); nep->name = dynp->d_un.d_val; + nep->idx = 0; nep->obj = NULL; nep->next = NULL; @@ -784,6 +788,10 @@ * If the second argument is non-NULL, then it refers to an already- * loaded shared object, whose library search path will be searched. * + * The third argument specifies which path to return if there are + * multiple libraries that match the name. idx = 0 requests the first + * match, idx = 1 requests the second etc. + * * The search order is: * rpath in the referencing file * LD_LIBRARY_PATH @@ -791,11 +799,13 @@ * /usr/lib */ static char * -find_library(const char *name, const Obj_Entry *refobj) +find_library(const char *name, const Obj_Entry *refobj, int idx) { char *pathname; if (strchr(name, '/') != NULL) { /* Hard coded pathname */ + if (idx != 0) + return NULL; if (name[0] != '/' && !trust) { _rtld_error("Absolute pathname required for shared object \"%s\"", name); @@ -804,16 +814,18 @@ return xstrdup(name); } - dbg(" Searching for \"%s\"", name); + dbg(" Searching for \"%s\" idx=%d", name, idx); - if ((pathname = search_library_path(name, ld_library_path)) != NULL || + if ((pathname = search_library_path(name, ld_library_path, &idx)) != NULL || (refobj != NULL && - (pathname = search_library_path(name, refobj->rpath)) != NULL) || - (pathname = search_library_path(name, gethints())) != NULL || - (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL) + (pathname = search_library_path(name, refobj->rpath, &idx)) != NULL) || + (pathname = search_library_path(name, gethints(), &idx)) != NULL || + (pathname = search_library_path(name, STANDARD_LIBRARY_PATH, + &idx)) != NULL) return pathname; - _rtld_error("Shared object \"%s\" not found", name); + if (idx <= 0) + _rtld_error("Shared object \"%s\" not found", name); return NULL; } @@ -1078,18 +1090,63 @@ static int load_needed_objects(Obj_Entry *first) { - Obj_Entry *obj; + Obj_Entry *obj, *otherobj, *zapobj; +restart: for (obj = first; obj != NULL; obj = obj->next) { Needed_Entry *needed; + /* + * Check first if any needed libraries conflict with libraries + * that are already loaded. + */ for (needed = obj->needed; needed != NULL; needed = needed->next) { const char *name = obj->strtab + needed->name; - char *path = find_library(name, obj); - needed->obj = NULL; - if (path == NULL && !ld_tracing) - return -1; + switch (check_libversion(name, &otherobj)) { + case -1: + /* + * The object needs a lower-numbered version of a library + * that is already loaded. We must remove the existing one. + */ + zapobj = otherobj; + break; + case 1: + /* + * The object needs a greater-numbered version of a library + * that is already loaded. Remove this object. + */ + zapobj = obj; + break; + default: + continue; + } + + remove_conflicting_lib(zapobj); + goto restart; + } + + for (needed = obj->needed; needed != NULL; needed = needed->next) { + const char *name = obj->strtab + needed->name; + char *path; + + if (needed->obj != NULL) + continue; + + path = find_library(name, obj, needed->idx); + if (path == NULL) { + if (needed->idx != 0 && obj != first) { + /* + * We removed this dependency due to a version conflict + * but there isn't a replacement. Try back-tracking + * even further by removing this object. + */ + remove_conflicting_lib(obj); + goto restart; + } + if (!ld_tracing) + return -1; + } if (path) { needed->obj = load_object(path); @@ -1119,7 +1176,7 @@ savech = p[len]; p[len] = '\0'; - if ((path = find_library(p, NULL)) == NULL) + if ((path = find_library(p, NULL, 0)) == NULL) return -1; if (load_object(path) == NULL) return -1; /* XXX - cleanup */ @@ -1460,7 +1517,7 @@ } static char * -search_library_path(const char *name, const char *path) +search_library_path(const char *name, const char *path, int *idxp) { size_t namelen = strlen(name); const char *p = path; @@ -1483,8 +1540,10 @@ strcpy(pathname + dirlen + 1, name); dbg(" Trying \"%s\"", pathname); - if (access(pathname, F_OK) == 0) /* We found it */ - return pathname; + if (access(pathname, F_OK) == 0) { /* We found it */ + if (idxp == NULL || (*idxp)-- == 0) + return pathname; + } free(pathname); } @@ -1583,7 +1642,7 @@ obj = obj_main; obj->refcount++; } else { - char *path = find_library(name, obj_main); + char *path = find_library(name, obj_main, 0); if (path != NULL) obj = load_object(path); } @@ -2166,6 +2225,117 @@ for (needed = root->needed; needed != NULL; needed = needed->next) if (needed->obj != NULL) unref_dag(needed->obj); +} + +/* + * Check whether loading the library `name' would conflict with an already + * loaded library that has the same name but a different version. Returns + * 0 if there is no conflict. Otherwise the return value indicates which + * of the two libraries has the greater version number: + * Returns -1 if `name' has a lower version than the conflicting library. + * Returns 1 if `name' has a greater version than the conflicting library. + */ +static int +check_libversion(const char *name, Obj_Entry **otherobjp) +{ + Obj_Entry *obj; + const char *basep, *p, *versp; + int baselen, v, vers; + + dbg("check_libversion: name `%s'", name); + + /* + * Get the version and the `libfoo.so.' bit. Make sure that the name + * does end in .so.[0-9]+. + */ + versp = strrchr(name, '.'); + if (versp == NULL) + return 0; + versp++; + + vers = 0; + for (p = versp; *p >= '0' && *p <= '9'; p++) + vers = 10 * vers + *p - '0'; + if (*p != '\0' || p == versp) + return 0; + + basep = basename(name); + baselen = versp - basep; + if (baselen < 4 || strncmp(versp - 4, ".so.", 4) != 0) + return 0; + + /* Search for other loaded libraries with the same `libfoo.so.' name */ + for (obj = obj_list->next; obj != NULL; obj = obj->next) { + p = basename(obj->path); + if (strncmp(p, basep, baselen) != 0) + continue; + + v = 0; + for (p = p + baselen; *p >= '0' && *p <= '9'; p++) + v = 10 * v + *p - '0'; + if (*p != '\0' || p[-1] == '.') + continue; + + if (v == vers) + continue; + + dbg("version mismatch for `%s': have %d, need %d", obj->path, v, vers); + *otherobjp = obj; + return vers < v ? -1 : 1; + } + return 0; +} + +/* + * Remove a library that is causing a version conflict and increment + * the index in `needed' references to ensure that it doesn't attempt + * to load the same library again. + */ +static void +remove_conflicting_lib(Obj_Entry *zapobj) +{ + Obj_Entry *obj; + + dbg("remove_conflicting_lib: `%s'", zapobj->path); +restart: + for (obj = obj_list; obj != NULL; obj = obj->next) { + Needed_Entry *needed; + + for (needed = obj->needed; needed != NULL; needed = needed->next) { + if (needed->obj == zapobj) { + dbg("remove_conflicting_lib: removing link from `%s'", + obj->path); + needed->idx++; + needed->obj = NULL; + remove_objref(zapobj); + + /* Restart to be safe - maybe not necessary. */ + goto restart; + } + } + } +} + +/* + * Remove a reference to an object, and unload the object if the reference + * count reaches zero. + */ +static void +remove_objref(Obj_Entry *obj) +{ + Needed_Entry *needed; + + dbg("remove_objref: `%s' refcnt %d", obj->path, obj->refcount); + + obj->refcount--; + if (obj->refcount > 0) + return; + + for (needed = obj->needed; needed != NULL; needed = needed->next) { + if (needed->obj != NULL) + remove_objref(needed->obj); + } + unload_object(obj); } /* Index: rtld.h =================================================================== RCS file: /home/iedowse/CVS/src/libexec/rtld-elf/rtld.h,v retrieving revision 1.24 diff -u -r1.24 rtld.h --- rtld.h 29 Oct 2001 10:10:02 -0000 1.24 +++ rtld.h 10 Nov 2002 00:41:17 -0000 @@ -68,6 +68,7 @@ typedef struct Struct_Needed_Entry { struct Struct_Needed_Entry *next; struct Struct_Obj_Entry *obj; + int idx; /* Index for next find_library() call */ unsigned long name; /* Offset of name in string table */ } Needed_Entry;