Changeset View
Changeset View
Standalone View
Standalone View
libexec/rtld-elf/rtld.c
Show First 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | |||||
static void initlist_add_objects(Obj_Entry *, Obj_Entry *, Objlist *); | static void initlist_add_objects(Obj_Entry *, Obj_Entry *, Objlist *); | ||||
static int initlist_objects_ifunc(Objlist *, bool, int, RtldLockState *); | static int initlist_objects_ifunc(Objlist *, bool, int, RtldLockState *); | ||||
static void linkmap_add(Obj_Entry *); | static void linkmap_add(Obj_Entry *); | ||||
static void linkmap_delete(Obj_Entry *); | static void linkmap_delete(Obj_Entry *); | ||||
static void load_filtees(Obj_Entry *, int flags, RtldLockState *); | static void load_filtees(Obj_Entry *, int flags, RtldLockState *); | ||||
static void unload_filtees(Obj_Entry *, RtldLockState *); | static void unload_filtees(Obj_Entry *, RtldLockState *); | ||||
static int load_needed_objects(Obj_Entry *, int); | static int load_needed_objects(Obj_Entry *, int); | ||||
static int load_preload_objects(const char *, bool); | static int load_preload_objects(const char *, bool); | ||||
static int load_kpreload(const void *addr); | |||||
static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int); | static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int); | ||||
static void map_stacks_exec(RtldLockState *); | static void map_stacks_exec(RtldLockState *); | ||||
static int obj_disable_relro(Obj_Entry *); | static int obj_disable_relro(Obj_Entry *); | ||||
static int obj_enforce_relro(Obj_Entry *); | static int obj_enforce_relro(Obj_Entry *); | ||||
static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); | static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); | ||||
static void objlist_call_init(Objlist *, RtldLockState *); | static void objlist_call_init(Objlist *, RtldLockState *); | ||||
static void objlist_clear(Objlist *); | static void objlist_clear(Objlist *); | ||||
static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); | static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); | ||||
▲ Show 20 Lines • Show All 687 Lines • ▼ Show 20 Lines | obj_main->dynsymcount); | ||||
/* Initialize a fake symbol for resolving undefined weak references. */ | /* Initialize a fake symbol for resolving undefined weak references. */ | ||||
sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); | sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); | ||||
sym_zero.st_shndx = SHN_UNDEF; | sym_zero.st_shndx = SHN_UNDEF; | ||||
sym_zero.st_value = -(uintptr_t)obj_main->relocbase; | sym_zero.st_value = -(uintptr_t)obj_main->relocbase; | ||||
if (!libmap_disable) | if (!libmap_disable) | ||||
libmap_disable = (bool)lm_init(libmap_override); | libmap_disable = (bool)lm_init(libmap_override); | ||||
if (aux_info[AT_KPRELOAD] != NULL && | |||||
aux_info[AT_KPRELOAD]->a_un.a_ptr != NULL) { | |||||
dbg("loading kernel vdso"); | |||||
if (load_kpreload(aux_info[AT_KPRELOAD]->a_un.a_ptr) == -1) | |||||
rtld_die(); | |||||
} | |||||
dbg("loading LD_PRELOAD_FDS libraries"); | dbg("loading LD_PRELOAD_FDS libraries"); | ||||
if (load_preload_objects(ld_preload_fds, true) == -1) | if (load_preload_objects(ld_preload_fds, true) == -1) | ||||
rtld_die(); | rtld_die(); | ||||
dbg("loading LD_PRELOAD libraries"); | dbg("loading LD_PRELOAD libraries"); | ||||
if (load_preload_objects(ld_preload, false) == -1) | if (load_preload_objects(ld_preload, false) == -1) | ||||
rtld_die(); | rtld_die(); | ||||
preload_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q)); | preload_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q)); | ||||
▲ Show 20 Lines • Show All 1,998 Lines • ▼ Show 20 Lines | obj->path); | ||||
return (obj); | return (obj); | ||||
errp: | errp: | ||||
munmap(obj->mapbase, obj->mapsize); | munmap(obj->mapbase, obj->mapsize); | ||||
obj_free(obj); | obj_free(obj); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static int | |||||
load_kpreload(const void *addr) | |||||
{ | |||||
Obj_Entry *obj; | |||||
const Elf_Ehdr *ehdr; | |||||
const Elf_Phdr *phdr, *phlimit, *phdyn, *seg0, *segn; | |||||
static const char kname[] = "[vdso]"; | |||||
ehdr = addr; | |||||
if (!check_elf_headers(ehdr, "kpreload")) | |||||
return (-1); | |||||
obj = obj_new(); | |||||
phdr = (const Elf_Phdr *)((const char *)addr + ehdr->e_phoff); | |||||
obj->phdr = phdr; | |||||
obj->phsize = ehdr->e_phnum * sizeof(*phdr); | |||||
phlimit = phdr + ehdr->e_phnum; | |||||
seg0 = segn = NULL; | |||||
for (; phdr < phlimit; phdr++) { | |||||
switch (phdr->p_type) { | |||||
case PT_DYNAMIC: | |||||
phdyn = phdr; | |||||
break; | |||||
case PT_GNU_STACK: | |||||
emaste: We could default stack_flags to `PF_R | PF_W` for preload objs (and we could avoid… | |||||
Done Inline ActionsIn fact, it so happens that default stack mode for kpreload is 0. Right after I read your comment, I wanted to change it to either PF_R|_W or PF_R|_W|_X, but now I think this is actually not bad. I added a comment to make this more explicit. kib: In fact, it so happens that default stack mode for kpreload is 0. Right after I read your… | |||||
/* Absense of PT_GNU_STACK implies stack_flags == 0. */ | |||||
obj->stack_flags = phdr->p_flags; | |||||
break; | |||||
case PT_LOAD: | |||||
if (seg0 == NULL || seg0->p_vaddr > phdr->p_vaddr) | |||||
seg0 = phdr; | |||||
if (segn == NULL || segn->p_vaddr + segn->p_memsz < | |||||
phdr->p_vaddr + phdr->p_memsz) | |||||
segn = phdr; | |||||
break; | |||||
} | |||||
} | |||||
obj->mapbase = __DECONST(caddr_t, addr); | |||||
obj->mapsize = segn->p_vaddr + segn->p_memsz - (Elf_Addr)addr; | |||||
obj->vaddrbase = 0; | |||||
obj->relocbase = obj->mapbase; | |||||
object_add_name(obj, kname); | |||||
obj->path = xstrdup(kname); | |||||
obj->dynamic = (const Elf_Dyn *)(obj->relocbase + phdyn->p_vaddr); | |||||
if (!digest_dynamic(obj, 0)) { | |||||
obj_free(obj); | |||||
return (-1); | |||||
} | |||||
/* | |||||
Done Inline ActionsIs there any reason we'd want to check this and fail if we find any? I suspect no. emaste: Is there any reason we'd want to check this and fail if we find any? I suspect no. | |||||
Done Inline ActionsYes, we want to check this, and updated patch contains the check, but not there. But IMO doing it in rtld is too late and either check or the need for relocation bricks the system. I added the checks to the vdso build scripts. kib: Yes, we want to check this, and updated patch contains the check, but not there. But IMO doing… | |||||
* We assume that kernel-preloaded object does not need | |||||
* relocation. It is currently written into read-only page, | |||||
* handling relocations would mean we need to allocate at | |||||
* least one additional page per AS. | |||||
*/ | |||||
dbg("%s mapbase %p phdrs %p PT_LOAD phdr %p vaddr %p dynamic %p", | |||||
obj->path, obj->mapbase, obj->phdr, seg0, | |||||
obj->relocbase + seg0->p_vaddr, obj->dynamic); | |||||
TAILQ_INSERT_TAIL(&obj_list, obj, next); | |||||
obj_count++; | |||||
obj_loads++; | |||||
linkmap_add(obj); /* for GDB & dlinfo() */ | |||||
max_stack_flags |= obj->stack_flags; | |||||
LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, 0, 0, obj->path); | |||||
return (0); | |||||
} | |||||
Obj_Entry * | Obj_Entry * | ||||
obj_from_addr(const void *addr) | obj_from_addr(const void *addr) | ||||
{ | { | ||||
Obj_Entry *obj; | Obj_Entry *obj; | ||||
TAILQ_FOREACH(obj, &obj_list, next) { | TAILQ_FOREACH(obj, &obj_list, next) { | ||||
if (obj->marker) | if (obj->marker) | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 3,245 Lines • ▼ Show 20 Lines | static const struct auxfmt { | ||||
AUXFMT(AT_HWCAP2, "%#lx"), | AUXFMT(AT_HWCAP2, "%#lx"), | ||||
AUXFMT(AT_BSDFLAGS, "%#lx"), | AUXFMT(AT_BSDFLAGS, "%#lx"), | ||||
AUXFMT(AT_ARGC, "%lu"), | AUXFMT(AT_ARGC, "%lu"), | ||||
AUXFMT(AT_ARGV, "%p"), | AUXFMT(AT_ARGV, "%p"), | ||||
AUXFMT(AT_ENVC, "%p"), | AUXFMT(AT_ENVC, "%p"), | ||||
AUXFMT(AT_ENVV, "%p"), | AUXFMT(AT_ENVV, "%p"), | ||||
AUXFMT(AT_PS_STRINGS, "%p"), | AUXFMT(AT_PS_STRINGS, "%p"), | ||||
AUXFMT(AT_FXRNG, "%p"), | AUXFMT(AT_FXRNG, "%p"), | ||||
AUXFMT(AT_KPRELOAD, "%p"), | |||||
}; | }; | ||||
static bool | static bool | ||||
is_ptr_fmt(const char *fmt) | is_ptr_fmt(const char *fmt) | ||||
{ | { | ||||
char last; | char last; | ||||
last = fmt[strlen(fmt) - 1]; | last = fmt[strlen(fmt) - 1]; | ||||
▲ Show 20 Lines • Show All 105 Lines • Show Last 20 Lines |
We could default stack_flags to PF_R | PF_W for preload objs (and we could avoid PT_GNU_STACK in the VDSO if desired)
(i.e., there's no point in introducing a new X-by-default stack case that requires PT_GNU_STACK to turn it off. If in the future we do need a RWX stack specified by a preload obj for some reason we can use PT_GNU_STACK to set it)