/*******************************************************************/ /* sltdl: a surrogate ltdl implementation */ /* Copyright (C) 2019 Z. Gilboa */ /* Released under the Standard MIT License; see COPYING.SLTDL. */ /*******************************************************************/ #include #include #include #include #include #include #include #include #include #include "sltdl_core.h" #include "sltdl_module.h" 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; 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 1; elements = 1; for (ch=&path[1]; *ch; ch++) { if (*ch == ':') { if (ch[1] != '/') return 1; 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 1; } 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 1; 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 0; } int lt_dlsetsearchpath(const char * path) { lt_slock(); return lt_sunlock(lt_dlsetsearchpath_locked(path)); } int lt_dladdsearchdir(const char * path) { int ret; const char * ch; char * buf; off_t alen; off_t plen; if (path[0] != '/') return 1; for (ch=path; *ch; ch++) if (*ch == ':') return 1; lt_slock(); alen = strlen(path); plen = strlen(lt_upath); /* no allocation needed? */ 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_sunlock(0); } /* (allocation needed) */ if (!(buf = malloc(plen + 1 + alen + 1))) return lt_sunlock(1); sprintf(buf,"%s:%s",lt_upath,path); ret = lt_dlsetsearchpath_locked(buf); free(buf); return lt_sunlock(ret); } 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 (path[0] != '/') return 1; if (!mark) return lt_dladdsearchdir(path); for (ch=path; *ch; ch++) if (*ch == ':') return 1; lt_slock(); alen = strlen(path); plen = strlen(lt_upath); if ((mark < lt_upath) || (mark >= <_upath[plen])) return lt_sunlock(1); if ((mark > lt_upath) && (mark[-1] != ':')) return lt_sunlock(1); mark = <_vpath[mark - lt_upath]; /* no allocation needed? */ 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); } } } /* (allocation needed) */ if (!(buf = malloc(plen + 1 + alen + 1))) return lt_sunlock(1); 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); } 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 -1; 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 (-1); } memcpy(&path[mlen],*pext,elen); path[mlen+elen] = 0; fdmod = openat(fdat,path,O_EXEC|O_CLOEXEC,0); if (fdmod >= 0) { if (mpath) { plen = strlen(*ppath); plen += mlen + 1 + elen + 1; if (!(*mpath = malloc(plen))) { close(fdat); close(fdmod); return (-1); } sprintf(*mpath,"%s/%s",*ppath,path); } close(fdat); return fdmod; } } close(fdat); } } return -1; } int lt_dlpathopen(const char * module, const char ** extv) { lt_slock(); return lt_sunlock(lt_dlpathopen_locked(module,extv,0)); } 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; /* path open */ if ((fdmod = lt_dlpathopen_locked(module,extv,&mpath)) < 0) return 0; close(fdmod); /* entry alloc */ if (lt_modv_next == lt_modv_cap) { if (!(modctx_buf = calloc(64,sizeof(*modctx)))) { free(mpath); return 0; } lt_modv_next = modctx_buf; lt_modv_cap = <_modv_next[64]; } /* dlopen */ if (!(maddr = dlopen(mpath,mode))) { free(mpath); return 0; } /* already dlopen'ed? */ for (modctx=lt_modv_head; modctx; modctx=modctx->mnext) { if (!strcmp(modctx->mpath,mpath)) { free(mpath); modctx->mrefs++; return modctx; } } /* module entry */ modctx = lt_modv_next; modctx->maddr = maddr; modctx->mpath = mpath; modctx->mrefs = 1; lt_modv_next++; /* add to list */ if (lt_modv_tail) { lt_modv_tail->mnext = modctx; lt_modv_tail = modctx; } else { lt_modv_head = modctx; lt_modv_tail = modctx; } /* all done */ 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); 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); return modctx; } struct lt_modctx * lt_dlopenadvise(const char * module, struct lt_modctl * modctl) { (void)modctl; return lt_dlopenext(module); }