Changeset View
Standalone View
libexec/rtld-elf/rtld.c
Show First 20 Lines • Show All 333 Lines • ▼ Show 20 Lines | |||||
* exit procedure pointer and the third to a place to store the main | * exit procedure pointer and the third to a place to store the main | ||||
* program's object. | * program's object. | ||||
* | * | ||||
* The return value is the main program's entry point. | * The return value is the main program's entry point. | ||||
*/ | */ | ||||
func_ptr_type | func_ptr_type | ||||
_rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) | _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) | ||||
{ | { | ||||
Elf_Auxinfo *aux, *auxp, *aux_info[AT_COUNT]; | Elf_Auxinfo *aux, *auxp, *aux_info[AT_COUNT]; | ||||
Objlist_Entry *entry; | Objlist_Entry *entry; | ||||
Obj_Entry *last_interposer, *obj, *preload_tail; | Obj_Entry *last_interposer, *obj, *preload_tail; | ||||
const Elf_Phdr *phdr; | const Elf_Phdr *phdr; | ||||
Objlist initlist; | Objlist initlist; | ||||
RtldLockState lockstate; | RtldLockState lockstate; | ||||
char **argv, *argv0, **env, *kexecpath, *library_path_rpath; | Elf_Addr *argcp; | ||||
char **argv, *argv0, **env, **envp, *kexecpath, *library_path_rpath; | |||||
caddr_t imgentry; | caddr_t imgentry; | ||||
char buf[MAXPATHLEN]; | char buf[MAXPATHLEN]; | ||||
int argc, fd, i, mib[2], phnum; | int argc, fd, i, mib[2], phnum; | ||||
size_t len; | size_t len; | ||||
emaste: Perhaps commit the re-sorting independently first? | |||||
Not Done Inline ActionsThis is not just resorting, I moved locals from the nested blocks and added some variables as well. I can produce separate patch to reorder existing local section, but the bits in the patch are not extractable. kib: This is not just resorting, I moved locals from the nested blocks and added some variables as… | |||||
Not Done Inline ActionsRight, I mean there is both resorting and other changes and I think the resorting part could be committed by itself - it would make it more clear what the other changes are. emaste: Right, I mean there is both resorting and other changes and I think the resorting part could be… | |||||
/* | /* | ||||
* On entry, the dynamic linker itself has not been relocated yet. | * On entry, the dynamic linker itself has not been relocated yet. | ||||
* Be very careful not to reference any global data until after | * Be very careful not to reference any global data until after | ||||
* init_rtld has returned. It is OK to reference file-scope statics | * init_rtld has returned. It is OK to reference file-scope statics | ||||
* and string constants, and to call static and global functions. | * and string constants, and to call static and global functions. | ||||
*/ | */ | ||||
/* Find the auxiliary vector on the stack. */ | /* Find the auxiliary vector on the stack. */ | ||||
argcp = sp; | |||||
argc = *sp++; | argc = *sp++; | ||||
argv = (char **) sp; | argv = (char **) sp; | ||||
sp += argc + 1; /* Skip over arguments and NULL terminator */ | sp += argc + 1; /* Skip over arguments and NULL terminator */ | ||||
env = (char **) sp; | env = (char **) sp; | ||||
while (*sp++ != 0) /* Skip over environment, and NULL terminator */ | while (*sp++ != 0) /* Skip over environment, and NULL terminator */ | ||||
; | ; | ||||
aux = (Elf_Auxinfo *) sp; | aux = (Elf_Auxinfo *) sp; | ||||
/* Digest the auxiliary vector. */ | /* Digest the auxiliary vector. */ | ||||
for (i = 0; i < AT_COUNT; i++) | for (i = 0; i < AT_COUNT; i++) | ||||
aux_info[i] = NULL; | aux_info[i] = NULL; | ||||
for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { | for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { | ||||
if (auxp->a_type < AT_COUNT) | if (auxp->a_type < AT_COUNT) | ||||
aux_info[auxp->a_type] = auxp; | aux_info[auxp->a_type] = auxp; | ||||
} | } | ||||
/* Initialize and relocate ourselves. */ | /* Initialize and relocate ourselves. */ | ||||
assert(aux_info[AT_BASE] != NULL); | assert(aux_info[AT_BASE] != NULL); | ||||
emasteUnsubmitted Not Done Inline ActionsWith the patch this assert triggers via % /libexec/ld-elf.so.1 /libexec/ld-elf.so.1 ld-elf.so.1: assert failed: /usr/home/emaste/src/freebsd/libexec/rtld-elf/rtld.c:381 emaste: With the patch this assert triggers via
```
% /libexec/ld-elf.so.1 /libexec/ld-elf.so.1
ld-elf. | |||||
init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info); | init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info); | ||||
__progname = obj_rtld.path; | __progname = obj_rtld.path; | ||||
argv0 = argv[0] != NULL ? argv[0] : "(null)"; | argv0 = argv[0] != NULL ? argv[0] : "(null)"; | ||||
environ = env; | environ = env; | ||||
main_argc = argc; | main_argc = argc; | ||||
main_argv = argv; | main_argv = argv; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (unsetenv(_LD("PRELOAD")) || unsetenv(_LD("LIBMAP")) || | ||||
libmap_disable = getenv(_LD("LIBMAP_DISABLE")) != NULL; | libmap_disable = getenv(_LD("LIBMAP_DISABLE")) != NULL; | ||||
libmap_override = getenv(_LD("LIBMAP")); | libmap_override = getenv(_LD("LIBMAP")); | ||||
ld_library_path = getenv(_LD("LIBRARY_PATH")); | ld_library_path = getenv(_LD("LIBRARY_PATH")); | ||||
ld_library_dirs = getenv(_LD("LIBRARY_PATH_FDS")); | ld_library_dirs = getenv(_LD("LIBRARY_PATH_FDS")); | ||||
ld_preload = getenv(_LD("PRELOAD")); | ld_preload = getenv(_LD("PRELOAD")); | ||||
ld_elf_hints_path = getenv(_LD("ELF_HINTS_PATH")); | ld_elf_hints_path = getenv(_LD("ELF_HINTS_PATH")); | ||||
ld_loadfltr = getenv(_LD("LOADFLTR")) != NULL; | ld_loadfltr = getenv(_LD("LOADFLTR")) != NULL; | ||||
library_path_rpath = getenv(_LD("LIBRARY_PATH_RPATH")); | library_path_rpath = getenv(_LD("LIBRARY_PATH_RPATH")); | ||||
if (library_path_rpath != NULL) { | if (library_path_rpath != NULL) { | ||||
Done Inline Actions!= NULL? also envp below emaste: `!= NULL`?
also envp below | |||||
if (library_path_rpath[0] == 'y' || | if (library_path_rpath[0] == 'y' || | ||||
library_path_rpath[0] == 'Y' || | library_path_rpath[0] == 'Y' || | ||||
library_path_rpath[0] == '1') | library_path_rpath[0] == '1') | ||||
ld_library_path_rpath = true; | ld_library_path_rpath = true; | ||||
else | else | ||||
ld_library_path_rpath = false; | ld_library_path_rpath = false; | ||||
} | } | ||||
dangerous_ld_env = libmap_disable || (libmap_override != NULL) || | dangerous_ld_env = libmap_disable || (libmap_override != NULL) || | ||||
Show All 10 Lines | debug = 1; | ||||
dbg("%s is initialized, base address = %p", __progname, | dbg("%s is initialized, base address = %p", __progname, | ||||
(caddr_t) aux_info[AT_BASE]->a_un.a_ptr); | (caddr_t) aux_info[AT_BASE]->a_un.a_ptr); | ||||
dbg("RTLD dynamic = %p", obj_rtld.dynamic); | dbg("RTLD dynamic = %p", obj_rtld.dynamic); | ||||
dbg("RTLD pltgot = %p", obj_rtld.pltgot); | dbg("RTLD pltgot = %p", obj_rtld.pltgot); | ||||
dbg("initializing thread locks"); | dbg("initializing thread locks"); | ||||
lockdflt_init(); | lockdflt_init(); | ||||
fd = -1; | |||||
/* | /* | ||||
* Load the main program, or process its program header if it is | * Load the main program, or process its program header if it is | ||||
* already loaded. | * already loaded. | ||||
*/ | */ | ||||
if (aux_info[AT_EXECFD] != NULL) { /* Load the main program. */ | if (aux_info[AT_EXECFD] != NULL) { | ||||
fd = aux_info[AT_EXECFD]->a_un.a_val; | fd = aux_info[AT_EXECFD]->a_un.a_val; | ||||
} else { | |||||
assert(aux_info[AT_PHDR] != NULL); | |||||
phdr = (const Elf_Phdr *)aux_info[AT_PHDR]->a_un.a_ptr; | |||||
Not Done Inline Actionsextra space before aux_info? emaste: extra space before `aux_info`? | |||||
if (phdr == obj_rtld.phdr) { | |||||
dbg("opening main program in direct exec mode"); | |||||
if (argc >= 2) { | |||||
argv0 = argv[1]; | |||||
fd = open(argv0, O_RDONLY | O_CLOEXEC | O_VERIFY); | |||||
if (fd == -1) { | |||||
Not Done Inline ActionsAs an aside it seems O_VERIFY is not documented in open.2. I see that this came from https://github.com/freebsd/freebsd/pull/27 - paging @stevek Also, what happens if we try to ld-elf.so.1 foo where foo has ---x--x--x perms? emaste: As an aside it seems `O_VERIFY` is not documented in `open.2`. I see that this came from https… | |||||
Not Done Inline ActionsIt is something internal from J that got committed but never get the flesh in our repository. kib: It is something internal from J that got committed but never get the flesh in our repository. | |||||
Not Done Inline ActionsSee D2902, which has the changes. I guess after all this time people have had enough time to comment. I'll see about committing that set of changes. stevek: See D2902, which has the changes. I guess after all this time people have had enough time to… | |||||
Not Done Inline ActionsAh. I added a comment in there - I'd suggest we should go ahead with the open.2 changes right now, since the corresponding source changes were made some time ago, and follow up with a D2902 rebase. emaste: Ah. I added a comment in there - I'd suggest we should go ahead with the open.2 changes right… | |||||
Not Done Inline ActionsSure, makes sense. I committed just the open.2 change and will rebase D2902. stevek: Sure, makes sense. I committed just the open.2 change and will rebase D2902. | |||||
rtld_printf("Opening %s: %s\n", argv0, | |||||
rtld_strerror(errno)); | |||||
rtld_die(); | |||||
} | |||||
/* | |||||
* For direct exec mode, argv[0] is the interpreter | |||||
* name, we must remove it and shift arguments left by | |||||
Not Done Inline ActionsI think this comment could be expanded a bit to separately explain main_arg[cv] and the argv/envp/auxp manipulation that follows it. emaste: I think this comment could be expanded a bit to separately explain main_arg[cv] and the… | |||||
* 1 before invoking binary main. Since stack layout | |||||
* places environment pointers and aux vectors right | |||||
* after the terminating NULL, we must shift | |||||
* environment and aux as well. | |||||
* XXX Shift will be > 1 when options are implemented. | |||||
*/ | |||||
do { | |||||
*argv = *(argv + 1); | |||||
argv++; | |||||
} while (*argv != 0); | |||||
*argcp -= 1; | |||||
main_argc = argc - 1; | |||||
envp = argv; | |||||
do { | |||||
*envp = *(envp + 1); | |||||
envp++; | |||||
} while (*envp != 0); | |||||
auxp = (Elf_Auxinfo *)(envp + 1); | |||||
do { | |||||
*auxp = *(auxp + 1); | |||||
auxp++; | |||||
} while (auxp->a_type != AT_NULL); | |||||
} else { | |||||
rtld_printf("no binary\n"); | |||||
rtld_die(); | |||||
} | |||||
} | |||||
} | |||||
if (fd != -1) { /* Load the main program. */ | |||||
dbg("loading main program"); | dbg("loading main program"); | ||||
obj_main = map_object(fd, argv0, NULL); | obj_main = map_object(fd, argv0, NULL); | ||||
close(fd); | close(fd); | ||||
if (obj_main == NULL) | if (obj_main == NULL) | ||||
rtld_die(); | rtld_die(); | ||||
max_stack_flags = obj->stack_flags; | max_stack_flags = obj->stack_flags; | ||||
} else { /* Main program already loaded. */ | } else { /* Main program already loaded. */ | ||||
dbg("processing main program's program header"); | dbg("processing main program's program header"); | ||||
assert(aux_info[AT_PHDR] != NULL); | assert(aux_info[AT_PHDR] != NULL); | ||||
phdr = (const Elf_Phdr *) aux_info[AT_PHDR]->a_un.a_ptr; | phdr = (const Elf_Phdr *) aux_info[AT_PHDR]->a_un.a_ptr; | ||||
assert(aux_info[AT_PHNUM] != NULL); | assert(aux_info[AT_PHNUM] != NULL); | ||||
phnum = aux_info[AT_PHNUM]->a_un.a_val; | phnum = aux_info[AT_PHNUM]->a_un.a_val; | ||||
assert(aux_info[AT_PHENT] != NULL); | assert(aux_info[AT_PHENT] != NULL); | ||||
assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); | assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); | ||||
assert(aux_info[AT_ENTRY] != NULL); | assert(aux_info[AT_ENTRY] != NULL); | ||||
imgentry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr; | imgentry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr; | ||||
if ((obj_main = digest_phdr(phdr, phnum, imgentry, argv0)) == NULL) | if ((obj_main = digest_phdr(phdr, phnum, imgentry, argv0)) == NULL) | ||||
rtld_die(); | rtld_die(); | ||||
} | } | ||||
if (aux_info[AT_EXECPATH] != NULL) { | if (aux_info[AT_EXECPATH] != NULL && fd == -1) { | ||||
kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr; | kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr; | ||||
dbg("AT_EXECPATH %p %s", kexecpath, kexecpath); | dbg("AT_EXECPATH %p %s", kexecpath, kexecpath); | ||||
if (kexecpath[0] == '/') | if (kexecpath[0] == '/') | ||||
obj_main->path = kexecpath; | obj_main->path = kexecpath; | ||||
else if (getcwd(buf, sizeof(buf)) == NULL || | else if (getcwd(buf, sizeof(buf)) == NULL || | ||||
strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) || | strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) || | ||||
strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf)) | strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf)) | ||||
obj_main->path = xstrdup(argv0); | obj_main->path = xstrdup(argv0); | ||||
else | else | ||||
obj_main->path = xstrdup(buf); | obj_main->path = xstrdup(buf); | ||||
} else { | } else { | ||||
dbg("No AT_EXECPATH"); | dbg("No AT_EXECPATH or direct exec"); | ||||
obj_main->path = xstrdup(argv0); | obj_main->path = xstrdup(argv0); | ||||
} | } | ||||
dbg("obj_main path %s", obj_main->path); | dbg("obj_main path %s", obj_main->path); | ||||
obj_main->mainprog = true; | obj_main->mainprog = true; | ||||
if (aux_info[AT_STACKPROT] != NULL && | if (aux_info[AT_STACKPROT] != NULL && | ||||
aux_info[AT_STACKPROT]->a_un.a_val != 0) | aux_info[AT_STACKPROT]->a_un.a_val != 0) | ||||
stack_prot = aux_info[AT_STACKPROT]->a_un.a_val; | stack_prot = aux_info[AT_STACKPROT]->a_un.a_val; | ||||
▲ Show 20 Lines • Show All 4,764 Lines • Show Last 20 Lines |
Perhaps commit the re-sorting independently first?