diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 333e08dbb943..f489eddd5610 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -61,7 +61,6 @@ map_object(int fd, const char *path, const struct stat *sb) Elf_Phdr **segs; int nsegs; Elf_Phdr *phdyn; - Elf_Phdr *phphdr; Elf_Phdr *phinterp; Elf_Phdr *phtls; caddr_t mapbase; @@ -79,7 +78,8 @@ map_object(int fd, const char *path, const struct stat *sb) Elf_Addr clear_vaddr; caddr_t clear_addr; caddr_t clear_page; - size_t nclear; + Elf_Addr phdr_vaddr; + size_t nclear, phsize; Elf_Addr bss_vaddr; Elf_Addr bss_vlimit; caddr_t bss_addr; @@ -95,9 +95,11 @@ map_object(int fd, const char *path, const struct stat *sb) * in that order. */ phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff); + phsize = hdr->e_phnum * sizeof (phdr[0]); phlimit = phdr + hdr->e_phnum; nsegs = -1; - phdyn = phphdr = phinterp = phtls = NULL; + phdyn = phinterp = phtls = NULL; + phdr_vaddr = 0; segs = alloca(sizeof(segs[0]) * hdr->e_phnum); while (phdr < phlimit) { switch (phdr->p_type) { @@ -108,7 +110,7 @@ map_object(int fd, const char *path, const struct stat *sb) case PT_LOAD: segs[++nsegs] = phdr; - if (segs[nsegs]->p_align < PAGE_SIZE) { + if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) { _rtld_error("%s: PT_LOAD segment %d not page-aligned", path, nsegs); return NULL; @@ -116,7 +118,8 @@ map_object(int fd, const char *path, const struct stat *sb) break; case PT_PHDR: - phphdr = phdr; + phdr_vaddr = phdr->p_vaddr; + phsize = phdr->p_memsz; break; case PT_DYNAMIC: @@ -211,6 +214,11 @@ map_object(int fd, const char *path, const struct stat *sb) return NULL; } } + if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff && + (data_vlimit - data_vaddr + data_offset) >= + (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) { + phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset; + } } obj = obj_new(); @@ -227,10 +235,19 @@ map_object(int fd, const char *path, const struct stat *sb) obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); if (hdr->e_entry != 0) obj->entry = (caddr_t) (obj->relocbase + hdr->e_entry); - if (phphdr != NULL) { - obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_vaddr); - obj->phsize = phphdr->p_memsz; + if (phdr_vaddr != 0) { + obj->phdr = (const Elf_Phdr *) (obj->relocbase + phdr_vaddr); + } else { + obj->phdr = malloc(phsize); + if (obj->phdr == NULL) { + obj_free(obj); + _rtld_error("%s: cannot allocate program header", path); + return NULL; + } + memcpy((char *)obj->phdr, (char *)hdr + hdr->e_phoff, phsize); + obj->phdr_alloc = true; } + obj->phsize = phsize; if (phinterp != NULL) obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr); if (phtls != NULL) { @@ -307,7 +324,6 @@ obj_free(Obj_Entry *obj) if (obj->tls_done) free_tls_offset(obj); - free(obj->path); while (obj->needed != NULL) { Needed_Entry *needed = obj->needed; obj->needed = needed->next; @@ -328,9 +344,16 @@ obj_free(Obj_Entry *obj) STAILQ_REMOVE_HEAD(&obj->dagmembers, link); free(elm); } - free(obj->vertab); - free(obj->origin_path); - free(obj->priv); + if (obj->vertab) + free(obj->vertab); + if (obj->origin_path) + free(obj->origin_path); + if (obj->priv) + free(obj->priv); + if (obj->path) + free(obj->path); + if (obj->phdr_alloc) + free((void *)obj->phdr); free(obj); } diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index e8e31c579780..174c6ea3842d 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -164,6 +164,7 @@ static Obj_Entry **obj_tail; /* Link field of last object in list */ static Obj_Entry *obj_main; /* The main program shared object */ static Obj_Entry obj_rtld; /* The dynamic linker shared object */ static unsigned int obj_count; /* Number of objects in obj_list */ +static unsigned int obj_loads; /* Number of objects in obj_list */ static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ STAILQ_HEAD_INITIALIZER(list_global); @@ -204,6 +205,7 @@ static func_ptr_type exports[] = { (func_ptr_type) &__tls_get_addr, (func_ptr_type) &_rtld_allocate_tls, (func_ptr_type) &_rtld_free_tls, + (func_ptr_type) &dl_iterate_phdr, NULL }; @@ -423,6 +425,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) *obj_tail = obj_main; obj_tail = &obj_main->next; obj_count++; + obj_loads++; /* Make sure we don't call the main program's init and fini functions. */ obj_main->init = obj_main->fini = (Elf_Addr)NULL; @@ -1387,6 +1390,7 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp) *obj_tail = obj; obj_tail = &obj->next; obj_count++; + obj_loads++; linkmap_add(obj); /* for GDB & dlinfo() */ dbg(" %p .. %p: %s", obj->mapbase, @@ -2079,6 +2083,37 @@ dlinfo(void *handle, int request, void *p) return (error); } +int +dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) +{ + struct dl_phdr_info phdr_info; + const Obj_Entry *obj; + int error, lockstate; + + lockstate = rlock_acquire(rtld_bind_lock); + + error = 0; + + for (obj = obj_list; obj != NULL; obj = obj->next) { + phdr_info.dlpi_addr = (Elf_Addr)obj->relocbase; + phdr_info.dlpi_name = STAILQ_FIRST(&obj->names) ? + STAILQ_FIRST(&obj->names)->name : obj->path; + phdr_info.dlpi_phdr = obj->phdr; + phdr_info.dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]); + phdr_info.dlpi_tls_modid = obj->tlsindex; + phdr_info.dlpi_tls_data = obj->tlsinit; + phdr_info.dlpi_adds = obj_loads; + phdr_info.dlpi_subs = obj_loads - obj_count; + + if ((error = callback(&phdr_info, sizeof phdr_info, param)) != 0) + break; + + } + rlock_release(rtld_bind_lock, lockstate); + + return (error); +} + struct fill_search_info_args { int request; unsigned int flags; diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index cc430e86f8f4..c7be5235578a 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -201,15 +201,16 @@ typedef struct Struct_Obj_Entry { Elf_Addr init; /* Initialization function to call */ Elf_Addr fini; /* Termination function to call */ - bool mainprog; /* True if this is the main program */ - bool rtld; /* True if this is the dynamic linker */ - bool textrel; /* True if there are relocations to text seg */ - bool symbolic; /* True if generated with "-Bsymbolic" */ - bool bind_now; /* True if all relocations should be made first */ - bool traced; /* Already printed in ldd trace output */ - bool jmpslots_done; /* Already have relocated the jump slots */ - bool init_done; /* Already have added object to init list */ - bool tls_done; /* Already allocated offset for static TLS */ + bool mainprog : 1; /* True if this is the main program */ + bool rtld : 1; /* True if this is the dynamic linker */ + bool textrel : 1; /* True if there are relocations to text seg */ + bool symbolic : 1; /* True if generated with "-Bsymbolic" */ + bool bind_now : 1; /* True if all relocations should be made first */ + bool traced : 1; /* Already printed in ldd trace output */ + bool jmpslots_done : 1; /* Already have relocated the jump slots */ + bool init_done : 1; /* Already have added object to init list */ + bool tls_done : 1; /* Already allocated offset for static TLS */ + bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */ struct link_map linkmap; /* for GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */