Blame src/logic/slbt_exec_ar.c

11f3c7
/*******************************************************************/
eac61a
/*  slibtool: a strong libtool implementation, written in C        */
11f3c7
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
11f3c7
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
11f3c7
/*******************************************************************/
11f3c7
11f3c7
#include <slibtool/slibtool.h>
8afddf
#include <slibtool/slibtool_output.h>
11f3c7
#include "slibtool_driver_impl.h"
11f3c7
#include "slibtool_ar_impl.h"
11f3c7
#include "slibtool_errinfo_impl.h"
11f3c7
#include "argv/argv.h"
11f3c7
0b177b
#define SLBT_DRIVER_MODE_AR_ACTIONS     (SLBT_DRIVER_MODE_AR_CHECK \
0b177b
	                                 | SLBT_DRIVER_MODE_AR_MERGE)
af36ce
8afddf
#define SLBT_DRIVER_MODE_AR_OUTPUTS     (SLBT_OUTPUT_ARCHIVE_MEMBERS  \
8afddf
	                                 | SLBT_OUTPUT_ARCHIVE_HEADERS \
8afddf
	                                 | SLBT_OUTPUT_ARCHIVE_SYMBOLS  \
8afddf
	                                 | SLBT_OUTPUT_ARCHIVE_ARMAPS)
8afddf
8afddf
#define SLBT_PRETTY_FLAGS               (SLBT_PRETTY_YAML      \
8afddf
	                                 | SLBT_PRETTY_POSIX    \
8afddf
	                                 | SLBT_PRETTY_HEXDATA)
8afddf
11f3c7
static int slbt_ar_usage(
11f3c7
	int				fdout,
11f3c7
	const char *			program,
11f3c7
	const char *			arg,
11f3c7
	const struct argv_option **	optv,
11f3c7
	struct argv_meta *		meta,
11f3c7
	struct slbt_exec_ctx *		ectx,
11f3c7
	int				noclr)
11f3c7
{
11f3c7
	char		header[512];
11f3c7
	bool		armode;
11f3c7
	const char *	dash;
11f3c7
11f3c7
	armode = (dash = strrchr(program,'-'))
11f3c7
		&& !strcmp(++dash,"ar");
11f3c7
11f3c7
	snprintf(header,sizeof(header),
11f3c7
		"Usage: %s%s [options] [ARCHIVE-FILE] [ARCHIVE_FILE] ...\n"
11f3c7
		"Options:\n",
11f3c7
		program,
11f3c7
		armode ? "" : " --mode=ar");
11f3c7
11f3c7
	switch (noclr) {
11f3c7
		case 0:
ef2baa
			slbt_argv_usage(fdout,header,optv,arg);
11f3c7
			break;
11f3c7
11f3c7
		default:
ef2baa
			slbt_argv_usage_plain(fdout,header,optv,arg);
11f3c7
			break;
11f3c7
	}
11f3c7
11f3c7
	if (ectx)
f3d47a
		slbt_ectx_free_exec_ctx(ectx);
11f3c7
ef2baa
	slbt_argv_free(meta);
11f3c7
11f3c7
	return SLBT_USAGE;
11f3c7
}
11f3c7
11f3c7
static int slbt_exec_ar_fail(
2e30eb
	struct slbt_exec_ctx *	ectx,
11f3c7
	struct argv_meta *	meta,
11f3c7
	int			ret)
11f3c7
{
ef2baa
	slbt_argv_free(meta);
2e30eb
	slbt_ectx_free_exec_ctx(ectx);
11f3c7
	return ret;
11f3c7
}
11f3c7
8afddf
static int slbt_exec_ar_perform_archive_actions(
8afddf
	const struct slbt_driver_ctx *  dctx,
8afddf
	struct slbt_archive_ctx **      arctxv)
