diff options
Diffstat (limited to 'src/logic/linkcmd/slbt_linkcmd_deps.c')
-rw-r--r-- | src/logic/linkcmd/slbt_linkcmd_deps.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/src/logic/linkcmd/slbt_linkcmd_deps.c b/src/logic/linkcmd/slbt_linkcmd_deps.c new file mode 100644 index 0000000..19ed716 --- /dev/null +++ b/src/logic/linkcmd/slbt_linkcmd_deps.c @@ -0,0 +1,788 @@ +/*******************************************************************/ +/* slibtool: a strong libtool implementation, written in C */ +/* Copyright (C) 2016--2024 SysDeer Technologies, LLC */ +/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */ +/*******************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> + +#include <slibtool/slibtool.h> +#include "slibtool_driver_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_linkcmd_impl.h" +#include "slibtool_mapfile_impl.h" +#include "slibtool_metafile_impl.h" +#include "slibtool_realpath_impl.h" +#include "slibtool_snprintf_impl.h" +#include "slibtool_visibility_impl.h" + +slbt_hidden int slbt_get_deps_meta( + const struct slbt_driver_ctx * dctx, + char * libfilename, + int fexternal, + struct slbt_deps_meta * depsmeta) +{ + int fdcwd; + char * ch; + char * cap; + char * base; + size_t libexlen; + struct stat st; + struct slbt_map_info * mapinfo; + char depfile[PATH_MAX]; + + /* fdcwd */ + fdcwd = slbt_driver_fdcwd(dctx); + + /* -rpath */ + if (slbt_snprintf(depfile,sizeof(depfile), + "%s.slibtool.rpath", + libfilename) < 0) + return SLBT_BUFFER_ERROR(dctx); + + /* -Wl,%s */ + if (!fstatat(fdcwd,depfile,&st,AT_SYMLINK_NOFOLLOW)) { + depsmeta->infolen += st.st_size + 4; + depsmeta->infolen++; + } + + /* .deps */ + if (slbt_snprintf(depfile,sizeof(depfile), + "%s.slibtool.deps", + libfilename) < 0) + return SLBT_BUFFER_ERROR(dctx); + + /* mapinfo */ + if (!(mapinfo = slbt_map_file(fdcwd,depfile,SLBT_MAP_INPUT))) + return (fexternal && (errno == ENOENT)) + ? 0 : SLBT_SYSTEM_ERROR(dctx,depfile); + + /* copied length */ + depsmeta->infolen += mapinfo->size; + depsmeta->infolen++; + + /* libexlen */ + libexlen = (base = strrchr(libfilename,'/')) + ? strlen(depfile) + 2 + (base - libfilename) + : strlen(depfile) + 2; + + /* iterate */ + ch = mapinfo->addr; + cap = mapinfo->cap; + + for (; ch<cap; ) { + if (*ch++ == '\n') { + depsmeta->infolen += libexlen; + depsmeta->depscnt++; + } + } + + slbt_unmap_file(mapinfo); + + return 0; +} + + +static int slbt_exec_link_normalize_dep_file( + const struct slbt_driver_ctx * dctx, + int deps, + char (*depfile)[PATH_MAX]) +{ + int ret; + int fdcwd; + int fdtgt; + char * dot; + char * slash; + const char * base; + const char * mark; + struct slbt_txtfile_ctx * tctx; + const char ** pline; + char * tgtmark; + char * depmark; + char * relmark; + char * tgtnext; + char * depnext; + char tgtdir [PATH_MAX]; + char tgtpath [PATH_MAX]; + char deppath [PATH_MAX]; + char pathbuf [PATH_MAX]; + char depsbuf [PATH_MAX]; + char basebuf [PATH_MAX]; + char relapath[PATH_MAX]; + + /* fdcwd */ + fdcwd = slbt_driver_fdcwd(dctx); + + /* first-pass dependency file */ + if (slbt_impl_get_txtfile_ctx(dctx,*depfile,deps,&tctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + /* second-pass dependency file */ + dot = strrchr(*depfile,'.'); + strcpy(dot,".tmp2"); + + if ((deps = openat(fdcwd,*depfile,O_RDWR|O_CREAT|O_TRUNC,0644)) < 0) { + slbt_lib_free_txtfile_ctx(tctx); + return SLBT_SYSTEM_ERROR(dctx,depfile); + } + + /* fdtgt */ + strcpy(tgtdir,*depfile); + + if (!(slash = strrchr(tgtdir,'/'))) + slash = tgtdir; + + slash[0] = '\0'; + + if ((slash = strrchr(tgtdir,'/'))) { + slash[0] = '\0'; + slash++; + } else { + slash = tgtdir; + slash[0] = '.'; + slash[1] = '/'; + slash[2] = '\0'; + } + + if ((slash > tgtdir) && strcmp(slash,".libs")) { + close(deps); + slbt_lib_free_txtfile_ctx(tctx); + + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + } + + if ((fdtgt = openat(fdcwd,tgtdir,O_DIRECTORY|O_CLOEXEC,0)) < 0) { + close(deps); + slbt_lib_free_txtfile_ctx(tctx); + + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + } + + if (slbt_realpath(fdcwd,tgtdir,0,tgtpath,sizeof(tgtpath)) < 0) { + close(fdtgt); + close(deps); + slbt_lib_free_txtfile_ctx(tctx); + + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + } + + strcpy(pathbuf,tgtpath); + + /* normalize dependency lines as needed */ + for (pline=tctx->txtlinev; *pline; pline++) { + if ((mark = *pline)) { + if ((mark[0] == '-') && (mark[1] == 'L')) { + mark = &mark[2]; + base = 0; + + } else if ((mark[0] == ':') && (mark[1] == ':')) { + mark = &mark[2]; + base = mark; + } + + if ((mark > *pline) && (mark[0] == '/')) + mark = *pline; + } + + if (mark > *pline) { + if (slbt_realpath( + fdtgt,mark,0,deppath, + sizeof(deppath)) < 0) + mark = *pline; + + else if ((tgtpath[0] != '/') || (deppath[0] != '/')) + mark = 0; + + else + strcpy(depsbuf,deppath); + + if ((mark > *pline) && base) { + slash = strrchr(deppath,'/'); + *slash = '\0'; + + slash = strrchr(deppath,'/'); + *slash = '\0'; + + base = basebuf; + slash = &depsbuf[slash - deppath]; + + strcpy(basebuf,++slash); + strcpy(depsbuf,deppath); + } + } + + if (mark > *pline) { + tgtmark = tgtpath; + depmark = deppath; + + tgtnext = strchr(tgtmark,'/'); + depnext = strchr(depmark,'/'); + + while (tgtnext && depnext) { + *tgtnext = '\0'; + *depnext = '\0'; + + if (strcmp(tgtmark,depmark)) { + tgtnext = 0; + depnext = 0; + } else { + tgtmark = &tgtnext[1]; + depmark = &depnext[1]; + + if (*tgtmark && *depmark) { + tgtnext = strchr(tgtmark,'/'); + depnext = strchr(depmark,'/'); + } else { + tgtnext = 0; + depnext = 0; + } + } + } + + strcpy(tgtmark,&pathbuf[tgtmark-tgtpath]); + strcpy(depmark,&depsbuf[depmark-deppath]); + + if ((tgtmark - tgtpath) == (depmark - deppath)) { + mark = &depmark[strlen(tgtmark)]; + + if ((mark[0] == '/') && !strncmp(tgtmark,depmark,mark-depmark)) + sprintf(relapath,"-L.%s",mark); + else + mark = relapath; + } else { + mark = relapath; + } + + if (mark == relapath) { + relmark = relapath; + relmark += sprintf(relapath,"-L../"); + + while ((tgtnext = strchr(tgtmark,'/'))) { + tgtmark = &tgtnext[1]; + relmark += sprintf(relmark,"../"); + } + + strcpy(relmark,depmark); + } + + if (base) { + relapath[0] = ':'; + relapath[1] = ':'; + } + + mark = relapath; + } + + if ((mark == relapath) && base) { + ret = slbt_dprintf(deps,"%s/%s\n",mark,base); + + } else if (mark) { + ret = slbt_dprintf(deps,"%s\n",mark); + + } else { + ret = (-1); + } + + if (ret < 0) { + close(deps); + close(fdtgt); + slbt_lib_free_txtfile_ctx(tctx); + + return mark + ? SLBT_SYSTEM_ERROR(dctx,0) + : SLBT_NESTED_ERROR(dctx); + } + + if (mark == relapath) + strcpy(tgtpath,pathbuf); + } + + close(fdtgt); + slbt_lib_free_txtfile_ctx(tctx); + + return deps; +} + + +static int slbt_exec_link_compact_dep_file( + const struct slbt_driver_ctx * dctx, + int deps, + char (*depfile)[PATH_MAX]) +{ + int fdcwd; + struct slbt_txtfile_ctx * tctx; + const char ** pline; + const char ** pcomp; + const char * depline; + char * dot; + + /* fdcwd */ + fdcwd = slbt_driver_fdcwd(dctx); + + /* second-pass dependency file */ + if (slbt_impl_get_txtfile_ctx(dctx,*depfile,deps,&tctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + /* second-pass dependency file */ + dot = strrchr(*depfile,'.'); + dot[0] = '\0'; + + if ((deps = openat(fdcwd,*depfile,O_RDWR|O_CREAT|O_TRUNC,0644)) < 0) { + slbt_lib_free_txtfile_ctx(tctx); + return SLBT_SYSTEM_ERROR(dctx,depfile); + } + + /* iterate, only write unique entries */ + for (pline=tctx->txtlinev; *pline; pline++) { + depline = *pline; + + if ((depline[0] == '-') && (depline[1] == 'L')) + for (pcomp=tctx->txtlinev; depline && pcomp<pline; pcomp++) + if (!strcmp(*pcomp,depline)) + depline = 0; + + if (depline && (slbt_dprintf(deps,"%s\n",depline) < 0)) { + close(deps); + slbt_lib_free_txtfile_ctx(tctx); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + slbt_lib_free_txtfile_ctx(tctx); + + return deps; +} + + +static bool slbt_archive_is_convenience_library(int fdcwd, const char * arpath) +{ + int fd; + char laipath[PATH_MAX]; + char * dot; + + strcpy(laipath,arpath); + dot = strrchr(laipath,'.'); + strcpy(dot,".lai"); + + if ((fd = openat(fdcwd,laipath,O_RDONLY,0)) >= 0) { + close(fd); + return false; + } + + return true; +} + + +slbt_hidden int slbt_exec_link_create_dep_file( + const struct slbt_driver_ctx * dctx, + struct slbt_exec_ctx * ectx, + char ** altv, + const char * libfilename, + bool farchive) +{ + int ret; + int deps; + int fdtmp; + int slen; + int fdcwd; + char ** parg; + char * popt; + char * plib; + char * path; + char * mark; + char * base; + size_t size; + bool fdep; + char deplib [PATH_MAX]; + char deppref[PATH_MAX]; + char reladir[PATH_MAX]; + char depfile[PATH_MAX]; + char pathbuf[PATH_MAX]; + struct stat st; + int ldepth; + int fardep; + int fdyndep; + struct slbt_map_info * mapinfo; + bool is_reladir; + + /* fdcwd */ + fdcwd = slbt_driver_fdcwd(dctx); + + /* depfile */ + if (slbt_snprintf(depfile,sizeof(depfile), + "%s.slibtool.deps.tmp1", + libfilename) < 0) + return SLBT_BUFFER_ERROR(dctx); + + strcpy(pathbuf,depfile); + + /* dependency prefix */ + if (depfile[0] == '/') { + deppref[0] = '\0'; + } else { + if ((mark = strrchr(depfile,'/'))) + *mark = 0; + + if (!mark) + mark = depfile; + + if (slbt_realpath(fdcwd,depfile,0,reladir,sizeof(reladir)) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (slbt_realpath(fdcwd,"./",0,deppref,sizeof(deppref)) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (mark > depfile) + *mark = '/'; + + mark = &reladir[strlen(deppref)]; + *mark++ = '\0'; + + if (strcmp(reladir,deppref)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + if ((base = strrchr(mark,'/'))) + base++; + + if (!base) + base = mark; + + if (strcmp(base,".libs")) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + base = mark; + mark = deppref; + + for (; base; ) { + if ((base = strchr(base,'/'))) { + mark += sprintf(mark,"../"); + base++; + } + } + + *mark = '\0'; + } + + /* deps */ + if ((deps = openat(fdcwd,depfile,O_RDWR|O_CREAT|O_TRUNC,0644)) < 0) + return SLBT_SYSTEM_ERROR(dctx,depfile); + + /* informational header */ + if (slbt_realpath(fdcwd,"./",0,reladir,sizeof(reladir)) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (slbt_dprintf(deps, + "# makefile target: %s\n" + "# slibtool target: %s\n" + "# cprocess fdcwd: %s\n", + dctx->cctx->output, + libfilename, + reladir) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + fdep = 0; + + /* iterate */ + for (parg=altv; *parg; parg++) { + popt = 0; + plib = 0; + path = 0; + mapinfo = 0; + + if (!strncmp(*parg,"-l",2)) { + if (fdep) { + if (slbt_dprintf( + deps, + "#\n# makefile target: %s\n", + dctx->cctx->output) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + fdep = false; + } + + popt = *parg; + plib = popt + 2; + + } else if (!strncmp(*parg,"-L",2)) { + if (fdep) { + if (slbt_dprintf( + deps, + "#\n# makefile target: %s\n", + dctx->cctx->output) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + fdep = false; + } + + popt = *parg; + path = popt + 2; + + } else if (!strncmp(*parg,"-f",2)) { + (void)0; + + } else if ((popt = strrchr(*parg,'.')) && !strcmp(popt,".la")) { + /* import dependency list */ + fdep = true; + + if ((base = strrchr(*parg,'/'))) + base++; + else + base = *parg; + + /* [relative .la directory] */ + if (base > *parg) { + slen = slbt_snprintf( + reladir, + sizeof(reladir), + "%s",*parg); + + if (slen < 0) { + close(deps); + return SLBT_BUFFER_ERROR(dctx); + } + + is_reladir = true; + reladir[base - *parg - 1] = 0; + } else { + is_reladir = false; + reladir[0] = '.'; + reladir[1] = 0; + } + + + /* dynamic library dependency? */ + strcpy(depfile,*parg); + mark = depfile + (base - *parg); + size = sizeof(depfile) - (base - *parg); + slen = slbt_snprintf(mark,size,".libs/%s",base); + + if (slen < 0) { + close(deps); + return SLBT_BUFFER_ERROR(dctx); + } + + mark = strrchr(mark,'.'); + strcpy(mark,dctx->cctx->settings.dsosuffix); + + fardep = 0; + fdyndep = !fstatat(fdcwd,depfile,&st,0); + + if (fdyndep && farchive) { + mark = strrchr(mark,'.'); + strcpy(mark,dctx->cctx->settings.arsuffix); + + if (fstatat(fdcwd,depfile,&st,0) < 0) { + strcpy(mark,dctx->cctx->settings.dsosuffix); + } else { + fdyndep = 0; + } + } + + /* [-L... as needed] */ + if (fdyndep && (ectx->ldirdepth >= 0)) { + if (slbt_dprintf(deps,"-L") < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + + for (ldepth=ectx->ldirdepth; ldepth; ldepth--) { + if (slbt_dprintf(deps,"../") < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (slbt_dprintf(deps,"%s%s.libs\n", + (is_reladir ? reladir : ""), + (is_reladir ? "/" : "")) < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + /* -ldeplib */ + if (fdyndep) { + *popt = 0; + mark = base; + mark += strlen(dctx->cctx->settings.dsoprefix); + + if (slbt_dprintf(deps,"-l%s\n",mark) < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + + *popt = '.'; + } else { + strcpy(depfile,*parg); + + slbt_adjust_wrapper_argument( + depfile,true, + dctx->cctx->settings.arsuffix); + + + fardep = farchive; + fardep |= !slbt_archive_is_convenience_library(fdcwd,depfile); + } + + if (fardep) { + if (slbt_dprintf(deps,"::") < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + + for (ldepth=ectx->ldirdepth; ldepth; ldepth--) { + if (slbt_dprintf(deps,"../") < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + + if (ectx->ldirdepth >= 0) { + if (slbt_dprintf(deps,"%s\n",depfile) < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } else { + if (slbt_dprintf(deps,"::./%s\n",depfile) < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + } + + /* [open dependency list] */ + strcpy(depfile,*parg); + mark = depfile + (base - *parg); + size = sizeof(depfile) - (base - *parg); + slen = slbt_snprintf(mark,size,".libs/%s",base); + + if (slen < 0) { + close(deps); + return SLBT_BUFFER_ERROR(dctx); + } + + mapinfo = 0; + + mark = strrchr(mark,'.'); + size = sizeof(depfile) - (mark - depfile); + + if (!fardep) { + slen = slbt_snprintf(mark,size, + "%s.slibtool.deps", + dctx->cctx->settings.dsosuffix); + + if (slen < 0) { + close(deps); + return SLBT_BUFFER_ERROR(dctx); + } + + mapinfo = slbt_map_file( + fdcwd,depfile, + SLBT_MAP_INPUT); + + if (!mapinfo && (errno != ENOENT)) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (!mapinfo) { + slen = slbt_snprintf(mark,size, + ".a.slibtool.deps"); + + if (slen < 0) { + close(deps); + return SLBT_BUFFER_ERROR(dctx); + } + + mapinfo = slbt_map_file( + fdcwd,depfile, + SLBT_MAP_INPUT); + + if (!mapinfo) { + strcpy(mark,".a.disabled"); + + if (fstatat(fdcwd,depfile,&st,AT_SYMLINK_NOFOLLOW)) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,depfile); + } + } + } + + /* [-l... as needed] */ + while (mapinfo && (mapinfo->mark < mapinfo->cap)) { + ret = slbt_mapped_readline( + dctx,mapinfo, + deplib,sizeof(deplib)); + + if (ret) { + close(deps); + return SLBT_NESTED_ERROR(dctx); + } + + if ((deplib[0] == '-') && (deplib[1] == 'L') && (deplib[2] != '/')) { + ret = slbt_dprintf( + deps,"-L%s%s/%s", + deppref,reladir,&deplib[2]); + + } else if ((deplib[0] == ':') && (deplib[1] == ':') && (deplib[2] != '/')) { + ret = slbt_dprintf( + deps,"::%s%s/%s", + deppref,reladir,&deplib[2]); + + } else { + ret = slbt_dprintf( + deps,"%s", + deplib); + } + + if (ret < 0) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (mapinfo) + slbt_unmap_file(mapinfo); + } + + if (plib && (slbt_dprintf(deps,"-l%s\n",plib) < 0)) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + + if (path && (slbt_dprintf(deps,"-L%s\n",path) < 0)) { + close(deps); + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if ((fdtmp = slbt_exec_link_normalize_dep_file(dctx,deps,&pathbuf)) < 0) + return SLBT_NESTED_ERROR(dctx); + + close(deps); + + if ((deps = slbt_exec_link_compact_dep_file(dctx,fdtmp,&pathbuf)) < 0) + return SLBT_NESTED_ERROR(dctx); + + close(deps); + close(fdtmp); + + return 0; +} |