Rt_map * setup(char **envp, auxv_t *auxv, Word _flags, char *_platform, int _syspagsz, char *_rtldname, ulong_t ld_base, ulong_t interp_base, int fd, Phdr *phdr, char *execname, char **argv, uid_t uid, uid_t euid, gid_t gid, gid_t egid, void *aoutdyn, int auxflags, uint_t *hwcap) { Rt_map *rlmp, *mlmp, *clmp, **tobj = NULL; Ehdr *ehdr; rtld_stat_t status; int features = 0, ldsoexec = 0; size_t eaddr, esize; char *str, *argvname; Word lmflags; mmapobj_result_t *mpp; Fdesc fdr = { 0 }, fdm = { 0 }; Rej_desc rej = { 0 }; APlist *ealp = NULL; /* * Now that ld.so has relocated itself, initialize our own 'environ' so * as to establish an address suitable for any libc requirements. */ _environ = (char **)((ulong_t)auxv - sizeof (char *)); _init(); _environ = envp; /* * Establish a base time. Total time diagnostics start from entering * ld.so.1 here, however the base time is reset each time the ld.so.1 * is re-entered. Note also, there will be a large time associated * with the first diagnostic from ld.so.1, as bootstrapping ld.so.1 * and establishing the liblddbg infrastructure takes some time. */ (void) gettimeofday(&DBG_TOTALTIME, NULL); DBG_DELTATIME = DBG_TOTALTIME; /* * Determine how ld.so.1 has been executed. */ if ((fd == -1) && (phdr == NULL)) { /* * If we received neither the AT_EXECFD nor the AT_PHDR aux * vector, ld.so.1 must have been invoked directly from the * command line. */ ldsoexec = 1; /* * AT_SUN_EXECNAME provides the most precise name, if it is * available, otherwise fall back to argv[0]. At this time, * there is no process name. */ if (execname) rtldname = execname; else if (argv[0]) rtldname = argv[0]; else rtldname = (char *)MSG_INTL(MSG_STR_UNKNOWN); } else { /* * Otherwise, we have a standard process. AT_SUN_EXECNAME * provides the most precise name, if it is available, * otherwise fall back to argv[0]. Provided the application * is already mapped, the process is the application, so * simplify the application name for use in any diagnostics. */ if (execname) argvname = execname; else if (argv[0]) argvname = execname = argv[0]; else argvname = execname = (char *)MSG_INTL(MSG_STR_UNKNOWN); if (fd == -1) { if ((str = strrchr(argvname, '/')) != NULL) procname = ++str; else procname = argvname; } /* * At this point, we don't know the runtime linkers full path * name. The _rtldname passed to us is the SONAME of the * runtime linker, which is typically /lib/ld.so.1 no matter * what the full path is. Use this for now, we'll reset the * runtime linkers name once the application is analyzed. */ if (_rtldname) { if ((str = strrchr(_rtldname, '/')) != NULL) rtldname = ++str; else rtldname = _rtldname; } else rtldname = (char *)MSG_INTL(MSG_STR_UNKNOWN); /* exec() brought in two objects for us. Count the second one */ cnt_map++; } /* * Initialize any global variables. */ at_flags = _flags; if ((org_scapset->sc_plat = _platform) != NULL) org_scapset->sc_platsz = strlen(_platform); if (org_scapset->sc_plat == NULL) platform_name(org_scapset); if (org_scapset->sc_mach == NULL) machine_name(org_scapset); /* * If pagesize is unspecified find its value. */ if ((syspagsz = _syspagsz) == 0) syspagsz = _sysconfig(_CONFIG_PAGESIZE); /* * Add the unused portion of the last data page to the free space list. * The page size must be set before doing this. Here, _end refers to * the end of the runtime linkers bss. Note that we do not use the * unused data pages from any included .so's to supplement this free * space as badly behaved .os's may corrupt this data space, and in so * doing ruin our data. */ eaddr = S_DROUND((size_t)&_end); esize = eaddr % syspagsz; if (esize) { esize = syspagsz - esize; addfree((void *)eaddr, esize); } /* * Establish initial link-map list flags, and link-map list alists. */ if (alist_append(&lml_main.lm_lists, NULL, sizeof (Lm_cntl), AL_CNT_LMLISTS) == NULL) return (0); lml_main.lm_flags |= LML_FLG_BASELM; lml_main.lm_lmid = LM_ID_BASE; lml_main.lm_lmidstr = (char *)MSG_ORIG(MSG_LMID_BASE); if (alist_append(&lml_rtld.lm_lists, NULL, sizeof (Lm_cntl), AL_CNT_LMLISTS) == NULL) return (0); lml_rtld.lm_flags |= (LML_FLG_RTLDLM | LML_FLG_HOLDLOCK); lml_rtld.lm_tflags |= LML_TFLG_NOAUDIT; lml_rtld.lm_lmid = LM_ID_LDSO; lml_rtld.lm_lmidstr = (char *)MSG_ORIG(MSG_LMID_LDSO); /* * Determine whether we have a secure executable. */ security(uid, euid, gid, egid, auxflags); /* * Make an initial pass of environment variables to pick off those * related to locale processing. At the same time, collect and save * any LD_XXXX variables for later processing. Note that this later * processing will be skipped if ld.so.1 is invoked from the command * line with -e LD_NOENVIRON. */ if (envp && (readenv_user((const char **)envp, &ealp) == 1)) return (0); /* * If ld.so.1 has been invoked directly, process its arguments. */ if (ldsoexec) { /* * Process any arguments that are specific to ld.so.1, and * reorganize the process stack to effectively remove ld.so.1 * from the stack. Reinitialize the environment pointer, as * this pointer may have been shifted after skipping ld.so.1's * arguments. */ if (rtld_getopt(argv, &envp, &auxv, &(lml_main.lm_flags), &(lml_main.lm_tflags), (aoutdyn != 0)) == 1) { eprintf(&lml_main, ERR_NONE, MSG_INTL(MSG_USG_BADOPT)); return (0); } _environ = envp; /* * Open the object that ld.so.1 is to execute. */ argvname = execname = argv[0]; if ((fd = open(argvname, O_RDONLY)) == -1) { int err = errno; eprintf(&lml_main, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), argvname, strerror(err)); return (0); } } /* * Having processed any ld.so.1 command line options, return to process * any LD_XXXX environment variables. */ if (ealp) { if (((rtld_flags & RT_FL_NOENVIRON) == 0) && (procenv_user(ealp, &(lml_main.lm_flags), &(lml_main.lm_tflags), (aoutdyn != 0)) == 1)) return (0); free(ealp); } /* * Initialize a hardware capability descriptor for use in comparing * each loaded object. The aux vector must provide AF_SUN_HWCAPVERIFY, * as prior to this setting any hardware capabilities that were found * could not be relied upon. */ if (auxflags & AF_SUN_HWCAPVERIFY) { rtld_flags2 |= RT_FL2_HWCAP; org_scapset->sc_hw_1 = (Xword)hwcap[0]; org_scapset->sc_hw_2 = (Xword)hwcap[1]; } /* * Create a mapping descriptor for ld.so.1. We can determine our * two segments information from known symbols. */ if ((mpp = calloc(2, sizeof (mmapobj_result_t))) == NULL) return (0); mpp[0].mr_addr = (caddr_t)M_PTRUNC(ld_base); mpp[0].mr_msize = (caddr_t)&_etext - mpp[0].mr_addr; mpp[0].mr_fsize = mpp[0].mr_msize; mpp[0].mr_prot = (PROT_READ | PROT_EXEC); mpp[1].mr_addr = (caddr_t)M_PTRUNC((uintptr_t)&r_debug); mpp[1].mr_msize = (caddr_t)&_end - mpp[1].mr_addr; mpp[1].mr_fsize = (caddr_t)&_edata - mpp[1].mr_addr; mpp[1].mr_prot = (PROT_READ | PROT_WRITE | PROT_EXEC); if ((fdr.fd_nname = stravl_insert(_rtldname, 0, 0, 0)) == NULL) return (0); if ((rlmp = elf_new_lmp(&lml_rtld, ALIST_OFF_DATA, &fdr, (Addr)mpp->mr_addr, (size_t)((uintptr_t)eaddr - (uintptr_t)ld_base), NULL, NULL, NULL)) == NULL) return (0); MMAPS(rlmp) = mpp; MMAPCNT(rlmp) = 2; PADSTART(rlmp) = (ulong_t)mpp[0].mr_addr; PADIMLEN(rlmp) = (ulong_t)mpp[0].mr_addr + (ulong_t)mpp[1].mr_addr + (ulong_t)mpp[1].mr_msize; MODE(rlmp) |= (RTLD_LAZY | RTLD_NODELETE | RTLD_GLOBAL | RTLD_WORLD); FLAGS(rlmp) |= (FLG_RT_ANALYZED | FLG_RT_RELOCED | FLG_RT_INITDONE | FLG_RT_INITCLCT | FLG_RT_FINICLCT | FLG_RT_MODESET); /* * Initialize the runtime linkers information. */ interp = &_interp; interp->i_name = (char *)rtldname; interp->i_faddr = (caddr_t)ADDR(rlmp); ldso_plt_init(rlmp); /* * Map in the file, if exec has not already done so, or if the file * was passed as an argument to an explicit execution of ld.so.1 from * the command line. */ if (fd != -1) { /* * Map the file. Once the object is mapped we no longer need * the file descriptor. */ (void) rtld_fstat(fd, &status); fdm.fd_oname = argvname; fdm.fd_ftp = map_obj(&lml_main, &fdm, status.st_size, argvname, fd, &rej); (void) close(fd); if (fdm.fd_ftp == NULL) { Conv_reject_desc_buf_t rej_buf; eprintf(&lml_main, ERR_FATAL, MSG_INTL(err_reject[rej.rej_type]), argvname, conv_reject_desc(&rej, &rej_buf, M_MACH)); return (0); } /* * Finish processing the loading of the file. */ if ((fdm.fd_nname = stravl_insert(argvname, 0, 0, 0)) == NULL) return (0); fdm.fd_dev = status.st_dev; fdm.fd_ino = status.st_ino; if ((mlmp = load_file(&lml_main, ALIST_OFF_DATA, NULL, &fdm, NULL)) == NULL) return (0); /* * We now have a process name for error diagnostics. */ if ((str = strrchr(argvname, '/')) != NULL) procname = ++str; else procname = argvname; if (ldsoexec) { mmapobj_result_t *mpp = MMAPS(mlmp); uint_t mnum, mapnum = MMAPCNT(mlmp); void *brkbase = NULL; /* * Since ld.so.1 was the primary executed object - the * brk() base has not yet been initialized, we need to * initialize it. For an executable, initialize it to * the end of the object. For a shared object (ET_DYN) * initialize it to the first page in memory. */ for (mnum = 0; mnum < mapnum; mnum++, mpp++) brkbase = mpp->mr_addr + mpp->mr_msize; if (brkbase == NULL) brkbase = (void *)syspagsz; if (_brk_unlocked(brkbase) == -1) { int err = errno; eprintf(&lml_main, ERR_FATAL, MSG_INTL(MSG_SYS_BRK), argvname, strerror(err)); return (0); } } } else { /* * Set up function ptr and arguments according to the type * of file class the executable is. (Currently only supported * types are ELF and a.out format.) Then create a link map * for the executable. */ if (aoutdyn) { #ifdef A_OUT mmapobj_result_t *mpp; /* * Create a mapping structure sufficient to describe * a single two segments. The ADDR() of the a.out is * established as 0, which is required but the AOUT * relocation code. */ if ((mpp = calloc(sizeof (mmapobj_result_t), 2)) == NULL) return (0); if ((fdm.fd_nname = stravl_insert(execname, 0, 0, 0)) == NULL) return (0); if ((mlmp = aout_new_lmp(&lml_main, ALIST_OFF_DATA, &fdm, 0, 0, aoutdyn, NULL, NULL)) == NULL) return (0); /* * Establish the true mapping information for the a.out. */ if (aout_get_mmap(&lml_main, mpp)) { free(mpp); return (0); } MSIZE(mlmp) = (size_t)(mpp[1].mr_addr + mpp[1].mr_msize) - S_ALIGN((size_t)mpp[0].mr_addr, syspagsz); MMAPS(mlmp) = mpp; MMAPCNT(mlmp) = 2; PADSTART(mlmp) = (ulong_t)mpp->mr_addr; PADIMLEN(mlmp) = mpp->mr_msize; /* * Disable any object configuration cache (BCP apps * bring in sbcp which can benefit from any object * cache, but both the app and sbcp can't use the same * objects). */ rtld_flags |= RT_FL_NOOBJALT; /* * Make sure no-direct bindings are in effect. */ lml_main.lm_tflags |= LML_TFLG_NODIRECT; #else eprintf(&lml_main, ERR_FATAL, MSG_INTL(MSG_ERR_REJ_UNKFILE), argvname); return (0); #endif } else if (phdr) { Phdr *pptr; Off i_offset = 0; Addr base = 0; ulong_t phsize; mmapobj_result_t *mpp, *fmpp, *hmpp = NULL; uint_t mapnum = 0; int i; size_t msize; /* * Using the executables phdr address determine the base * address of the input file. NOTE, this assumes the * program headers and elf header are part of the same * mapped segment. Although this has held for many * years now, it might be more flexible if the kernel * gave use the ELF headers start address, rather than * the Program headers. * * Determine from the ELF header if we're been called * from a shared object or dynamic executable. If the * latter, then any addresses within the object are used * as is. Addresses within shared objects must be added * to the process's base address. */ ehdr = (Ehdr *)((Addr)phdr - phdr->p_offset); phsize = ehdr->e_phentsize; if (ehdr->e_type == ET_DYN) base = (Addr)ehdr; /* * Allocate a mapping array to retain mapped segment * information. */ if ((fmpp = mpp = calloc(ehdr->e_phnum, sizeof (mmapobj_result_t))) == NULL) return (0); /* * Extract the needed information from the segment * headers. */ for (i = 0, pptr = phdr; i < ehdr->e_phnum; i++) { if (pptr->p_type == PT_INTERP) { i_offset = pptr->p_offset; interp->i_faddr = (caddr_t)interp_base; } if ((pptr->p_type == PT_LOAD) && (pptr->p_filesz || pptr->p_memsz)) { int perm = (PROT_READ | PROT_EXEC); size_t off; if (i_offset && pptr->p_filesz && (i_offset >= pptr->p_offset) && (i_offset <= (pptr->p_memsz + pptr->p_offset))) { interp->i_name = (char *) pptr->p_vaddr + i_offset - pptr->p_offset + base; i_offset = 0; } if (pptr->p_flags & PF_W) perm |= PROT_WRITE; /* * Retain segments mapping info. Round * each segment to a page boundary, as * this insures addresses are suitable * for mprotect() if required. */ off = pptr->p_vaddr + base; if (hmpp == NULL) { hmpp = mpp; mpp->mr_addr = (caddr_t)ehdr; } else mpp->mr_addr = (caddr_t)off; off -= (size_t)(uintptr_t)mpp->mr_addr; mpp->mr_msize = pptr->p_memsz + off; mpp->mr_fsize = pptr->p_filesz + off; mpp->mr_prot = perm; mpp++, mapnum++; } pptr = (Phdr *)((ulong_t)pptr + phsize); } mpp--; msize = (size_t)(mpp->mr_addr + mpp->mr_msize) - S_ALIGN((size_t)fmpp->mr_addr, syspagsz); if ((fdm.fd_nname = stravl_insert(execname, 0, 0, 0)) == NULL) return (0); if ((mlmp = elf_new_lmp(&lml_main, ALIST_OFF_DATA, &fdm, (Addr)hmpp->mr_addr, msize, NULL, NULL, NULL)) == NULL) return (0); MMAPS(mlmp) = fmpp; MMAPCNT(mlmp) = mapnum; PADSTART(mlmp) = (ulong_t)fmpp->mr_addr; PADIMLEN(mlmp) = (ulong_t)fmpp->mr_addr + (ulong_t)mpp->mr_addr + (ulong_t)mpp->mr_msize; } } /* * Establish the interpretors name as that defined within the initial * object (executable). This provides for ORIGIN processing of ld.so.1 * dependencies. Note, the NAME() of the object remains that which was * passed to us as the SONAME on execution. */ if (ldsoexec == 0) { size_t len = strlen(interp->i_name); if (expand(&interp->i_name, &len, 0, 0, (PD_TKN_ISALIST | PD_TKN_CAP), rlmp) & PD_TKN_RESOLVED) fdr.fd_flags |= FLG_FD_RESOLVED; } fdr.fd_pname = interp->i_name; (void) fullpath(rlmp, &fdr); /* * The runtime linker acts as a filtee for various dl*() functions that * are defined in libc (and libdl). Make sure this standard name for * the runtime linker is also registered in the FullPathNode AVL tree. */ (void) fpavl_insert(&lml_rtld, rlmp, _rtldname, 0); /* * Having established the true runtime linkers name, simplify the name * for error diagnostics. */ if ((str = strrchr(PATHNAME(rlmp), '/')) != NULL) rtldname = ++str; else rtldname = PATHNAME(rlmp); /* * Expand the fullpath name of the application. This typically occurs * as a part of loading an object, but as the kernel probably mapped * it in, complete this processing now. */ (void) fullpath(mlmp, 0); /* * Some troublesome programs will change the value of argv[0]. Dupping * the process string protects us, and insures the string is left in * any core files. */ if ((str = (char *)strdup(procname)) == NULL) return (0); procname = str; FLAGS(mlmp) |= (FLG_RT_ISMAIN | FLG_RT_MODESET); FLAGS1(mlmp) |= FL1_RT_USED; /* * It's the responsibility of MAIN(crt0) to call it's _init and _fini * section, therefore null out any INIT/FINI so that this object isn't * collected during tsort processing. And, if the application has no * initarray or finiarray we can economize on establishing bindings. */ INIT(mlmp) = FINI(mlmp) = NULL; if ((INITARRAY(mlmp) == NULL) && (FINIARRAY(mlmp) == NULL)) FLAGS1(mlmp) |= FL1_RT_NOINIFIN; /* * Identify lddstub if necessary. */ if (lml_main.lm_flags & LML_FLG_TRC_LDDSTUB) FLAGS1(mlmp) |= FL1_RT_LDDSTUB; /* * Retain our argument information for use in dlinfo. */ argsinfo.dla_argv = argv--; argsinfo.dla_argc = (long)*argv; argsinfo.dla_envp = envp; argsinfo.dla_auxv = auxv; (void) enter(0); /* * Add our two main link-maps to the dynlm_list */ if (aplist_append(&dynlm_list, &lml_main, AL_CNT_DYNLIST) == NULL) return (0); if (aplist_append(&dynlm_list, &lml_rtld, AL_CNT_DYNLIST) == NULL) return (0); /* * Reset the link-map counts for both lists. The init count is used to * track how many objects have pending init sections, this gets incre- * mented each time an object is relocated. Since ld.so.1 relocates * itself, it's init count will remain zero. * The object count is used to track how many objects have pending fini * sections, as ld.so.1 handles its own fini we can zero its count. */ lml_main.lm_obj = 1; lml_rtld.lm_obj = 0; /* * Initialize debugger information structure. Some parts of this * structure were initialized statically. */ r_debug.rtd_rdebug.r_map = (Link_map *)lml_main.lm_head; r_debug.rtd_rdebug.r_ldsomap = (Link_map *)lml_rtld.lm_head; r_debug.rtd_rdebug.r_ldbase = r_debug.rtd_rdebug.r_ldsomap->l_addr; r_debug.rtd_dynlmlst = &dynlm_list; /* * Determine the dev/inode information for the executable to complete * load_so() checking for those who might dlopen(a.out). */ if (rtld_stat(PATHNAME(mlmp), &status) == 0) { STDEV(mlmp) = status.st_dev; STINO(mlmp) = status.st_ino; } /* * Initialize any configuration information. */ if (!(rtld_flags & RT_FL_NOCFG)) { if ((features = elf_config(mlmp, (aoutdyn != 0))) == -1) return (0); } #if defined(_ELF64) /* * If this is a 64-bit process, determine whether this process has * restricted the process address space to 32-bits. Any dependencies * that are restricted to a 32-bit address space can only be loaded if * the executable has established this requirement. */ if (CAPSET(mlmp).sc_sf_1 & SF1_SUNW_ADDR32) rtld_flags2 |= RT_FL2_ADDR32; #endif /* * Establish any alternative capabilities, and validate this object * if it defines it's own capabilities information. */ if (cap_alternative() == 0) return (0); if (cap_check_lmp(mlmp, &rej) == 0) { if (lml_main.lm_flags & LML_FLG_TRC_ENABLE) { /* LINTED */ (void) printf(MSG_INTL(ldd_warn[rej.rej_type]), NAME(mlmp), rej.rej_str); } else { /* LINTED */ eprintf(&lml_main, ERR_FATAL, MSG_INTL(err_reject[rej.rej_type]), NAME(mlmp), rej.rej_str); return (0); } } /* * Establish the modes of the initial object. These modes are * propagated to any preloaded objects and explicit shared library * dependencies. * * If we're generating a configuration file using crle(1), remove * any RTLD_NOW use, as we don't want to trigger any relocation proc- * essing during crle(1)'s first past (this would just be unnecessary * overhead). Any filters are explicitly loaded, and thus RTLD_NOW is * not required to trigger filter loading. * * Note, RTLD_NOW may have been established during analysis of the * application had the application been built -z now. */ MODE(mlmp) |= (RTLD_NODELETE | RTLD_GLOBAL | RTLD_WORLD); if (rtld_flags & RT_FL_CONFGEN) { MODE(mlmp) |= RTLD_CONFGEN; MODE(mlmp) &= ~RTLD_NOW; rtld_flags2 &= ~RT_FL2_BINDNOW; } if ((MODE(mlmp) & RTLD_NOW) == 0) { if (rtld_flags2 & RT_FL2_BINDNOW) MODE(mlmp) |= RTLD_NOW; else MODE(mlmp) |= RTLD_LAZY; } /* * If debugging was requested initialize things now that any cache has * been established. A user can specify LD_DEBUG=help to discover the * list of debugging tokens available without running the application. * However, don't allow this setting from a configuration file. * * Note, to prevent recursion issues caused by loading and binding the * debugging libraries themselves, a local debugging descriptor is * initialized. Once the debugging setup has completed, this local * descriptor is copied to the global descriptor which effectively * enables diagnostic output. * * Ignore any debugging request if we're being monitored by a process * that expects the old getpid() initialization handshake. */ if ((rpl_debug || prm_debug) && ((rtld_flags & RT_FL_DEBUGGER) == 0)) { Dbg_desc _dbg_desc = {0}; struct timeval total = DBG_TOTALTIME; struct timeval delta = DBG_DELTATIME; if (rpl_debug) { if (dbg_setup(rpl_debug, &_dbg_desc) == 0) return (0); if (_dbg_desc.d_extra & DBG_E_HELP_EXIT) rtldexit(&lml_main, 0); } if (prm_debug) (void) dbg_setup(prm_debug, &_dbg_desc); *dbg_desc = _dbg_desc; DBG_TOTALTIME = total; DBG_DELTATIME = delta; } /* * Now that debugging is enabled generate any diagnostics from any * previous events. */ if (DBG_ENABLED) { DBG_CALL(Dbg_cap_val(&lml_main, org_scapset, alt_scapset, M_MACH)); DBG_CALL(Dbg_file_config_dis(&lml_main, config->c_name, features)); DBG_CALL(Dbg_file_ldso(rlmp, envp, auxv, LIST(rlmp)->lm_lmidstr, ALIST_OFF_DATA)); if (THIS_IS_ELF(mlmp)) { DBG_CALL(Dbg_file_elf(&lml_main, PATHNAME(mlmp), ADDR(mlmp), MSIZE(mlmp), LIST(mlmp)->lm_lmidstr, ALIST_OFF_DATA)); } else { DBG_CALL(Dbg_file_aout(&lml_main, PATHNAME(mlmp), ADDR(mlmp), MSIZE(mlmp), LIST(mlmp)->lm_lmidstr, ALIST_OFF_DATA)); } } /* * Enable auditing. */ if (rpl_audit || prm_audit || profile_lib) { int ndx; const char *aud[3]; aud[0] = rpl_audit; aud[1] = prm_audit; aud[2] = profile_lib; /* * Any global auditing (set using LD_AUDIT or LD_PROFILE) that * can't be established is non-fatal. */ if ((auditors = calloc(1, sizeof (Audit_desc))) == NULL) return (0); for (ndx = 0; ndx < 3; ndx++) { if (aud[ndx]) { if ((auditors->ad_name = strdup(aud[ndx])) == NULL) return (0); rtld_flags2 |= RT_FL2_FTL2WARN; (void) audit_setup(mlmp, auditors, PD_FLG_EXTLOAD, NULL); rtld_flags2 &= ~RT_FL2_FTL2WARN; } } lml_main.lm_tflags |= auditors->ad_flags; } if (AUDITORS(mlmp)) { /* * Any object required auditing (set with a DT_DEPAUDIT dynamic * entry) that can't be established is fatal. */ if (FLAGS1(mlmp) & FL1_RT_GLOBAUD) { /* * If this object requires global auditing, use the * local auditing information to set the global * auditing descriptor. The effect is that a * DT_DEPAUDIT act as an LD_AUDIT. */ if ((auditors == NULL) && ((auditors = calloc(1, sizeof (Audit_desc))) == NULL)) return (0); auditors->ad_name = AUDITORS(mlmp)->ad_name; if (audit_setup(mlmp, auditors, 0, NULL) == 0) return (0); lml_main.lm_tflags |= auditors->ad_flags; /* * Clear the local auditor information. */ free((void *) AUDITORS(mlmp)); AUDITORS(mlmp) = NULL; } else { /* * Establish any local auditing. */ if (audit_setup(mlmp, AUDITORS(mlmp), 0, NULL) == 0) return (0); AFLAGS(mlmp) |= AUDITORS(mlmp)->ad_flags; lml_main.lm_flags |= LML_FLG_LOCAUDIT; } } /* * Explicitly add the initial object and ld.so.1 to those objects being * audited. Note, although the ld.so.1 link-map isn't auditable, * establish a cookie for ld.so.1 as this may be bound to via the * dl*() family. */ if ((lml_main.lm_tflags | AFLAGS(mlmp)) & LML_TFLG_AUD_MASK) { if (((audit_objopen(mlmp, mlmp) == 0) || (audit_objopen(mlmp, rlmp) == 0)) && (AFLAGS(mlmp) & LML_TFLG_AUD_MASK)) return (0); } /* * Map in any preloadable shared objects. Establish the caller as the * head of the main link-map list. In the case of being exercised from * lddstub, the caller gets reassigned to the first target shared object * so as to provide intuitive diagnostics from ldd(). * * Note, it is valid to preload a 4.x shared object with a 5.0 * executable (or visa-versa), as this functionality is required by * ldd(1). */ clmp = mlmp; if (rpl_preload && (preload(rpl_preload, mlmp, &clmp) == 0)) return (0); if (prm_preload && (preload(prm_preload, mlmp, &clmp) == 0)) return (0); /* * Load all dependent (needed) objects. */ if (analyze_lmc(&lml_main, ALIST_OFF_DATA, mlmp, mlmp, NULL) == NULL) return (0); /* * Relocate all the dependencies we've just added. * * If this process has been established via crle(1), the environment * variable LD_CONFGEN will have been set. crle(1) may create this * process twice. The first time crle only needs to gather dependency * information. The second time, is to dldump() the images. * * If we're only gathering dependencies, relocation is unnecessary. * As crle(1) may be building an arbitrary family of objects, they may * not fully relocate either. Hence the relocation phase is not carried * out now, but will be called by crle(1) once all objects have been * loaded. */ if ((rtld_flags & RT_FL_CONFGEN) == 0) { DBG_CALL(Dbg_util_nl(&lml_main, DBG_NL_STD)); if (relocate_lmc(&lml_main, ALIST_OFF_DATA, mlmp, mlmp, NULL) == 0) return (0); /* * Inform the debuggers that basic process initialization is * complete, and that the state of ld.so.1 (link-map lists, * etc.) is stable. This handshake enables the debugger to * initialize themselves, and consequently allows the user to * set break points in .init code. * * Most new debuggers use librtld_db to monitor activity events. * Older debuggers indicated their presence by setting the * DT_DEBUG entry in the dynamic executable (see elf_new_lm()). * In this case, getpid() is called so that the debugger can * catch the system call. This old mechanism has some * restrictions, as getpid() should not be called prior to * basic process initialization being completed. This * restriction has become increasingly difficult to maintain, * as the use of auditors, LD_DEBUG, and the initialization * handshake with libc can result in "premature" getpid() * calls. The use of this getpid() handshake is expected to * disappear at some point in the future, and there is intent * to work towards that goal. */ rd_event(&lml_main, RD_DLACTIVITY, RT_CONSISTENT); rd_event(&lml_rtld, RD_DLACTIVITY, RT_CONSISTENT); if (rtld_flags & RT_FL_DEBUGGER) { r_debug.rtd_rdebug.r_flags |= RD_FL_ODBG; (void) getpid(); } } /* * Indicate preinit activity, and call any auditing routines. These * routines are called before initializing any threads via libc, or * before collecting the complete set of .inits on the primary link-map. * Although most libc interfaces are encapsulated in local routines * within libc, they have been known to escape (ie. call a .plt). As * the appcert auditor uses preinit as a trigger to establish some * external interfaces to the main link-maps libc, we need to activate * this trigger before exercising any code within libc. Additionally, * I wouldn't put it past an auditor to add additional objects to the * primary link-map. Hence, we collect .inits after the audit call. */ rd_event(&lml_main, RD_PREINIT, 0); if (aud_activity || ((lml_main.lm_tflags | AFLAGS(mlmp)) & LML_TFLG_AUD_ACTIVITY)) audit_activity(mlmp, LA_ACT_CONSISTENT); if (aud_preinit || ((lml_main.lm_tflags | AFLAGS(mlmp)) & LML_TFLG_AUD_PREINIT)) audit_preinit(mlmp); /* * If we're creating initial configuration information, we're done * now that the auditing step has been called. */ if (rtld_flags & RT_FL_CONFGEN) { leave(LIST(mlmp), 0); return (mlmp); } /* * Sort the .init sections of all objects we've added. If we're * tracing we only need to execute this under ldd(1) with the -i or -u * options. */ lmflags = lml_main.lm_flags; if (((lmflags & LML_FLG_TRC_ENABLE) == 0) || (lmflags & (LML_FLG_TRC_INIT | LML_FLG_TRC_UNREF))) { if ((tobj = tsort(mlmp, LIST(mlmp)->lm_init, RT_SORT_REV)) == (Rt_map **)S_ERROR) return (0); } /* * If we are tracing we're done. This is the one legitimate use of a * direct call to rtldexit() rather than return, as we don't want to * return and jump to the application. */ if (lmflags & LML_FLG_TRC_ENABLE) { unused(&lml_main); rtldexit(&lml_main, 0); } /* * Check if this instance of the linker should have a primary link * map. This flag allows multiple copies of the -same- -version- * of the linker (and libc) to run in the same address space. * * Without this flag we only support one copy of the linker in a * process because by default the linker will always try to * initialize at one primary link map The copy of libc which is * initialized on a primary link map will initialize global TLS * data which can be shared with other copies of libc in the * process. The problem is that if there is more than one copy * of the linker, only one copy should link libc onto a primary * link map, otherwise libc will attempt to re-initialize global * TLS data. So when a copy of the linker is loaded with this * flag set, it will not initialize any primary link maps since * presumably another copy of the linker will do this. * * Note that this flag only allows multiple copies of the -same- * -version- of the linker (and libc) to coexist. This approach * will not work if we are trying to load different versions of * the linker and libc into the same process. The reason for * this is that the format of the global TLS data may not be * the same for different versions of libc. In this case each * different version of libc must have it's own primary link map * and be able to maintain it's own TLS data. The only way this * can be done is by carefully managing TLS pointers on transitions * between code associated with each of the different linkers. * Note that this is actually what is done for processes in lx * branded zones. Although in the lx branded zone case, the * other linker and libc are actually gld and glibc. But the * same general TLS management mechanism used by the lx brand * would apply to any attempts to run multiple versions of the * solaris linker and libc in a single process. */ if (auxflags & AF_SUN_NOPLM) rtld_flags2 |= RT_FL2_NOPLM; /* * Establish any static TLS for this primary link-map. Note, regardless * of whether TLS is available, an initial handshake occurs with libc to * indicate we're processing the primary link-map. Having identified * the primary link-map, initialize threads. */ if (rt_get_extern(&lml_main, mlmp) == 0) return (0); if ((rtld_flags2 & RT_FL2_NOPLM) == 0) { if (tls_statmod(&lml_main, mlmp) == 0) return (0); rt_thr_init(&lml_main); rtld_flags2 |= RT_FL2_PLMSETUP; } else { rt_thr_init(&lml_main); } /* * Fire all dependencies .init sections. Identify any unused * dependencies, and leave the runtime linker - effectively calling * the dynamic executables entry point. */ call_array(PREINITARRAY(mlmp), (uint_t)PREINITARRAYSZ(mlmp), mlmp, SHT_PREINIT_ARRAY); if (tobj) call_init(tobj, DBG_INIT_SORT); rd_event(&lml_main, RD_POSTINIT, 0); unused(&lml_main); DBG_CALL(Dbg_util_call_main(mlmp)); rtld_flags |= (RT_FL_OPERATION | RT_FL_APPLIC); leave(LIST(mlmp), 0); return (mlmp); }
obj eval(obj exp){ ev: assert(!! exp); obj rr,lt, rt; switch (exp->type) { case tInd: return doInd(eval(ult(exp)), ul(eval(urt(exp)))); case LIST: return List2v(evalList(ul(exp))); case tArray: return map_obj(eval, exp); case tAnd: return prod_eval(ul(exp), mult); case MULT: return prod_eval(ul(exp), mult); case ARITH: return prod_eval(ul(exp), add); case POW: return prod_eval(ul(exp), power); case DIVIDE: return prod_eval(ul(exp), divide); case tRef: return retain(uref(exp)); case tSymbol: if( macromode) { if(obj rr = search_assoc(car(macro_env), exp)){ macromode = false; // macro lexical scope should be pushed to the stack here rr = exec(rr); macromode = true; return rr; } } return eval_symbol(exp); case tMinus: lt = eval(uref(exp)); rr = uMinus(lt); // releasing if(rr) {release(lt); return rr;} static obj symumn = Symbol("-"); rr = udef_op0(symumn, lt); if(rr) {release(lt); return rr;} error("uMinus: not defined to that type"); case tReturn: if(! uref(exp)) return encap(tSigRet, nil); return encap(tSigRet, eval(uref(exp))); case tBreak: return retain(exp); case CONDITION: return evalCond(exp); case tOp: if(type(ult(exp)) ==tSymbol) { lt = search_assoc(curr_interp->types, ult(exp)); if(lt) return encap((ValueType)vrInt(lt), eval(urt(exp)));} lt = eval(ult(exp)); push(lt); switch(lt->type){ case tCont: assert(0); case tSpecial: rr = ufn(lt)(urt(exp)); break; case tSyntaxLam: rr = macro_exec(lt, urt(exp)); break; case tInternalFn: case tClosure: rt = eval(urt(exp)); rr = eval_function(lt, rt); break; default: rt = eval(urt(exp)); rr = call_fn(mult, lt, rt); release(rt); } release(pop(&is)); return rr; case tClosure: assert(0); case tCurry: return eval_curry(exp, em0(exp)); /* obj vars = Assoc(); bind_vars(&vars, em0(exp), em2(exp)); rr = eval_curry(exp, vars); release(vars); return rr; */ case tArrow: // return enclose(exp); /* if(macromode){ if(obj rr = search_assoc(car(macro_env), exp)){ } } */ return render(tClosure, list3(retain(em0(exp)), retain(em1(exp)), retain(env))); case tDefine: return func_def(em0(exp), em1(exp), em2(exp)); case tSyntaxDef: let(lfind_var(em0(exp)), render(tSyntaxLam, list3(retain(em1(exp)), retain(em2(exp)), nil))); return nil; case tExec: return exec(exp); case tAssign: lt = car(exp); if(type(lt)==tOp){ return func_def(ult(lt), urt(lt), cdr(exp)); } else if(type(lt)==tMinus){ static obj symumn = Symbol("-"); return func_def(symumn, uref(lt), cdr(exp)); } else return do_assign(lt, eval(cdr(exp))); case tIf: rr = eval(em0(exp)); if (type(rr) != INT) error("if: Boolean Expected"); if (vrInt(rr)) { rr = em1(exp); } else { rr = em2(exp); } return eval(rr); case tWhile: for(;;) { rr = eval(car(exp)); if (type(rr) != INT) error("while: Boolean expected"); if(!vrInt(rr)) break; rr = exec(cdr(exp)); if(rr && type(rr)==tSigRet) return rr; if(rr && type(rr)==tBreak) {release(rr); break;} if(rr) release(rr); } return nil; default: return retain(exp); } }
std::string load_game_dialog(display& disp, const config& game_config, bool* show_replay, bool* cancel_orders) { std::vector<savegame::save_info> games; { cursor::setter cur(cursor::WAIT); games = savegame::manager::get_saves_list(); } if(games.empty()) { gui2::show_transient_message(disp.video(), _("No Saved Games"), _("There are no saved games to load.\n\n(Games are saved automatically when you complete a scenario)")); return ""; } std::vector<config*> summaries; std::vector<savegame::save_info>::const_iterator i; //FIXME: parent_to_child is not used yet std::map<std::string,std::string> parent_to_child; for(i = games.begin(); i != games.end(); ++i) { config& cfg = savegame::save_index::save_summary(i->name); parent_to_child[cfg["parent"]] = i->name; summaries.push_back(&cfg); } const events::event_context context; std::vector<std::string> items; std::ostringstream heading; heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date"); items.push_back(heading.str()); for(i = games.begin(); i != games.end(); ++i) { std::string name = i->name; utils::truncate_as_wstring(name, std::min<size_t>(name.size(), 40)); std::ostringstream str; str << name << COLUMN_SEPARATOR << format_time_summary(i->time_modified); items.push_back(str.str()); } gamemap map_obj(game_config, ""); gui::dialog lmenu(disp, _("Load Game"), _("Choose the game to load"), gui::NULL_DIALOG); lmenu.set_basic_behavior(gui::OK_CANCEL); gui::menu::basic_sorter sorter; sorter.set_alpha_sort(0).set_id_sort(1); lmenu.set_menu(items, &sorter); gui::filter_textbox* filter = new gui::filter_textbox(disp.video(), _("Filter: "), items, items, 1, lmenu); lmenu.set_textbox(filter); save_preview_pane save_preview(disp.video(),game_config,&map_obj,games,summaries,*filter); lmenu.add_pane(&save_preview); // create an option for whether the replay should be shown or not // if(show_replay != NULL) { // lmenu.add_option(_("Show replay"), false, // // use smallgui layout as default, otherwise the load screen glitches at low res! // //game_config::small_gui ? gui::dialog::BUTTON_CHECKBOX : gui::dialog::BUTTON_STANDARD); // gui::dialog::BUTTON_CHECKBOX); // } if(cancel_orders != NULL) { lmenu.add_option(_("Cancel orders"), false, // use smallgui layout as default, otherwise the load screen glitches at low res! //game_config::small_gui ? gui::dialog::BUTTON_STANDARD : gui::dialog::BUTTON_EXTRA); gui::dialog::BUTTON_STANDARD); } lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("OK"),0,false), gui::dialog::BUTTON_STANDARD); lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("Cancel"),1,true), gui::dialog::BUTTON_STANDARD); delete_save save_deleter(disp,*filter,games,summaries); gui::dialog_button_info delete_button(&save_deleter,_("Delete Save")); lmenu.add_button(delete_button, // use smallgui layout as default, otherwise the load screen glitches at low res! //game_config::small_gui ? gui::dialog::BUTTON_HELP : gui::dialog::BUTTON_EXTRA); gui::dialog::BUTTON_HELP); int res = lmenu.show(); savegame::save_index::write_save_index(); if(res == -1) return ""; res = filter->get_index(res); int option_index = 0; if(show_replay != NULL) { *show_replay = lmenu.option_checked(option_index++); const config& summary = *summaries[res]; if (summary["replay"].to_bool() && !summary["snapshot"].to_bool(true)) { *show_replay = true; } } if (cancel_orders != NULL) { *cancel_orders = lmenu.option_checked(option_index++); } return games[res].name; }