8afddf
{
8afddf
	struct slbt_archive_ctx **      arctxp;
0b177b
	struct slbt_archive_ctx *       arctx;
7ffcb5
	bool                            farname;
7ffcb5
7ffcb5
	switch (dctx->cctx->fmtflags & SLBT_PRETTY_FLAGS) {
7ffcb5
		case SLBT_PRETTY_POSIX:
7ffcb5
			farname = (arctxv[0] && arctxv[1]);
7ffcb5
			break;
7ffcb5
7ffcb5
		default:
7ffcb5
			farname = true;
7ffcb5
			break;
7ffcb5
	}
8afddf
8afddf
	for (arctxp=arctxv; *arctxp; arctxp++) {
8afddf
		if (dctx->cctx->fmtflags & SLBT_DRIVER_MODE_AR_OUTPUTS)
3fa3e3
			if (farname && (slbt_au_output_arname(*arctxp) < 0))
8afddf
				return SLBT_NESTED_ERROR(dctx);
8afddf
8afddf
		if (dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_MEMBERS)
3fa3e3
			if (slbt_au_output_members((*arctxp)->meta) < 0)
8afddf
				return SLBT_NESTED_ERROR(dctx);
b75e41
b75e41
		if (dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_SYMBOLS)
3fa3e3
			if (slbt_au_output_symbols((*arctxp)->meta) < 0)
b75e41
				return SLBT_NESTED_ERROR(dctx);
2dc23e
2dc23e
		if (dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_MAPFILE)
3fa3e3
			if (slbt_au_output_mapfile((*arctxp)->meta) < 0)
2dc23e
				return SLBT_NESTED_ERROR(dctx);
8afddf
	}
8afddf
ae486a
	if (dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_DLSYMS)
ae486a
		if (slbt_au_output_dlsyms(arctxv,dctx->cctx->dlunit) < 0)
ae486a
			return SLBT_NESTED_ERROR(dctx);
ae486a
0b177b
	if (dctx->cctx->drvflags & SLBT_DRIVER_MODE_AR_MERGE) {
51c276
		if (slbt_ar_merge_archives(arctxv,&arctx) < 0)
0b177b
			return SLBT_NESTED_ERROR(dctx);
0b177b
0b177b
		/* (defer mode to umask) */
51c276
		if (slbt_ar_store_archive(arctx,dctx->cctx->output,0666) < 0)
0b177b
			return SLBT_NESTED_ERROR(dctx);
0b177b
	}
0b177b
8afddf
	return 0;
8afddf
}
8afddf
2e30eb
int slbt_exec_ar(const struct slbt_driver_ctx * dctx)
11f3c7
{
11f3c7
	int				ret;
11f3c7
	int				fdout;
af36ce
	int				fderr;
11f3c7
	char **				argv;
11f3c7
	char **				iargv;
2e30eb
	struct slbt_exec_ctx *		ectx;
af36ce
	struct slbt_driver_ctx_impl *	ictx;
af36ce
	const struct slbt_common_ctx *	cctx;
11f3c7
	struct slbt_archive_ctx **	arctxv;
7bab5a
	struct slbt_archive_ctx **	arctxp;
11f3c7
	const char **			unitv;
11f3c7
	const char **			unitp;
11f3c7
	size_t				nunits;
11f3c7
	struct argv_meta *		meta;
11f3c7
	struct argv_entry *		entry;
11f3c7
	const struct argv_option *	optv[SLBT_OPTV_ELEMENTS];
11f3c7
11f3c7
	/* context */
2e30eb
	if (slbt_ectx_get_exec_ctx(dctx,&ectx) < 0)
2e30eb
		return SLBT_NESTED_ERROR(dctx);
11f3c7
11f3c7
	/* initial state, ar mode skin */
f3d47a
	slbt_ectx_reset_arguments(ectx);
11f3c7
	slbt_disable_placeholders(ectx);
af36ce
af36ce
	ictx  = slbt_get_driver_ictx(dctx);
af36ce
	cctx  = dctx->cctx;
11f3c7
	iargv = ectx->cargv;
af36ce
11f3c7
	fdout = slbt_driver_fdout(dctx);
af36ce
	fderr = slbt_driver_fderr(dctx);
11f3c7
11f3c7
	/* missing arguments? */
ef2baa
	slbt_optv_init(slbt_ar_options,optv);
11f3c7
11f3c7
	if (!iargv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE))
