Blame src/logic/linkcmd/slbt_linkcmd_executable.c

11c887
/*******************************************************************/
11c887
/*  slibtool: a skinny libtool implementation, written in C        */
49181b
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
11c887
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
11c887
/*******************************************************************/
11c887
11c887
#include <stdlib.h>
11c887
#include <stdio.h>
11c887
#include <string.h>
11c887
#include <fcntl.h>
11c887
#include <errno.h>
11c887
#include <sys/stat.h>
11c887
11c887
#include <slibtool/slibtool.h>
11c887
#include "slibtool_driver_impl.h"
11c887
#include "slibtool_errinfo_impl.h"
11c887
#include "slibtool_linkcmd_impl.h"
11c887
#include "slibtool_mapfile_impl.h"
11c887
#include "slibtool_metafile_impl.h"
d4b2a5
#include "slibtool_realpath_impl.h"
11c887
#include "slibtool_snprintf_impl.h"
11c887
#include "slibtool_symlink_impl.h"
11c887
#include "slibtool_spawn_impl.h"
4b56de
#include "slibtool_visibility_impl.h"
11c887
11c887
static int slbt_linkcmd_exit(
11c887
	struct slbt_deps_meta *	depsmeta,
11c887
	int			ret)
11c887
{
11c887
	if (depsmeta->altv)
11c887
		free(depsmeta->altv);
11c887
11c887
	if (depsmeta->args)
11c887
		free(depsmeta->args);
11c887
11c887
	return ret;
11c887
}
11c887
11c887
static void slbt_emit_fdwrap_dl_path_fixup(
11c887
	char *	cwd,
11c887
	char *	dpfixup,
11c887
	size_t	dpfixup_size,
11c887
	char *	wrapper)
11c887
{
11c887
	char *	p;
11c887
	char *	q;
11c887
	char *	wrapper_dname;
11c887
11c887
	/* obtain cwd-relative directory name of wrapper */
11c887
	for (p=cwd,q=wrapper; *p && *q && (*p==*q); p++,q++)
11c887
		(void)0;
11c887
11c887
	wrapper_dname = (*q == '/') ? (q + 1) : q;
11c887
11c887
	dpfixup[0] = 0; strncat(dpfixup,"${0%/*}",dpfixup_size - 1);
11c887
11c887
	/* append parent directory fixup for each level of depth in wrapper_dname */
11c887
	for (p=wrapper_dname,q=0; *p; ) {
11c887
		if ((p[0] == '.') && (p[1] == '/')) {
11c887
			p++; p++;
11c887
		} else if ((q = strchr(p, '/'))) {
11c887
			strncat(dpfixup,"/..",dpfixup_size-1); p = (q + 1);
11c887
		} else {
11c887
			break;
11c887
		}
11c887
	}
11c887
11c887
	strncat(dpfixup,"/",dpfixup_size-1);
11c887
}
11c887
4b56de
slbt_hidden int slbt_exec_link_create_executable(
11c887
	const struct slbt_driver_ctx *	dctx,
11c887
	struct slbt_exec_ctx *		ectx,
11c887
	const char *			exefilename)
