| |
| |
| |
| |
| |
| |
| #include <limits.h> |
| #include <dlfcn.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <pthread.h> |
| |
| #include <sltdl/sltdl.h> |
| #include "sltdl_core.h" |
| #include "sltdl_module.h" |
| |
| #ifdef O_EXEC |
| #define SLTDL_MODULE_OPEN_OPTIONS (O_CLOEXEC|O_EXEC) |
| #else |
| #define SLTDL_MODULE_OPEN_OPTIONS (O_CLOEXEC) |
| #endif |
| |
| static int lt_status; |
| static off_t lt_plen; |
| static off_t lt_plocs; |
| static char * lt_upath; |
| static char * lt_vpath; |
| static char ** lt_vmark; |
| static char ** lt_pathv; |
| |
| static struct lt_modctx * lt_modv_head; |
| static struct lt_modctx * lt_modv_tail; |
| |
| static struct lt_modctx * lt_modv_next; |
| static struct lt_modctx * lt_modv_cap; |
| |
| static int lt_setstatus(int ret, int status) |
| { |
| lt_status = status; |
| return ret; |
| } |
| |
| const char * lt_dlgetsearchpath(void) |
| { |
| return lt_upath; |
| } |
| |
| static int lt_dlsetsearchpath_locked(const char * path) |
| { |
| const char * ch; |
| char * uch; |
| char * vch; |
| char ** pathv; |
| off_t elements; |
| |
| if (path[0] != '/') |
| return lt_setstatus(1,SLTDL_PATH_INVALID_FIRST_CHAR); |
| |
| elements = 1; |
| |
| for (ch=&path[1]; *ch; ch++) { |
| if (*ch == ':') { |
| if (ch[1] != '/') |
| return lt_setstatus(1,SLTDL_PATH_INVALID_FIRST_CHAR); |
| |
| elements++; |
| } |
| } |
| |
| if (++elements > lt_plocs) { |
| if (lt_pathv) { |
| free(lt_pathv); |
| lt_pathv = 0; |
| } |
| |
| lt_plocs = elements; |
| lt_plocs += 0x3f; |
| lt_plocs |= 0x3f; |
| lt_plocs ^= 0x3f; |
| |
| if (!(lt_pathv = calloc(lt_plocs,sizeof(char *)))) |
| return lt_setstatus(1,SLTDL_SYSTEM_ERROR); |
| } |
| |
| if ((ch - path) > lt_plen) { |
| if (lt_upath) { |
| free(lt_upath); |
| lt_upath = 0; |
| } |
| |
| lt_plen = ((ch - path + 1) <= 0x800) |
| ? 0x800 : ch - path + 1; |
| |
| lt_plen += 0x7ff; |
| lt_plen |= 0x7ff; |
| lt_plen ^= 0x7ff; |
| if (!(lt_upath = calloc(2*lt_plen,1))) |
| return lt_setstatus(1,SLTDL_SYSTEM_ERROR); |
| |
| lt_vpath = <_upath[lt_plen]; |
| } |
| |
| pathv = lt_pathv; |
| *pathv++ = lt_vpath; |
| |
| for (ch=path, uch=lt_upath, vch=lt_vpath; *ch; ch++) { |
| if (*ch == ':') { |
| *uch++ = ch[0]; |
| *uch++ = ch[1]; |
| |
| *vch++ = 0; |
| *pathv = vch; |
| *vch++ = ch[1]; |
| |
| ch++; |
| pathv++; |
| } else { |
| *uch++ = *ch; |
| *vch++ = *ch; |
| } |
| } |
| |
| lt_vmark = pathv; |
| |
| return lt_setstatus(0,SLTDL_OK); |
| } |
| |
| int lt_dlsetsearchpath(const char * path) |
| { |
| int ret; |
| lt_slock(); |
| ret = lt_dlsetsearchpath_locked(path); |
| return lt_sunlock(ret,lt_status); |
| } |
| |
| static int lt_dladdsearchdir_locked(const char * path) |
| { |
| int ret; |
| const char * ch; |
| char * buf; |
| off_t alen; |
| off_t plen; |
| |
| if (path[0] != '/') |
| return lt_setstatus(1,SLTDL_PATH_INVALID_FIRST_CHAR); |
| |
| for (ch=path; *ch; ch++) |
| if (*ch == ':') |
| return lt_setstatus(1,SLTDL_PATH_INVALID_SEPARATTOR_CHAR); |
| |
| alen = strlen(path); |
| plen = strlen(lt_upath); |
| |
| |
| if (!lt_pathv[lt_plocs - 2] && ((plen + 1 + alen + 1) <= lt_plen)) { |
| lt_upath[plen] = ':'; |
| lt_vpath[plen] = 0; |
| |
| plen++; |
| |
| strcpy(<_upath[plen],path); |
| strcpy(<_vpath[plen],path); |
| |
| *lt_vmark++ = <_vpath[plen]; |
| |
| return lt_setstatus(0,SLTDL_OK); |
| } |
| |
| |
| if (!(buf = malloc(plen + 1 + alen + 1))) |
| return lt_setstatus(1,SLTDL_SYSTEM_ERROR); |
| |
| sprintf(buf,"%s:%s",lt_upath,path); |
| |
| ret = lt_dlsetsearchpath_locked(buf); |
| |
| free(buf); |
| |
| return ret; |
| } |
| |
| int lt_dladdsearchdir(const char * path) |
| { |
| int ret; |
| lt_slock(); |
| ret = lt_dladdsearchdir_locked(path); |
| return lt_sunlock(ret,lt_status); |
| } |
| |
| int lt_dlinsertsearchdir(const char * mark, const char * path) |
| { |
| int ret; |
| const char * ch; |
| char * buf; |
| char * dst; |
| off_t alen; |
| off_t plen; |
| off_t slen; |
| off_t offset; |
| char ** pathv; |
| |
| if (!mark) |
| return lt_dladdsearchdir(path); |
| |
| lt_slock(); |
| |
| if (path[0] != '/') |
| return lt_sunlock(1,SLTDL_PATH_INVALID_FIRST_CHAR); |
| |
| for (ch=path; *ch; ch++) |
| if (*ch == ':') |
| return lt_sunlock(1,SLTDL_PATH_INVALID_SEPARATTOR_CHAR); |
| |
| alen = strlen(path); |
| plen = strlen(lt_upath); |
| |
| if ((mark < lt_upath) || (mark >= <_upath[plen])) |
| return lt_sunlock(1,SLTDL_PATH_INVALID_MARK); |
| |
| if ((mark > lt_upath) && (mark[-1] != ':')) |
| return lt_sunlock(1,SLTDL_PATH_INVALID_MARK); |
| |
| mark = <_vpath[mark - lt_upath]; |
| |
| |
| if (!lt_pathv[lt_plocs - 2] && ((plen + 1 + alen + 1) <= lt_plen)) { |
| for (pathv=lt_vmark; pathv>=lt_pathv; pathv--) { |
| slen = strlen(pathv[-1]); |
| offset = pathv[-1] - lt_vpath; |
| offset += alen + 1; |
| |
| memcpy(<_upath[offset],pathv[-1],slen); |
| memcpy(<_vpath[offset],<_upath[offset],slen); |
| |
| if (pathv < lt_vmark) |
| lt_upath[offset + slen] = ':'; |
| |
| pathv[0] = pathv[-1] + alen + 1; |
| |
| if (pathv[-1] == mark) { |
| offset = mark - lt_vpath; |
| strcpy(<_vpath[offset],path); |
| memcpy(<_upath[offset],path,alen); |
| lt_upath[offset+alen] = ':'; |
| lt_vmark++; |
| return lt_sunlock(0,SLTDL_OK); |
| } |
| } |
| } |
| |
| |
| if (!(buf = malloc(plen + 1 + alen + 1))) |
| return lt_sunlock(1,SLTDL_SYSTEM_ERROR); |
| |
| for (dst=buf, pathv=lt_pathv; *pathv; pathv++) { |
| if (*pathv == mark) |
| dst += sprintf(dst,"%s:",path); |
| |
| if (pathv[1]) |
| dst += sprintf(dst,"%s:",*pathv); |
| else |
| dst += sprintf(dst,"%s",*pathv); |
| } |
| |
| ret = lt_dlsetsearchpath_locked(buf); |
| |
| free(buf); |
| |
| return lt_sunlock(ret,lt_status); |
| } |
| |
| static int lt_dlpathopen_locked( |
| const char * module, |
| const char ** extv, |
| char ** mpath) |
| { |
| int fdat; |
| int fdmod; |
| char ** ppath; |
| const char ** pext; |
| size_t plen; |
| size_t mlen; |
| size_t elen; |
| char path[1024]; |
| |
| if ((mlen = strlen(module)) >= sizeof(path)) |
| return lt_setstatus(-1,SLTDL_PATH_INVALID_LEN); |
| |
| memcpy(path,module,mlen); |
| |
| for (ppath=lt_pathv; *ppath; ppath++) { |
| fdat = open(*ppath,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0); |
| |
| if (fdat >= 0) { |
| for (pext=extv; *pext; pext++) { |
| if (mlen + (elen = strlen(*pext)) >= (sizeof(path))) { |
| close(fdat); |
| return lt_setstatus(-1,SLTDL_PATH_INVALID_LEN); |
| } |
| |
| memcpy(&path[mlen],*pext,elen); |
| path[mlen+elen] = 0; |
| |
| fdmod = openat(fdat,path,SLTDL_MODULE_OPEN_OPTIONS,0); |
| |
| if (fdmod >= 0) { |
| if (mpath) { |
| plen = strlen(*ppath); |
| plen += mlen + 1 + elen + 1; |
| |
| if (!(*mpath = malloc(plen))) { |
| close(fdat); |
| close(fdmod); |
| return lt_setstatus(-1,SLTDL_SYSTEM_ERROR); |
| } |
| |
| sprintf(*mpath,"%s/%s",*ppath,path); |
| } |
| |
| close(fdat); |
| return lt_setstatus(fdmod,SLTDL_OK); |
| } |
| } |
| |
| close(fdat); |
| } |
| } |
| |
| return lt_setstatus(-1,SLTDL_PATH_NO_ENTRY); |
| } |
| |
| int lt_dlpathopen(const char * module, const char ** extv) |
| { |
| int ret; |
| lt_slock(); |
| ret = lt_dlpathopen_locked(module,extv,0); |
| return lt_sunlock(ret,lt_status); |
| } |
| |
| static struct lt_modctx * lt_dlopen_locked( |
| const char * module, |
| const char ** extv, |
| int mode) |
| { |
| int fdmod; |
| char * mpath; |
| void * maddr; |
| struct lt_modctx * modctx; |
| struct lt_modctx * modctx_buf; |
| |
| |
| if ((fdmod = lt_dlpathopen_locked(module,extv,&mpath)) < 0) |
| return 0; |
| |
| close(fdmod); |
| |
| |
| if (lt_modv_next == lt_modv_cap) { |
| if (!(modctx_buf = calloc(64,sizeof(*modctx)))) { |
| free(mpath); |
| lt_setstatus(0,SLTDL_SYSTEM_ERROR); |
| return 0; |
| } |
| |
| lt_modv_next = modctx_buf; |
| lt_modv_cap = <_modv_next[64]; |
| } |
| |
| |
| if (!(maddr = dlopen(mpath,mode))) { |
| free(mpath); |
| lt_setstatus(0,SLTDL_DLFCN_ERROR); |
| return 0; |
| } |
| |
| |
| for (modctx=lt_modv_head; modctx; modctx=modctx->mnext) { |
| if (!strcmp(modctx->mpath,mpath)) { |
| free(mpath); |
| modctx->mrefs++; |
| return modctx; |
| } |
| } |
| |
| |
| modctx = lt_modv_next; |
| modctx->maddr = maddr; |
| modctx->mpath = mpath; |
| modctx->mrefs = 1; |
| lt_modv_next++; |
| |
| |
| if (lt_modv_tail) { |
| lt_modv_tail->mnext = modctx; |
| lt_modv_tail = modctx; |
| } else { |
| lt_modv_head = modctx; |
| lt_modv_tail = modctx; |
| } |
| |
| |
| return modctx; |
| } |
| |
| struct lt_modctx * lt_dlopen(const char * module) |
| { |
| struct lt_modctx * modctx; |
| const char * extv[2] = {"",0}; |
| |
| lt_slock(); |
| modctx = lt_dlopen_locked(module,extv,RTLD_NOW); |
| lt_sunlock(0,lt_status); |
| return modctx; |
| } |
| |
| struct lt_modctx * lt_dlopenext(const char * module) |
| { |
| struct lt_modctx * modctx; |
| const char * extv[3] = {"",OS_LIB_SUFFIX,0}; |
| |
| lt_slock(); |
| modctx = lt_dlopen_locked(module,extv,RTLD_NOW); |
| lt_sunlock(0,lt_status); |
| return modctx; |
| } |
| |
| struct lt_modctx * lt_dlopenadvise(const char * module, struct lt_modctl * modctl) |
| { |
| (void)modctl; |
| return lt_dlopenext(module); |
| } |
| |
| void * lt_dlsym(struct lt_modctx * modctx, const char * symname) |
| { |
| return dlsym(modctx->maddr,symname); |
| } |
| |
| int lt_dlclose(struct lt_modctx * modctx) |
| { |
| struct lt_modctx * pmod; |
| |
| lt_slock(); |
| |
| for (pmod=lt_modv_head; pmod ; pmod=pmod->mnext) { |
| if (pmod == modctx) { |
| if (pmod->mrefs) { |
| pmod->mrefs--; |
| return lt_sunlock(0,SLTDL_OK); |
| } |
| |
| return lt_sunlock(-1,SLTDL_MODULE_REF_COUNT); |
| } |
| } |
| |
| return lt_sunlock(-1,SLTDL_MODULE_PTR_INVALID); |
| } |