11f3c7
		return slbt_ar_usage(
11f3c7
			fdout,
11f3c7
			dctx->program,
2e30eb
			0,optv,0,ectx,
11f3c7
			dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER);
11f3c7
11f3c7
	/* <ar> argv meta */
ef2baa
	if (!(meta = slbt_argv_get(
11f3c7
			iargv,optv,
11f3c7
			dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS
11f3c7
				? ARGV_VERBOSITY_ERRORS
11f3c7
				: ARGV_VERBOSITY_NONE,
11f3c7
			fdout)))
11f3c7
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
11f3c7
			SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_AR_FAIL));
11f3c7
11f3c7
	/* dest, alternate argument vector options */
11f3c7
	argv    = ectx->altv;
11f3c7
	*argv++ = iargv[0];
11f3c7
	nunits  = 0;
11f3c7
11f3c7
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
11f3c7
		if (entry->fopt) {
11f3c7
			switch (entry->tag) {
11f3c7
				case TAG_AR_HELP:
11f3c7
					slbt_ar_usage(
11f3c7
						fdout,
11f3c7
						dctx->program,
11f3c7
						0,optv,0,ectx,
11f3c7
						dctx->cctx->drvflags
11f3c7
							& SLBT_DRIVER_ANNOTATE_NEVER);
1eb928
a470d5
					ictx->cctx.drvflags |= SLBT_DRIVER_VERSION;
a470d5
					ictx->cctx.drvflags ^= SLBT_DRIVER_VERSION;
a470d5
ef2baa
					slbt_argv_free(meta);
1eb928
1eb928
					return SLBT_OK;
af36ce
b87f70
				case TAG_AR_VERSION:
b87f70
					ictx->cctx.drvflags |= SLBT_DRIVER_VERSION;
b87f70
					break;
b87f70
af36ce
				case TAG_AR_CHECK:
af36ce
					ictx->cctx.drvflags |= SLBT_DRIVER_MODE_AR_CHECK;
af36ce
					break;
8afddf
0b177b
				case TAG_AR_MERGE:
0b177b
					ictx->cctx.drvflags |= SLBT_DRIVER_MODE_AR_MERGE;
0b177b
					break;
0b177b
0b177b
				case TAG_AR_OUTPUT:
0b177b
					ictx->cctx.output = entry->arg;
0b177b
					break;
0b177b
8afddf
				case TAG_AR_PRINT:
8afddf
					if (!entry->arg)
8afddf
						ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_MEMBERS;
8afddf
8afddf
					else if (!strcmp(entry->arg,"members"))
8afddf
						ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_MEMBERS;
8afddf
8afddf
					else if (!strcmp(entry->arg,"headers"))
8afddf
						ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_HEADERS;
8afddf
8afddf
					else if (!strcmp(entry->arg,"symbols"))
8afddf
						ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_SYMBOLS;
8afddf
8afddf
					else if (!strcmp(entry->arg,"armaps"))
8afddf
						ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_ARMAPS;
8afddf
8afddf
					break;
8afddf
2dc23e
				case TAG_AR_MAPFILE:
2dc23e
					ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_MAPFILE;
2dc23e
					break;
2dc23e
dcf6a5
				case TAG_AR_DLSYMS:
dcf6a5
					ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_DLSYMS;
dcf6a5
					break;
dcf6a5
dcf6a5
				case TAG_AR_DLUNIT:
dcf6a5
					ictx->cctx.dlunit = entry->arg;
dcf6a5
					break;
dcf6a5
874b59
				case TAG_AR_NOSORT:
874b59
					ictx->cctx.fmtflags |= SLBT_OUTPUT_ARCHIVE_NOSORT;
874b59
					break;
874b59
76c5e9
				case TAG_AR_REGEX:
76c5e9
					ictx->cctx.regex = entry->arg;
76c5e9
					break;
76c5e9
8afddf
				case TAG_AR_PRETTY:
8afddf
					if (!strcmp(entry->arg,"yaml")) {
8afddf
						ictx->cctx.fmtflags &= ~(uint64_t)SLBT_PRETTY_FLAGS;
8afddf
						ictx->cctx.fmtflags |= SLBT_PRETTY_YAML;
8afddf
8afddf
					} else if (!strcmp(entry->arg,"posix")) {
8afddf
						ictx->cctx.fmtflags &= ~(uint64_t)SLBT_PRETTY_FLAGS;
8afddf
						ictx->cctx.fmtflags |= SLBT_PRETTY_POSIX;
8afddf
					}
8afddf
8afddf
					break;
191700
886865
				case TAG_AR_POSIX:
886865
					ictx->cctx.fmtflags &= ~(uint64_t)SLBT_PRETTY_FLAGS;
886865
					ictx->cctx.fmtflags |= SLBT_PRETTY_POSIX;
886865
					break;
886865
886865
				case TAG_AR_YAML:
886865
					ictx->cctx.fmtflags &= ~(uint64_t)SLBT_PRETTY_FLAGS;
886865
					ictx->cctx.fmtflags |= SLBT_PRETTY_YAML;
886865
					break;
886865
191700
				case TAG_AR_VERBOSE:
191700
					ictx->cctx.fmtflags |= SLBT_PRETTY_VERBOSE;
191700
					break;
11f3c7
			}
11f3c7
11f3c7
			if (entry->fval) {
11f3c7
				*argv++ = (char *)entry->arg;
11f3c7
			}
11f3c7
		} else {
11f3c7
			nunits++;
11f3c7
		};