11c887
{
11c887
	int	fdcwd;
11c887
	int	fdwrap;
11c887
	char ** parg;
11c887
	char ** xarg;
11c887
	char *	base;
11c887
	char *	ccwrap;
11c887
	char	cwd    [PATH_MAX];
11c887
	char	dpfixup[PATH_MAX];
11c887
	char	output [PATH_MAX];
11c887
	char	wrapper[PATH_MAX];
11c887
	char	wraplnk[PATH_MAX];
11c887
	bool	fabspath;
11c887
	bool	fpic;
11c887
	const struct slbt_source_version * verinfo;
11c887
	struct slbt_deps_meta depsmeta = {0,0,0,0};
11c887
	struct stat st;
11c887
11c887
	/* initial state */
f3d47a
	slbt_ectx_reset_arguments(ectx);
11c887
11c887
	/* placeholders */
11c887
	slbt_reset_placeholders(ectx);
11c887
11c887
	/* fdcwd */
11c887
	fdcwd = slbt_driver_fdcwd(dctx);
11c887
11c887
	/* fpic */
a3e2d3
	fpic = (dctx->cctx->drvflags & SLBT_DRIVER_SHARED);
11c887
11c887
	/* input argument adjustment */
11c887
	for (parg=ectx->cargv; *parg; parg++)
11c887
		slbt_adjust_object_argument(*parg,fpic,true,fdcwd);
11c887
11c887
	/* linker argument adjustment */
11c887
	for (parg=ectx->cargv, xarg=ectx->xargv; *parg; parg++, xarg++)
11c887
		if (slbt_adjust_linker_argument(
11c887
				dctx,
a3e2d3
				*parg,xarg,fpic,
11c887
				dctx->cctx->settings.dsosuffix,
11c887
				dctx->cctx->settings.arsuffix,
11c887
				&depsmeta) < 0)
11c887
			return SLBT_NESTED_ERROR(dctx);
11c887
11c887
	/* --no-undefined */
11c887
	if (dctx->cctx->drvflags & SLBT_DRIVER_NO_UNDEFINED)
4560b9
		*ectx->noundef = slbt_host_group_is_darwin(dctx)
d2d5af
			? "-Wl,-undefined,error"
d2d5af
			: "-Wl,--no-undefined";
11c887
11c887
	/* executable wrapper: create */
11c887
	if (slbt_snprintf(wrapper,sizeof(wrapper),
11c887
				"%s.wrapper.tmp",
11c887
				dctx->cctx->output) < 0)
11c887
		return SLBT_BUFFER_ERROR(dctx);
11c887
11c887
	if ((fdwrap = openat(fdcwd,wrapper,O_RDWR|O_CREAT|O_TRUNC,0644)) < 0)
11c887
		return SLBT_SYSTEM_ERROR(dctx,wrapper);
11c887
11c887
	slbt_exec_set_fdwrapper(ectx,fdwrap);
11c887
11c887
	/* executable wrapper: header */
f0270a
	verinfo = slbt_api_source_version();
11c887
11c887
	/* cwd, DL_PATH fixup */
d4b2a5
	if (slbt_realpath(fdcwd,".",O_DIRECTORY,cwd,sizeof(cwd)))
11c887
		return SLBT_SYSTEM_ERROR(dctx,0);
11c887
11c887
	slbt_emit_fdwrap_dl_path_fixup(
11c887
		cwd,dpfixup,sizeof(dpfixup),
11c887
		wrapper);
11c887
11c887
	if (slbt_dprintf(fdwrap,
11c887
			"#!/bin/sh\n"
11c887
			"# libtool compatible executable wrapper\n"
11c887
			"# Generated by %s (slibtool %d.%d.%d)\n"
11c887
			"# [commit reference: %s]\n\n"
11c887
11c887
			"if [ -z \"$%s\" ]; then\n"
11c887
			"\tDL_PATH=\n"
11c887
			"\tCOLON=\n"
11c887
			"\tLCOLON=\n"
11c887
			"else\n"
11c887
			"\tDL_PATH=\n"
11c887
			"\tCOLON=\n"
11c887
			"\tLCOLON=':'\n"
11c887
			"fi\n\n"
11c887
			"DL_PATH_FIXUP=\"%s\";\n\n",
11c887
11c887
			dctx->program,
11c887
			verinfo->major,verinfo->minor,verinfo->revision,
11c887
			verinfo->commit,
11c887
			dctx->cctx->settings.ldpathenv,
11c887
			dpfixup) < 0)
11c887
		return SLBT_SYSTEM_ERROR(dctx,0);
11c887
11c887
	/* output */
11c887
	if (slbt_snprintf(output,sizeof(output),
11c887
			"%s",exefilename) < 0)
11c887
		return SLBT_BUFFER_ERROR(dctx);
11c887
11c887
	*ectx->lout[0] = "-o";
11c887
	*ectx->lout[1] = output;
11c887
11c887
	/* static? */
11c887
	if (dctx->cctx->drvflags & SLBT_DRIVER_ALL_STATIC)
11c887
		*ectx->dpic = "-static";
11c887
11c887
	/* .libs/libfoo.so --> -L.libs -lfoo */
11c887
	if (slbt_exec_link_adjust_argument_vector(
11c887
			dctx,ectx,&depsmeta,cwd,false))
11c887
		return SLBT_NESTED_ERROR(dctx);
11c887
11c887
	/* using alternate argument vector */
11c887
	ccwrap        = (char *)dctx->cctx->ccwrap;
11c887
	ectx->argv    = depsmeta.altv;
11c887
	ectx->program = ccwrap ? ccwrap : depsmeta.altv[0];
11c887
11c887
	/* executable wrapper symlink */
11c887
	if (slbt_snprintf(wraplnk,sizeof(wraplnk),
11c887
			"%s.exe.wrapper",
11c887
			exefilename) < 0)
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_BUFFER_ERROR(dctx));
11c887
11c887
	/* executable wrapper: base name */
