diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 7c20398..0a6b60f 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -11,6 +11,7 @@ MAN= rtld.1 CSTD?= gnu99 CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD CFLAGS+= -I${.CURDIR}/${MACHINE_ARCH} -I${.CURDIR} +CFLAGS+= -DDEBUG -g LDFLAGS+= -nostdlib -e .rtld_start INSTALLFLAGS= -C -b PRECIOUSPROG= diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index f489edd..2d06074 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -348,6 +348,8 @@ obj_free(Obj_Entry *obj) free(obj->vertab); if (obj->origin_path) free(obj->origin_path); + if (obj->z_origin) + free(obj->rpath); if (obj->priv) free(obj->priv); if (obj->path) diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index f67093a..5161e6e 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -118,6 +119,7 @@ static void objlist_remove_unref(Objlist *); static void *path_enumerate(const char *, path_enum_proc, void *); static int relocate_objects(Obj_Entry *, bool, Obj_Entry *); static int rtld_dirname(const char *, char *); +static int rtld_dirname_abs(const char *, char *); static void rtld_exit(void); static char *search_library_path(const char *, const char *); static const void **get_program_var_addr(const char *); @@ -134,6 +136,9 @@ static void unlink_object(Obj_Entry *); static void unload_object(Obj_Entry *); static void unref_dag(Obj_Entry *); static void ref_dag(Obj_Entry *); +static int origin_subst_one(char **res, const char *real, const char *kw, + const char *subst, char *may_free); +static char *origin_subst(const char *real, const char *origin_path); static int rtld_verify_versions(const Objlist *); static int rtld_verify_object_versions(Obj_Entry *); static void object_add_name(Obj_Entry *, const char *); @@ -412,7 +417,25 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) die(); } - obj_main->path = xstrdup(argv0); + if (aux_info[AT_EXECPATH] != 0) { + char *kexecpath; + char buf[MAXPATHLEN]; + + kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr; + dbg("AT_EXECPATH %p %s", kexecpath, kexecpath); + if (kexecpath[0] == '/') + obj_main->path = kexecpath; + else if (getcwd(buf, sizeof(buf)) == NULL || + strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) || + strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf)) + obj_main->path = xstrdup(argv0); + else + obj_main->path = xstrdup(buf); + } else { + dbg("No AT_EXECPATH"); + obj_main->path = xstrdup(argv0); + } + dbg("obj_main path %s", obj_main->path); obj_main->mainprog = true; /* @@ -614,6 +637,83 @@ basename(const char *name) return p != NULL ? p + 1 : name; } +static struct utsname uts; + +static int +origin_subst_one(char **res, const char *real, const char *kw, const char *subst, + char *may_free) +{ + const char *p, *p1; + char *res1; + int subst_len; + int kw_len; + + res1 = *res = NULL; + p = real; + subst_len = kw_len = 0; + for (;;) { + p1 = strstr(p, kw); + if (p1 != NULL) { + if (subst_len == 0) { + subst_len = strlen(subst); + kw_len = strlen(kw); + } + if (*res == NULL) { + *res = xmalloc(PATH_MAX); + res1 = *res; + } + if ((res1 - *res) + subst_len + (p1 - p) >= PATH_MAX) { + _rtld_error("Substitution of %s in %s cannot be performed", + kw, real); + if (may_free != NULL) + free(may_free); + free(res); + return (false); + } + memcpy(res1, p, p1 - p); + res1 += p1 - p; + memcpy(res1, subst, subst_len); + res1 += subst_len; + p = p1 + kw_len; + } else { + if (*res == NULL) { + if (may_free != NULL) + *res = may_free; + else + *res = xstrdup(real); + return (true); + } + *res1 = '\0'; + if (may_free != NULL) + free(may_free); + if (strlcat(res1, p, PATH_MAX - (res1 - *res)) >= PATH_MAX) { + free(res); + return (false); + } + return (true); + } + } +} + +static char * +origin_subst(const char *real, const char *origin_path) +{ + char *res1, *res2, *res3, *res4; + + if (uts.sysname[0] == '\0') { + if (uname(&uts) != 0) { + _rtld_error("utsname failed: %d", errno); + return (NULL); + } + } + if (!origin_subst_one(&res1, real, "$ORIGIN", origin_path, NULL) || + !origin_subst_one(&res2, res1, "$OSNAME", uts.sysname, res1) || + !origin_subst_one(&res3, res2, "$OSREL", uts.release, res2) || + !origin_subst_one(&res4, res3, "$PLATFORM", uts.machine, res3)) + return (NULL); + return (res4); +} + static void die(void) { @@ -790,11 +890,8 @@ digest_dynamic(Obj_Entry *obj, int early) #endif case DT_FLAGS: - if (dynp->d_un.d_val & DF_ORIGIN) { - obj->origin_path = xmalloc(PATH_MAX); - if (rtld_dirname(obj->path, obj->origin_path) == -1) - die(); - } + if ((dynp->d_un.d_val & DF_1_ORIGIN) && trust) + obj->z_origin = true; if (dynp->d_un.d_val & DF_SYMBOLIC) obj->symbolic = true; if (dynp->d_un.d_val & DF_TEXTREL) @@ -826,6 +923,15 @@ digest_dynamic(Obj_Entry *obj, int early) break; #endif + case DT_FLAGS_1: + if ((dynp->d_un.d_val & DF_1_ORIGIN) && trust) + obj->z_origin = true; + if (dynp->d_un.d_val & DF_1_GLOBAL) + /* XXX */; + if (dynp->d_un.d_val & DF_1_BIND_NOW) + obj->bind_now = true; + break; + default: if (!early) { dbg("Ignoring d_tag %ld = %#lx", (long)dynp->d_tag, @@ -844,8 +950,20 @@ digest_dynamic(Obj_Entry *obj, int early) obj->pltrelsize = 0; } - if (dyn_rpath != NULL) - obj->rpath = obj->strtab + dyn_rpath->d_un.d_val; +#if 0 + obj->z_origin = true; +#endif + if (obj->z_origin && obj->origin_path == NULL) { + obj->origin_path = xmalloc(PATH_MAX); + if (rtld_dirname_abs(obj->path, obj->origin_path) == -1) + die(); + } + + if (dyn_rpath != NULL) { + obj->rpath = (char *)obj->strtab + dyn_rpath->d_un.d_val; + if (obj->z_origin) + obj->rpath = origin_subst(obj->rpath, obj->origin_path); + } if (dyn_soname != NULL) object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val); @@ -1003,7 +1121,10 @@ find_library(const char *xname, const Obj_Entry *refobj) xname); return NULL; } - return xstrdup(xname); + if (refobj->z_origin) + return origin_subst(xname, refobj->origin_path); + else + return xstrdup(xname); } if (libmap_disable || (refobj == NULL) || @@ -2309,6 +2430,23 @@ rtld_dirname(const char *path, char *bname) return (0); } +static int +rtld_dirname_abs(const char *path, char *base) +{ + char base_rel[PATH_MAX]; + + if (rtld_dirname(path, base) == -1) + return (-1); + if (base[0] == '/') + return (0); + if (getcwd(base_rel, sizeof(base_rel)) == NULL || + strlcat(base_rel, "/", sizeof(base_rel)) >= sizeof(base_rel) || + strlcat(base_rel, base, sizeof(base_rel)) >= sizeof(base_rel)) + return (-1); + strcpy(base, base_rel); + return (0); +} + static void linkmap_add(Obj_Entry *obj) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index c76bc76..f47c215 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -195,7 +195,7 @@ typedef struct Struct_Obj_Entry { const Elf_Hashelt *chains; /* Hash table chain array */ unsigned long nchains; /* Number of chains */ - const char *rpath; /* Search path specified in object */ + char *rpath; /* Search path specified in object */ Needed_Entry *needed; /* Shared objects needed by this one (%) */ STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we @@ -216,6 +216,7 @@ typedef struct Struct_Obj_Entry { 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. */ + bool z_origin : 1; /* Process rpath and soname tokens */ struct link_map linkmap; /* for GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ diff --git a/sys/amd64/include/elf.h b/sys/amd64/include/elf.h index a4c7f79..e5c95f7 100644 --- a/sys/amd64/include/elf.h +++ b/sys/amd64/include/elf.h @@ -81,16 +81,14 @@ __ElfType(Auxinfo); #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused for i386). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ -/* - * The following non-standard values are used in Linux ELF binaries. - */ #define AT_NOTELF 10 /* Program is not ELF ?? */ #define AT_UID 11 /* Real uid. */ #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ /* * Relocation types. diff --git a/sys/arm/include/elf.h b/sys/arm/include/elf.h index c516864..ee2843f 100644 --- a/sys/arm/include/elf.h +++ b/sys/arm/include/elf.h @@ -75,8 +75,9 @@ __ElfType(Auxinfo); #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ #define R_ARM_COUNT 33 /* Count of defined relocation types. */ diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c index e5d8828..2b7ecba 100644 --- a/sys/compat/ia32/ia32_sysvec.c +++ b/sys/compat/ia32/ia32_sysvec.c @@ -189,15 +189,21 @@ ia32_copyout_strings(struct image_params *imgp) char *stringp, *destp; u_int32_t *stack_base; struct freebsd32_ps_strings *arginfo; + size_t execpath_len; int szsigcode; /* * Calculate string base and vector table pointers. * Also deal with signal trampoline code for this exec type. */ + if (imgp->execpath != NULL && imgp->auxargs != NULL) + execpath_len = strlen(imgp->execpath) + 1; + else + execpath_len = 0; arginfo = (struct freebsd32_ps_strings *)FREEBSD32_PS_STRINGS; szsigcode = *(imgp->proc->p_sysent->sv_szsigcode); destp = (caddr_t)arginfo - szsigcode - SPARE_USRSPACE - + roundup(execpath_len, sizeof(char *)) - roundup((ARG_MAX - imgp->args->stringspace), sizeof(char *)); /* @@ -208,6 +214,15 @@ ia32_copyout_strings(struct image_params *imgp) ((caddr_t)arginfo - szsigcode), szsigcode); /* + * Copy the image path for the rtld. + */ + if (execpath_len != 0) { + imgp->execpathp = (uintptr_t)arginfo - szsigcode - execpath_len; + copyout(imgp->execpath, (void *)imgp->execpathp, + execpath_len); + } + + /* * If we have a valid auxargs ptr, prepare some room * on the stack. */ @@ -223,9 +238,9 @@ ia32_copyout_strings(struct image_params *imgp) * the arg and env vector sets,and imgp->auxarg_size is room * for argument of Runtime loader. */ - vectp = (u_int32_t *) (destp - (imgp->args->argc + imgp->args->envc + 2 + - imgp->auxarg_size) * sizeof(u_int32_t)); - + vectp = (u_int32_t *) (destp - (imgp->args->argc + + imgp->args->envc + 2 + imgp->auxarg_size + execpath_len) * + sizeof(u_int32_t)); } else /* * The '+ 2' is for the null pointers at the end of each of diff --git a/sys/i386/include/elf.h b/sys/i386/include/elf.h index 1470173..af71ab8 100644 --- a/sys/i386/include/elf.h +++ b/sys/i386/include/elf.h @@ -84,16 +84,14 @@ __ElfType(Auxinfo); #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused for i386). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ -/* - * The following non-standard values are used in Linux ELF binaries. - */ #define AT_NOTELF 10 /* Program is not ELF ?? */ #define AT_UID 11 /* Real uid. */ #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ /* * Relocation types. diff --git a/sys/ia64/include/elf.h b/sys/ia64/include/elf.h index faab8d1..65802aa 100644 --- a/sys/ia64/include/elf.h +++ b/sys/ia64/include/elf.h @@ -82,16 +82,14 @@ __ElfType(Auxinfo); #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused for i386). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ -/* - * The following non-standard values are used in Linux ELF binaries. - */ #define AT_NOTELF 10 /* Program is not ELF ?? */ #define AT_UID 11 /* Real uid. */ #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ /* * Values for e_flags. diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index f2bdcf5..3b52e0a 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -885,6 +885,8 @@ __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); AUXARGS_ENTRY(pos, AT_ENTRY, args->entry); AUXARGS_ENTRY(pos, AT_BASE, args->base); + if (imgp->execpathp != 0) + AUXARGS_ENTRY(pos, AT_EXECPATH, imgp->execpathp); AUXARGS_ENTRY(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index af6c9e4..6f04790 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -379,6 +379,8 @@ do_execve(td, args, mac_p) imgp->ps_strings = 0; imgp->auxarg_size = 0; imgp->args = args; + imgp->execpath = imgp->freepath = NULL; + imgp->execpathp = 0; #ifdef MAC error = mac_execve_enter(imgp, mac_p); @@ -519,6 +521,14 @@ interpret: * of the sv_copyout_strings/sv_fixup operations require the vnode. */ VOP_UNLOCK(imgp->vp, 0); + + /* + * Do the best to calculate the full path to the image file. + */ + if (imgp->auxargs && (args->fname[0] == '/' || + vn_fullpath(td, imgp->vp, &imgp->execpath, &imgp->freepath) != 0)) + imgp->execpath = args->fname; + /* * Copy out strings (args and env) and initialize stack base */ @@ -859,6 +869,8 @@ exec_fail_dealloc: if (imgp->object != NULL) vm_object_deallocate(imgp->object); + free(imgp->freepath, M_TEMP); + if (error == 0) { /* * Stop the process here if its stop event mask has @@ -1164,18 +1176,24 @@ exec_copyout_strings(imgp) register_t *stack_base; struct ps_strings *arginfo; struct proc *p; + size_t execpath_len; int szsigcode; /* * Calculate string base and vector table pointers. * Also deal with signal trampoline code for this exec type. */ + if (imgp->execpath != NULL && imgp->auxargs != NULL) + execpath_len = strlen(imgp->execpath) + 1; + else + execpath_len = 0; p = imgp->proc; szsigcode = 0; arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; if (p->p_sysent->sv_szsigcode != NULL) szsigcode = *(p->p_sysent->sv_szsigcode); destp = (caddr_t)arginfo - szsigcode - SPARE_USRSPACE - + roundup(execpath_len, sizeof(char *)) - roundup((ARG_MAX - imgp->args->stringspace), sizeof(char *)); /* @@ -1186,6 +1204,15 @@ exec_copyout_strings(imgp) szsigcode), szsigcode); /* + * Copy the image path for the rtld. + */ + if (execpath_len != 0) { + imgp->execpathp = (uintptr_t)arginfo - szsigcode - execpath_len; + copyout(imgp->execpath, (void *)imgp->execpathp, + execpath_len); + } + + /* * If we have a valid auxargs ptr, prepare some room * on the stack. */ @@ -1202,9 +1229,8 @@ exec_copyout_strings(imgp) * for argument of Runtime loader. */ vectp = (char **)(destp - (imgp->args->argc + - imgp->args->envc + 2 + imgp->auxarg_size) * + imgp->args->envc + 2 + imgp->auxarg_size + execpath_len) * sizeof(char *)); - } else { /* * The '+ 2' is for the null pointers at the end of each of diff --git a/sys/mips/include/elf.h b/sys/mips/include/elf.h index 6e48ec1..3a31daa 100644 --- a/sys/mips/include/elf.h +++ b/sys/mips/include/elf.h @@ -241,15 +241,13 @@ __ElfType(Auxinfo); #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused for i386). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ -/* - * The following non-standard values are used in Linux ELF binaries. - */ #define AT_NOTELF 10 /* Program is not ELF ?? */ #define AT_UID 11 /* Real uid. */ #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ #endif /* !_MACHINE_ELF_H_ */ diff --git a/sys/powerpc/include/elf.h b/sys/powerpc/include/elf.h index 422a86a..1224fbb 100644 --- a/sys/powerpc/include/elf.h +++ b/sys/powerpc/include/elf.h @@ -77,8 +77,9 @@ __ElfType(Auxinfo); #define AT_DCACHEBSIZE 10 /* Data cache block size for the processor. */ #define AT_ICACHEBSIZE 11 /* Instruction cache block size for the uP. */ #define AT_UCACHEBSIZE 12 /* Cache block size, or `0' if cache not unified. */ +#define AT_EXECPATH 13 /* Path to the executable. */ -#define AT_COUNT 13 /* Count of defined aux entry types. */ +#define AT_COUNT 14 /* Count of defined aux entry types. */ /* * Relocation types. diff --git a/sys/sparc64/include/elf.h b/sys/sparc64/include/elf.h index 108ade1..c0fcbee 100644 --- a/sys/sparc64/include/elf.h +++ b/sys/sparc64/include/elf.h @@ -78,16 +78,14 @@ __ElfType(Auxinfo); #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ -/* - * The following non-standard values are used in Linux ELF binaries. - */ #define AT_NOTELF 10 /* Program is not ELF ?? */ #define AT_UID 11 /* Real uid. */ #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ /* Define "machine" characteristics */ #if __ELF_WORD_SIZE == 32 diff --git a/sys/sun4v/include/elf.h b/sys/sun4v/include/elf.h index 108ade1..c0fcbee 100644 --- a/sys/sun4v/include/elf.h +++ b/sys/sun4v/include/elf.h @@ -78,16 +78,14 @@ __ElfType(Auxinfo); #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ -/* - * The following non-standard values are used in Linux ELF binaries. - */ #define AT_NOTELF 10 /* Program is not ELF ?? */ #define AT_UID 11 /* Real uid. */ #define AT_EUID 12 /* Effective uid. */ #define AT_GID 13 /* Real gid. */ #define AT_EGID 14 /* Effective gid. */ +#define AT_EXECPATH 15 /* Path to the executable. */ -#define AT_COUNT 15 /* Count of defined aux entry types. */ +#define AT_COUNT 16 /* Count of defined aux entry types. */ /* Define "machine" characteristics */ #if __ELF_WORD_SIZE == 32 diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index 34c0b3a..bf64a3b 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -466,6 +466,11 @@ typedef struct { executable contains code using a static thread-local storage scheme. */ +/* Values for DT_FLAGS_1 */ +#define DF_1_BIND_NOW 0x00000001 /* Same as DF_BIND_NOW */ +#define DF_1_GLOBAL 0x00000002 /* Set the RTLD_GLOBAL for object */ +#define DF_1_ORIGIN 0x00000080 /* Process $ORIGIN */ + /* Values for n_type. Used in core files. */ #define NT_PRSTATUS 1 /* Process status. */ #define NT_FPREGSET 2 /* Floating point registers. */ diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h index 011a7ae..e6acc00 100644 --- a/sys/sys/imgact.h +++ b/sys/sys/imgact.h @@ -66,6 +66,9 @@ struct image_params { size_t auxarg_size; struct image_args *args; /* system call arguments */ struct sysentvec *sysent; /* system entry vector */ + char *execpath; + unsigned long execpathp; + char *freepath; }; #ifdef _KERNEL