11f3c7
	}
11f3c7
b87f70
	/* defer --version printing to slbt_main() as needed */
b87f70
	if (cctx->drvflags & SLBT_DRIVER_VERSION) {
ef2baa
		slbt_argv_free(meta);
2e30eb
		slbt_ectx_free_exec_ctx(ectx);
b87f70
		return SLBT_OK;
b87f70
	}
b87f70
af36ce
	/* at least one action must be specified */
8afddf
	if (cctx->fmtflags & SLBT_DRIVER_MODE_AR_OUTPUTS) {
8afddf
		(void)0;
8afddf
2dc23e
	} else if (cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_MAPFILE) {
2dc23e
		(void)0;
2dc23e
dcf6a5
	} else if (cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_DLSYMS) {
132ccd
		if (!cctx->dlunit) {
a2500c
			slbt_dprintf(fderr,
a2500c
				"%s: missing -Wdlunit: generation of a dlsyms vtable "
a2500c
				"requires the name of the dynamic library, executable "
a2500c
				"program, or dynamic module for which the vtable would "
a2500c
				"be generated.\n",
a2500c
				dctx->program);
a2500c
132ccd
			return slbt_exec_ar_fail(
132ccd
				ectx,meta,
132ccd
				SLBT_CUSTOM_ERROR(
132ccd
					dctx,
132ccd
					SLBT_ERR_AR_DLUNIT_NOT_SPECIFIED));
132ccd
		}
8afddf
	} else if (!(cctx->drvflags & SLBT_DRIVER_MODE_AR_ACTIONS)) {
af36ce
		if (cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS)
af36ce
			slbt_dprintf(fderr,
af36ce
				"%s: at least one action must be specified\n",
af36ce
				dctx->program);
af36ce
af36ce
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
af36ce
			SLBT_CUSTOM_ERROR(
af36ce
				dctx,
af36ce
				SLBT_ERR_AR_NO_ACTION_SPECIFIED));
af36ce
	}
af36ce
0b177b
	/* -Wmerge without -Woutput? */
