/*******************************************************************/
/* 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 <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <slibtool/slibtool.h>
#include "slibtool_driver_impl.h"
#include "slibtool_linkcmd_impl.h"
#include "slibtool_errinfo_impl.h"
#include "slibtool_ar_impl.h"
#define SLBT_ECTX_LIB_EXTRAS 24
#define SLBT_ECTX_SPARE_PTRS 16
static int slbt_ectx_free_exec_ctx_impl(
struct slbt_exec_ctx_impl * ictx,
int status);
static size_t slbt_parse_comma_separated_flags(
const char * str,
int * argc)
{
const char * ch;
for (ch=str; *ch; ch++)
if (*ch == ',')
(*argc)++;
return ch - str;
}
static char * slbt_source_file(char ** argv)
{
char ** parg;
char * ch;
for (parg=argv; *parg; parg++)
if ((ch = strrchr(*parg,'.')))
if ((!(strcmp(++ch,"s")))
|| (!(strcmp(ch,"S")))
|| (!(strcmp(ch,"asm")))
|| (!(strcmp(ch,"c")))
|| (!(strcmp(ch,"cc")))
|| (!(strcmp(ch,"cpp")))
|| (!(strcmp(ch,"cxx"))))
return *parg;
return 0;
}
static int slbt_exec_ctx_tool_argc(char ** argv)
{
char ** parg;
if (!(parg = argv))
return 0;
for (; *parg; )
parg++;
return parg - argv + 1;
}
static struct slbt_exec_ctx_impl * slbt_exec_ctx_alloc(
const struct slbt_driver_ctx * dctx)
{
struct slbt_driver_ctx_impl * ctx;
struct slbt_exec_ctx_impl * ictx;
size_t size;
size_t slen;
int argc;
char * args;
char * shadow;
char * csrc;
char ** parg;
struct slbt_archive_ctx ** dlactxv;
size_t ndlopen;
/* internal driver context for host-specific tool arguments */
ctx = slbt_get_driver_ictx(dctx);
/* initial buffer size (cargv, -Wc) */
argc = 0;
csrc = 0;
size = 0;
for (parg=dctx->cctx->cargv; *parg; parg++, argc++) {
if (!(strncmp("-Wc,",*parg,4))) {
size += slbt_parse_comma_separated_flags(
&(*parg)[4],&argc) + 1;
} else {
size += strlen(*parg) + 1;
}
}
/* argument transformation: additive, worst case scenario */
size += argc * (strlen(".lo") + 1);
size += argc * (strlen(".libs/") + 1);
size += argc * (strlen(".0000.0000.0000") + 1);
/* buffer size (csrc, ldirname, lbasename, lobjname, aobjname, etc.) */
if (dctx->cctx->libname) {
slen = strlen(dctx->cctx->libname);
size += (strlen(".slibtool.expsyms.extension") + slen + 1) * SLBT_ECTX_LIB_EXTRAS;
} else if (dctx->cctx->output) {
slen = strlen(dctx->cctx->output);
size += (strlen(".slibtool.expsyms.extension") + slen + 1) * SLBT_ECTX_LIB_EXTRAS;
} else if ((csrc = slbt_source_file(dctx->cctx->cargv))) {
slen = strlen(csrc);
size += (strlen(".slibtool.expsyms.extension") + slen + 1) * SLBT_ECTX_LIB_EXTRAS;
}
/* string buffers: args, shadow */
if (!(args = malloc(size)))
return 0;
if (!(shadow = malloc(size))) {
free(args);
return 0;
}
/* tool-specific argv: to simplify matters, be additive */
argc += slbt_exec_ctx_tool_argc(ctx->host.ar_argv);
argc += slbt_exec_ctx_tool_argc(ctx->host.as_argv);
argc += slbt_exec_ctx_tool_argc(ctx->host.nm_argv);
argc += slbt_exec_ctx_tool_argc(ctx->host.ranlib_argv);
argc += slbt_exec_ctx_tool_argc(ctx->host.windres_argv);
argc += slbt_exec_ctx_tool_argc(ctx->host.dlltool_argv);
argc += slbt_exec_ctx_tool_argc(ctx->host.mdso_argv);
/* argv transformation: .libs/libfoo.so --> -L.libs -lfoo */
argc *= 2;
/* argv ad-hoc extensions */
argc += SLBT_ECTX_SPARE_PTRS;
/* ctx alloc and vector alloc: argv, xargv, and altv, where we */
/* assume -Wl,--whole-archive arg -Wl,--no-whole-archive; */
/* and also dlargv for compiling dlunit.dlopen.c */
if (!(ictx = calloc(1,sizeof(*ictx)))) {
free(args);
free(shadow);
return 0;
}
if (!(ictx->vbuffer = calloc(6*(argc+1),sizeof(char *)))) {
free(args);
free(shadow);
free(ictx);
}
if ((ndlopen = (slbt_get_driver_ictx(dctx))->ndlopen)) {
if (!(dlactxv = calloc(ndlopen+1,sizeof(*dlactxv)))) {
free(ictx->vbuffer);
free(ictx);
free(args);
free(shadow);
}
ictx->dlactxv = dlactxv;
}
/* all ready */
ictx->dctx = dctx;
ictx->args = args;
ictx->argc = argc;
ictx->size = size;
ictx->shadow = shadow;
ictx->ctx.csrc = csrc;
ictx->fdwrapper = (-1);
ictx->ctx.envp = slbt_driver_envp(dctx);
return ictx;
}
int slbt_ectx_get_exec_ctx(
const struct slbt_driver_ctx * dctx,
struct slbt_exec_ctx ** ectx)
{
struct slbt_exec_ctx_impl * ictx;
struct slbt_driver_ctx_impl * idctx;
char ** parg;
char ** src;
char ** dst;
char * ch;
char * mark;
const char * dmark;
char * slash;
char * arname;
struct slbt_archive_ctx ** dlactxv;
const char ** dlopenv;
const char * arprefix;
const char * dsoprefix;
const char * impprefix;
const char * ref;
int i;
/* internal driver context */
idctx = slbt_get_driver_ictx(dctx);
/* alloc */
if (!(ictx = slbt_exec_ctx_alloc(dctx)))
return SLBT_NESTED_ERROR(dctx);
/* init with guard for later .lo check */
ch = ictx->args + strlen(".lo");
ictx->ctx.argv = ictx->vbuffer;
ictx->ctx.xargv = &ictx->ctx.argv [ictx->argc + 1];
ictx->ctx.altv = &ictx->ctx.xargv[ictx->argc + 1];
ictx->dlargv = &ictx->ctx.altv [ictx->argc + 1];
/* <compiler> */
ictx->ctx.compiler = dctx->cctx->cargv[0];
ictx->ctx.cargv = ictx->ctx.argv;
/* ldirname, lbasename */
ref = (dctx->cctx->output)
? dctx->cctx->output
: ictx->ctx.csrc;
if (ref && !ictx->ctx.csrc && (mark = strrchr(ref,'/'))) {
if (!(strncmp(ref,"../",3)))
dmark = 0;
else if (!(strncmp(ref,"./",2)))
dmark = &ref[1];
else
dmark = strchr(ref,'/');
for (; dmark; ) {
if (!(strncmp(dmark,"/./",3))) {
dmark = strchr(&dmark[2],'/');
} else if (!(strncmp(dmark,"/../",4))) {
ictx->ctx.ldirdepth = -1;
dmark = 0;
} else {
for (; *dmark=='/'; )
dmark++;
ictx->ctx.ldirdepth++;
dmark = strchr(dmark,'/');
}
}
ictx->ctx.ldirname = ch;
strcpy(ch,ref);
ch += mark - ref;
ch += sprintf(ch,"%s","/.libs/");
ch++;
ictx->ctx.lbasename = ch;
ch += sprintf(ch,"%s",++mark);
ch++;
} else if (ref) {
ictx->ctx.ldirname = ch;
ch += sprintf(ch,"%s",".libs/");
ch++;
ictx->ctx.lbasename = ch;
mark = strrchr(ref,'/');
ch += sprintf(ch,"%s",mark ? ++mark : ref);
ch++;
}
/* lbasename suffix */
if (ref && (dctx->cctx->mode == SLBT_MODE_COMPILE)) {
if ((ch[-4] == '.') && (ch[-3] == 'l') && (ch[-2] == 'o')) {
ch[-3] = 'o';
ch[-2] = 0;
ch--;
} else if (ictx->ctx.csrc) {
if ((mark = strrchr(ictx->ctx.lbasename,'.'))) {
ch = mark;
*++ch = 'o';
*++ch = 0;
ch++;
}
}
}
/* cargv, -Wc */
for (i=0, parg=dctx->cctx->cargv; *parg; parg++, ch++) {
if (!(strncmp("-Wc,",*parg,4))) {
strcpy(ch,&(*parg)[4]);
ictx->ctx.argv[i++] = ch;
for (; *ch; ch++)
if (*ch == ',') {
*ch++ = 0;
ictx->ctx.argv[i++] = ch;
}
} else {
ictx->ctx.argv[i++] = ch;
ch += sprintf(ch,"%s",*parg);
ch += strlen(".libs/-L-l");
}
}
/* placeholders for -DPIC, -fPIC, -c, -o, <output> */
ictx->ctx.dpic = &ictx->ctx.argv[i++];
ictx->ctx.fpic = &ictx->ctx.argv[i++];
ictx->ctx.cass = &ictx->ctx.argv[i++];
ictx->ctx.noundef = &ictx->ctx.argv[i++];
ictx->ctx.soname = &ictx->ctx.argv[i++];
ictx->ctx.lsoname = &ictx->ctx.argv[i++];
ictx->ctx.symdefs = &ictx->ctx.argv[i++];
ictx->ctx.symfile = &ictx->ctx.argv[i++];
ictx->ctx.explarg = &ictx->ctx.argv[i++];
ictx->ctx.expsyms = &ictx->ctx.argv[i++];
ictx->ctx.lout[0] = &ictx->ctx.argv[i++];
ictx->ctx.lout[1] = &ictx->ctx.argv[i++];
ictx->ctx.rpath[0] = &ictx->ctx.argv[i++];
ictx->ctx.rpath[1] = &ictx->ctx.argv[i++];
ictx->ctx.sentinel= &ictx->ctx.argv[i++];
slbt_reset_placeholders(&ictx->ctx);
/* dsoprefix */
if (dctx->cctx->settings.dsoprefix) {
ictx->dsoprefix = ch;
strcpy(ch,dctx->cctx->settings.dsoprefix);
ch += strlen(ch) + 1;
}
/* output file name */
if (ref && ((dctx->cctx->mode == SLBT_MODE_COMPILE))) {
*ictx->ctx.lout[0] = "-o";
*ictx->ctx.lout[1] = ch;
ictx->ctx.lobjname = ch;
ch += sprintf(ch,"%s%s",
ictx->ctx.ldirname,
ictx->ctx.lbasename)
+ 1;
ictx->ctx.aobjname = ch;
ch += sprintf(ch,"%s",ictx->ctx.ldirname);
ch -= strlen(".libs/");
ch += sprintf(ch,"%s",
ictx->ctx.lbasename)
+ 1;
ictx->ctx.ltobjname = ch;
strcpy(ch,ictx->ctx.aobjname);
if ((mark = strrchr(ch,'.')))
ch = mark + sprintf(mark,"%s",".lo")
+ 1;
}
/* linking: arfilename, lafilename, laifilename, dsobasename, dsofilename, mapfilename */
if (dctx->cctx->mode == SLBT_MODE_LINK && dctx->cctx->libname) {
/* arprefix, dsoprefix */
if (dctx->cctx->drvflags & SLBT_DRIVER_MODULE) {
ictx->ctx.sonameprefix = "";
arprefix = "";
dsoprefix = "";
impprefix = "";
} else {
ictx->ctx.sonameprefix = ictx->dsoprefix;
arprefix = dctx->cctx->settings.arprefix;
dsoprefix = dctx->cctx->settings.dsoprefix;
impprefix = dctx->cctx->settings.impprefix;
}
/* arfilename */
ictx->ctx.arfilename = ch;
ch += sprintf(ch,"%s%s%s%s",
ictx->ctx.ldirname,
arprefix,
dctx->cctx->libname,
dctx->cctx->settings.arsuffix);
ch++;
/* lafilename */
ictx->ctx.lafilename = ch;
ch += sprintf(ch,"%s%s%s.la",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname);
ch++;
/* laifilename */
ictx->ctx.laifilename = ch;
ch += sprintf(ch,"%s%s%s.lai",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname);
ch++;
/* dsobasename */
ictx->ctx.dsobasename = ch;
ch += sprintf(ch,"%s%s%s",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname);
ch++;
/* dsofilename */
ictx->ctx.dsofilename = ch;
ch += sprintf(ch,"%s%s%s%s",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->settings.dsosuffix);
ch++;
/* mapfilename */
ictx->ctx.mapfilename = ch;
ch += sprintf(ch,"%s%s%s%s%s%s",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->release ? "-" : "",
dctx->cctx->release ? dctx->cctx->release : "",
dctx->cctx->settings.mapsuffix);
ch++;
/* deffilename */
ictx->ctx.deffilename = ch;
ch += sprintf(ch,"%s%s%s%s%s%s.def",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->release ? "-" : "",
dctx->cctx->release ? dctx->cctx->release : "",
dctx->cctx->settings.dsosuffix);
ch++;
/* rpathfilename */
ictx->ctx.rpathfilename = ch;
ch += sprintf(ch,"%s%s%s%s.slibtool.rpath",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->settings.dsosuffix);
ch++;
/* default implib file name */
ictx->ctx.dimpfilename = ch;
ch += sprintf(ch,"%s%s%s%s",
ictx->ctx.ldirname,
impprefix,
dctx->cctx->libname,
dctx->cctx->settings.impsuffix);
ch++;
/* primary implib file name */
ictx->ctx.pimpfilename = ch;
ch += sprintf(ch,"%s%s%s%s%s.%d%s",
ictx->ctx.ldirname,
impprefix,
dctx->cctx->libname,
dctx->cctx->release ? "-" : "",
dctx->cctx->release ? dctx->cctx->release : "",
dctx->cctx->verinfo.major,
dctx->cctx->settings.impsuffix);
ch++;
/* versioned implib file name */
ictx->ctx.vimpfilename = ch;
ch += sprintf(ch,"%s%s%s%s%s.%d.%d.%d%s",
ictx->ctx.ldirname,
impprefix,
dctx->cctx->libname,
dctx->cctx->release ? "-" : "",
dctx->cctx->release ? dctx->cctx->release : "",
dctx->cctx->verinfo.major,
dctx->cctx->verinfo.minor,
dctx->cctx->verinfo.revision,
dctx->cctx->settings.impsuffix);
ch++;
/* relfilename */
if (dctx->cctx->release) {
ictx->ctx.relfilename = ch;
ch += dctx->cctx->verinfo.verinfo
? sprintf(ch,"%s%s%s-%s%s.%d.%d.%d%s",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->release,
dctx->cctx->settings.osdsuffix,
dctx->cctx->verinfo.major,
dctx->cctx->verinfo.minor,
dctx->cctx->verinfo.revision,
dctx->cctx->settings.osdfussix)
: sprintf(ch,"%s%s%s-%s%s",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->release,
dctx->cctx->settings.dsosuffix);
ch++;
}
/* dsorellnkname */
if (dctx->cctx->release) {
ictx->ctx.dsorellnkname = ch;
ch += sprintf(ch,"%s%s%s-%s%s",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname,
dctx->cctx->release,
dctx->cctx->settings.dsosuffix);
ch++;
}
/* dlopensrc, dlopenobj */
if (idctx->ndlopen) {
ictx->ctx.dlopensrc = ch;
ch += sprintf(ch,"%s%s%s.dlopen.c",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname);
ch++;
ictx->ctx.dlopenobj = ch;
ch += sprintf(ch,"%s%s%s.dlopen.o",
ictx->ctx.ldirname,
dsoprefix,
dctx->cctx->libname);
ch++;
ictx->ctx.dlunit = ch;
ch += sprintf(ch,"%s%s",
dsoprefix,
dctx->cctx->libname);
ch++;
}
}
/* dlopen, dlpreopen */
if ((dlopenv = idctx->dlopenv), (dlactxv = ictx->dlactxv)) {
for (; *dlopenv; ) {
arname = ictx->sbuf;
strcpy(arname,*dlopenv);
slbt_adjust_wrapper_argument(arname,true);
if (slbt_ar_get_archive_ctx(dctx,arname,dlactxv) < 0)
return slbt_ectx_free_exec_ctx_impl(
ictx,
SLBT_NESTED_ERROR(dctx));
if (slbt_ar_update_syminfo(*dlactxv,&ictx->ctx) < 0)
return slbt_ectx_free_exec_ctx_impl(
ictx,
SLBT_NESTED_ERROR(dctx));
dlopenv++;
dlactxv++;
}
if (slbt_ar_create_dlsyms(
ictx->dlactxv,
ictx->ctx.dlunit,
ictx->ctx.dlopensrc,
0644) < 0)
return slbt_ectx_free_exec_ctx_impl(
ictx,
SLBT_NESTED_ERROR(dctx));
}
/* linking: exefilename */
if (dctx->cctx->mode == SLBT_MODE_LINK && !dctx->cctx->libname) {
ictx->ctx.exefilename = ch;
if ((slash = strrchr(dctx->cctx->output,'/'))) {
strcpy(ch,dctx->cctx->output);
mark = ch + (slash - dctx->cctx->output);
sprintf(++mark,".libs/%s",++slash);
ch += strlen(ch) + 1;
} else
ch += sprintf(ch,".libs/%s",dctx->cctx->output) + 1;
}
/* vector of exported symbols (raw input via -export-symbols) */
if (dctx->cctx->expsyms)
if (slbt_lib_get_symlist_ctx(
dctx,dctx->cctx->expsyms,
&ictx->sctx) < 0)
return SLBT_NESTED_ERROR(dctx);
/* argument strings shadow copy */
memcpy(ictx->shadow,ictx->args,ictx->size);
/* compile mode: argument vector shadow copy */
if (dctx->cctx->mode == SLBT_MODE_COMPILE)
for (src=ictx->ctx.argv, dst=ictx->ctx.xargv; *src; src++, dst++)
*dst = *src;
/* save the full vector's lout, mout */
ictx->lout[0] = ictx->ctx.lout[0];
ictx->lout[1] = ictx->ctx.lout[1];
ictx->mout[0] = ictx->ctx.mout[0];
ictx->mout[1] = ictx->ctx.mout[1];
/* all done */
*ectx = &ictx->ctx;
return 0;
}
static int slbt_ectx_free_exec_ctx_impl(
struct slbt_exec_ctx_impl * ictx,
int status)
{
struct slbt_archive_ctx ** dlactxv;
if (ictx->sctx)
slbt_lib_free_symlist_ctx(ictx->sctx);
if (ictx->fdwrapper >= 0)
close(ictx->fdwrapper);
if (ictx->dlactxv) {
for (dlactxv=ictx->dlactxv; *dlactxv; dlactxv++)
slbt_ar_free_archive_ctx(*dlactxv);
free(ictx->dlactxv);
}
free(ictx->args);
free(ictx->shadow);
free(ictx->vbuffer);
free (ictx);
return status;
}
void slbt_ectx_free_exec_ctx(struct slbt_exec_ctx * ctx)
{
struct slbt_exec_ctx_impl * ictx;
uintptr_t addr;
if (ctx) {
addr = (uintptr_t)ctx - offsetof(struct slbt_exec_ctx_impl,ctx);
ictx = (struct slbt_exec_ctx_impl *)addr;
slbt_ectx_free_exec_ctx_impl(ictx,0);
}
}
void slbt_ectx_reset_arguments(struct slbt_exec_ctx * ectx)
{
struct slbt_exec_ctx_impl * ictx;
uintptr_t addr;
addr = (uintptr_t)ectx - offsetof(struct slbt_exec_ctx_impl,ctx);
ictx = (struct slbt_exec_ctx_impl *)addr;
memcpy(ictx->args,ictx->shadow,ictx->size);
}
void slbt_ectx_reset_argvector(struct slbt_exec_ctx * ectx)
{
struct slbt_exec_ctx_impl * ictx;
uintptr_t addr;
char ** src;
char ** dst;
addr = (uintptr_t)ectx - offsetof(struct slbt_exec_ctx_impl,ctx);
ictx = (struct slbt_exec_ctx_impl *)addr;
for (src=ectx->xargv, dst=ectx->argv; *src; src++, dst++)
*dst = *src;
*dst = 0;
ectx->lout[0] = ictx->lout[0];
ectx->lout[1] = ictx->lout[1];
ectx->mout[0] = ictx->mout[0];
ectx->mout[1] = ictx->mout[1];
}
slbt_hidden void slbt_reset_placeholders(struct slbt_exec_ctx * ectx)
{
struct slbt_exec_ctx_impl * ictx;
ictx = slbt_get_exec_ictx(ectx);
if (ictx->lout[0]) {
ectx->lout[0] = ictx->lout[0];
ectx->lout[1] = ictx->lout[1];
}
*ectx->dpic = "-USLIBTOOL_PLACEHOLDER_DPIC";
*ectx->fpic = "-USLIBTOOL_PLACEHOLDER_FPIC";
*ectx->cass = "-USLIBTOOL_PLACEHOLDER_COMPILE_ASSEMBLE";
*ectx->noundef = "-USLIBTOOL_PLACEHOLDER_NO_UNDEFINED";
*ectx->soname = "-USLIBTOOL_PLACEHOLDER_SONAME";
*ectx->lsoname = "-USLIBTOOL_PLACEHOLDER_LSONAME";
*ectx->symdefs = "-USLIBTOOL_PLACEHOLDER_SYMDEF_SWITCH";
*ectx->symfile = "-USLIBTOOL_PLACEHOLDER_SYMDEF_FILE";
*ectx->explarg = "-USLIBTOOL_PLACEHOLDER_EXPSYMS_SWITCH";
*ectx->expsyms = "-USLIBTOOL_PLACEHOLDER_EXPSYMS_FILE";
*ectx->lout[0] = "-USLIBTOOL_PLACEHOLDER_OUTPUT_SWITCH";
*ectx->lout[1] = "-USLIBTOOL_PLACEHOLDER_OUTPUT_FILE";
*ectx->rpath[0] = "-USLIBTOOL_PLACEHOLDER_RPATH_SWITCH";
*ectx->rpath[1] = "-USLIBTOOL_PLACEHOLDER_RPATH_DIR";
ectx->mout[0] = 0;
ectx->mout[1] = 0;
*ectx->sentinel= 0;
}
slbt_hidden void slbt_disable_placeholders(struct slbt_exec_ctx * ectx)
{
*ectx->dpic = 0;
*ectx->fpic = 0;
*ectx->cass = 0;
*ectx->noundef = 0;
*ectx->soname = 0;
*ectx->lsoname = 0;
*ectx->symdefs = 0;
*ectx->symfile = 0;
*ectx->explarg = 0;
*ectx->expsyms = 0;
*ectx->lout[0] = 0;
*ectx->lout[1] = 0;
*ectx->rpath[0] = 0;
*ectx->rpath[1] = 0;
*ectx->sentinel= 0;
}