11c887
	base = strrchr(wraplnk,'/');
11c887
	base++;
11c887
11c887
	/* executable wrapper: footer */
11c887
	fabspath = (exefilename[0] == '/');
11c887
11c887
	if (slbt_dprintf(fdwrap,
11c887
			"DL_PATH=\"${DL_PATH}${LCOLON}${%s}\"\n\n"
11c887
			"export %s=\"$DL_PATH\"\n\n"
11c887
			"if [ $(basename \"$0\") = \"%s\" ]; then\n"
11c887
			"\tprogram=\"$1\"; shift\n"
11c887
			"\texec \"$program\" \"$@\"\n"
11c887
			"fi\n\n"
11c887
			"exec %s/%s \"$@\"\n",
11c887
			dctx->cctx->settings.ldpathenv,
11c887
			dctx->cctx->settings.ldpathenv,
11c887
			base,
11c887
			fabspath ? "" : cwd,
11c887
			fabspath ? &exefilename[1] : exefilename) < 0)
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_SYSTEM_ERROR(dctx,0));
11c887
11c887
	/* sigh */
11c887
	if (slbt_exec_link_finalize_argument_vector(dctx,ectx))
11c887
		return SLBT_NESTED_ERROR(dctx);
11c887
11c887
	/* step output */
11c887
	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
33a569
		if (slbt_output_link(ectx))
11c887
			return slbt_linkcmd_exit(
11c887
				&depsmeta,
11c887
				SLBT_NESTED_ERROR(dctx));
11c887
11c887
	/* spawn */
11c887
	if ((slbt_spawn(ectx,true) < 0) && (ectx->pid < 0)) {
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_SPAWN_ERROR(dctx));
11c887
11c887
	} else if (ectx->exitcode) {
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_CUSTOM_ERROR(
11c887
				dctx,
11c887
				SLBT_ERR_LINK_ERROR));
11c887
	}
11c887
11c887
	/* executable wrapper: finalize */
11c887
	slbt_exec_close_fdwrapper(ectx);
11c887
11c887
	if (slbt_create_symlink(
11c887
			dctx,ectx,
11c887
			dctx->cctx->output,wraplnk,
11c887
			SLBT_SYMLINK_WRAPPER))
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_NESTED_ERROR(dctx));
11c887
11c887
	if (fstatat(fdcwd,wrapper,&st,0))
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_SYSTEM_ERROR(dctx,wrapper));
11c887
11c887
	if (renameat(fdcwd,wrapper,fdcwd,dctx->cctx->output))
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_SYSTEM_ERROR(dctx,dctx->cctx->output));
11c887
11c887
	if (fchmodat(fdcwd,dctx->cctx->output,0755,0))
11c887
		return slbt_linkcmd_exit(
11c887
			&depsmeta,
11c887
			SLBT_SYSTEM_ERROR(dctx,dctx->cctx->output));
11c887
11c887
	return slbt_linkcmd_exit(&depsmeta,0);
11c887
}