0b177b
	if ((cctx->drvflags & SLBT_DRIVER_MODE_AR_MERGE) && !cctx->output) {
0b177b
		if (cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS)
0b177b
			slbt_dprintf(fderr,
0b177b
				"%s: archive merging: output must be specified.\n",
0b177b
				dctx->program);
0b177b
0b177b
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
0b177b
			SLBT_CUSTOM_ERROR(
0b177b
				dctx,
0b177b
				SLBT_ERR_AR_OUTPUT_NOT_SPECIFIED));
0b177b
	}
0b177b
0b177b
0b177b
	/* -Woutput without -Wmerge? */
0b177b
	if (cctx->output && !(cctx->drvflags & SLBT_DRIVER_MODE_AR_MERGE)) {
0b177b
		if (cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS)
0b177b
			slbt_dprintf(fderr,
0b177b
				"%s: output may only be specified "
0b177b
				"when merging one or more archives.\n",
0b177b
				dctx->program);
0b177b
0b177b
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
0b177b
			SLBT_CUSTOM_ERROR(
0b177b
				dctx,
0b177b
				SLBT_ERR_AR_OUTPUT_NOT_APPLICABLE));
0b177b
	}
0b177b
0b177b
af36ce
	/* at least one unit must be specified */
af36ce
	if (!nunits) {
af36ce
		if (cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS)
af36ce
			slbt_dprintf(fderr,
af36ce
				"%s: all actions require at least one input unit\n",
af36ce
				dctx->program);
af36ce
af36ce
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
af36ce
			SLBT_CUSTOM_ERROR(
af36ce
				dctx,
af36ce
				SLBT_ERR_AR_NO_INPUT_SPECIFIED));
af36ce
	}
af36ce
11f3c7
	/* archive vector allocation */
11f3c7
	if (!(arctxv = calloc(nunits+1,sizeof(struct slbt_archive_ctx *))))
11f3c7
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
11f3c7
			SLBT_SYSTEM_ERROR(dctx,0));
11f3c7
11f3c7
	/* unit vector allocation */
11f3c7
	if (!(unitv = calloc(nunits+1,sizeof(const char *)))) {
11f3c7
		free (arctxv);
11f3c7
11f3c7
		return slbt_exec_ar_fail(
2e30eb
			ectx,meta,
11f3c7
			SLBT_SYSTEM_ERROR(dctx,0));
11f3c7
	}
11f3c7
11f3c7
	/* unit vector initialization */
11f3c7
	for (entry=meta->entries,unitp=unitv; entry->fopt || entry->arg; entry++)
11f3c7
		if (!entry->fopt)
11f3c7
			*unitp++ = entry->arg;
11f3c7
7bab5a
	/* archive context vector initialization */
7bab5a
	for (unitp=unitv,arctxp=arctxv; *unitp; unitp++,arctxp++) {
51c276
		if (slbt_ar_get_archive_ctx(dctx,*unitp,arctxp) < 0) {
7bab5a
			for (arctxp=arctxv; *arctxp; arctxp++)
51c276
				slbt_ar_free_archive_ctx(*arctxp);
7bab5a
7bab5a
			free(unitv);
7bab5a
			free(arctxv);
7bab5a
7bab5a
			return slbt_exec_ar_fail(
2e30eb
				ectx,meta,
7bab5a
				SLBT_NESTED_ERROR(dctx));
7bab5a
		}
7bab5a
	}
7bab5a
8afddf
	/* archive operations */
8afddf
	ret = slbt_exec_ar_perform_archive_actions(dctx,arctxv);
8afddf
11f3c7
	/* all done */
7bab5a
	for (arctxp=arctxv; *arctxp; arctxp++)
51c276
		slbt_ar_free_archive_ctx(*arctxp);
7bab5a
11f3c7
	free(unitv);
11f3c7
	free(arctxv);
11f3c7
ef2baa
	slbt_argv_free(meta);
2e30eb
	slbt_ectx_free_exec_ctx(ectx);
11f3c7
8afddf
	return ret;
11f3c7
}