/*******************************************************************/
/* slibtool: a skinny 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 * 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 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];
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) && strcmp(tgtpath,deppath)) {
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);
}
mark = relapath;
} else if (mark > *pline) {
strcpy(relapath,"-L.");
mark = relapath;
}
ret = mark ? slbt_dprintf(deps,"%s\n",mark) : (-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;
}
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 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);
fdyndep = !fstatat(fdcwd,depfile,&st,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 = '.';
}
/* [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 (!farchive) {
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);
}
ret = ((deplib[0] == '-')
&& (deplib[1] == 'L')
&& (deplib[2] != '/'))
? slbt_dprintf(
deps,"-L%s%s/%s",
deppref,reladir,&deplib[2])
: 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;
}