diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 621d084..31cc665 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -117,6 +117,13 @@ static void objlist_push_head(Objlist *, Obj_Entry *); static void objlist_push_tail(Objlist *, Obj_Entry *); static void objlist_remove(Objlist *, Obj_Entry *); static void *path_enumerate(const char *, path_enum_proc, void *); +static void relocate_add_neededs(Needed_Entry *needed, Objlist *list); +static void relocate_add_objects(Obj_Entry *obj, Obj_Entry **tail, + Objlist *list); +static int relocate_objects_list(Objlist *list, bool bind_now, + Obj_Entry *rtldobj, int flags, RtldLockState *lockstate); +static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate); static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int, RtldLockState *); static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, @@ -2216,6 +2223,59 @@ objlist_remove(Objlist *list, Obj_Entry *obj) } } +static void +relocate_add_neededs(Needed_Entry *needed, Objlist *list) +{ + + if (needed->next != NULL) + relocate_add_neededs(needed->next, list); + + if (needed->obj != NULL) + relocate_add_objects(needed->obj, &needed->obj->next, list); +} + +static void +relocate_add_objects(Obj_Entry *obj, Obj_Entry **tail, Objlist *list) +{ + + if (obj->relocated || obj->relocate_scanned) + return; + obj->relocate_scanned = true; + + if (&obj->next != tail) + relocate_add_objects(obj->next, tail, list); + + if (obj->needed != NULL) + relocate_add_neededs(obj->needed, list); + if (obj->needed_filtees != NULL) + relocate_add_neededs(obj->needed_filtees, list); + if (obj->needed_aux_filtees != NULL) + relocate_add_neededs(obj->needed_aux_filtees, list); + + objlist_push_tail(list, obj); +} + +static int +relocate_objects_list(Objlist *list, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate) +{ + Objlist_Entry *elm; + Obj_Entry *obj; + int error; + + error = 0; + for (obj = obj_list; obj != NULL; obj = obj->next) + obj->relocate_scanned = false; + + STAILQ_FOREACH(elm, list, link) { + error = relocate_object(elm->obj, bind_now, rtldobj, flags, + lockstate); + if (error == -1) + break; + } + return (error); +} + /* * Relocate newly-loaded shared objects. The argument is a pointer to * the Obj_Entry for the first such object. All objects from the first @@ -2223,46 +2283,44 @@ objlist_remove(Objlist *list, Obj_Entry *obj) * or -1 on failure. */ static int -relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, +relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate) { - Obj_Entry *obj; - for (obj = first; obj != NULL; obj = obj->next) { if (obj->relocated) - continue; + return (0); obj->relocated = true; if (obj != rtldobj) - dbg("relocating \"%s\"", obj->path); + dbg("relocating \"%s\"", obj->path); if (obj->symtab == NULL || obj->strtab == NULL || - !(obj->valid_hash_sysv || obj->valid_hash_gnu)) { - _rtld_error("%s: Shared object has no run-time symbol table", - obj->path); - return -1; + !(obj->valid_hash_sysv || obj->valid_hash_gnu)) { + _rtld_error("%s: Shared object has no run-time symbol table", + obj->path); + return (-1); } if (obj->textrel) { - /* There are relocations to the write-protected text segment. */ - if (mprotect(obj->mapbase, obj->textsize, - PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { - _rtld_error("%s: Cannot write-enable text segment: %s", - obj->path, rtld_strerror(errno)); - return -1; - } + /* There are relocations to the write-protected text segment. */ + if (mprotect(obj->mapbase, obj->textsize, + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + _rtld_error("%s: Cannot write-enable text segment: %s", + obj->path, rtld_strerror(errno)); + return (-1); + } } /* Process the non-PLT relocations. */ if (reloc_non_plt(obj, rtldobj, flags, lockstate)) - return -1; + return (-1); if (obj->textrel) { /* Re-protected the text segment. */ - if (mprotect(obj->mapbase, obj->textsize, - PROT_READ|PROT_EXEC) == -1) { - _rtld_error("%s: Cannot write-protect text segment: %s", - obj->path, rtld_strerror(errno)); - return -1; - } + if (mprotect(obj->mapbase, obj->textsize, + PROT_READ|PROT_EXEC) == -1) { + _rtld_error("%s: Cannot write-protect text segment: %s", + obj->path, rtld_strerror(errno)); + return (-1); + } } @@ -2271,18 +2329,19 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, /* Process the PLT relocations. */ if (reloc_plt(obj) == -1) - return -1; + return (-1); /* Relocate the jump slots if we are doing immediate binding. */ if (obj->bind_now || bind_now) - if (reloc_jmpslots(obj, flags, lockstate) == -1) - return -1; + if (reloc_jmpslots(obj, flags, lockstate) == -1) + return (-1); if (obj->relro_size > 0) { - if (mprotect(obj->relro_page, obj->relro_size, PROT_READ) == -1) { - _rtld_error("%s: Cannot enforce relro protection: %s", - obj->path, rtld_strerror(errno)); - return -1; - } + if (mprotect(obj->relro_page, obj->relro_size, + PROT_READ) == -1) { + _rtld_error("%s: Cannot enforce relro protection: %s", + obj->path, rtld_strerror(errno)); + return (-1); + } } /* @@ -2292,9 +2351,30 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, */ obj->magic = RTLD_MAGIC; obj->version = RTLD_VERSION; - } - return (0); + return (0); +} + +/* + * Relocate newly-loaded shared objects. The argument is a pointer to + * the Obj_Entry for the first such object. All objects from the first + * to the end of the list of objects are relocated. Returns 0 on success, + * or -1 on failure. + */ +static int +relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate) +{ + Obj_Entry *obj; + int error; + + for (error = 0, obj = first; obj != NULL; obj = obj->next) { + error = relocate_object(obj, bind_now, rtldobj, flags, + lockstate); + if (error == -1) + break; + } + return (error); } /* @@ -2579,11 +2659,12 @@ dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, { Obj_Entry **old_obj_tail; Obj_Entry *obj; - Objlist initlist; + Objlist initlist, reloclist; RtldLockState mlockstate; int result; objlist_init(&initlist); + objlist_init(&reloclist); if (lockstate == NULL && !(lo_flags & RTLD_LO_EARLY)) { wlock_acquire(rtld_bind_lock, &mlockstate); @@ -2614,10 +2695,15 @@ dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, result = rtld_verify_versions(&obj->dagmembers); if (result != -1 && ld_tracing) goto trace; - if (result == -1 || (relocate_objects(obj, - (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld, - (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, - lockstate)) == -1) { + if (result != -1) { + relocate_add_objects(obj, &obj->next, &reloclist); + result = relocate_objects_list(&reloclist, + (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld, + (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, + lockstate); + objlist_clear(&reloclist); + } + if (result == -1) { dlopen_cleanup(obj); obj = NULL; } else if (lo_flags & RTLD_LO_EARLY) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 47389fe..e41d7e4 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -245,6 +245,7 @@ typedef struct Struct_Obj_Entry { bool mainprog : 1; /* True if this is the main program */ bool rtld : 1; /* True if this is the dynamic linker */ bool relocated : 1; /* True if processed by relocate_objects() */ + bool relocate_scanned : 1; /* True if object is already on reloc list */ bool ver_checked : 1; /* True if processed by rtld_verify_object_versions */ bool textrel : 1; /* True if there are relocations to text seg */ bool symbolic : 1; /* True if generated with "-Bsymbolic" */