Blame src/driver/pe_driver_ctx.c

e2e2c2
/***************************************************************/
e2e2c2
/*  perk: PE Resource Kit                                      */
425fb8
/*  Copyright (C) 2015--2021  SysDeer Technologies, LLC        */
e2e2c2
/*  Released under GPLv2 and GPLv3; see COPYING.PERK.          */
e2e2c2
/***************************************************************/
e2e2c2
81e8b3
#include <stdint.h>
6f882b
#include <unistd.h>
81e8b3
#include <fcntl.h>
c61328
249590
#define ARGV_DRIVER
249590
81e8b3
#include <perk/perk.h>
949bb3
#include <perk/perk_output.h>
b7025c
#include "perk_version.h"
249590
#include "perk_driver_impl.h"
81e8b3
#include "argv/argv.h"
81e8b3
b7025c
/* package info */
b7025c
static const struct pe_source_version pe_src_version = {
b7025c
	PERK_TAG_VER_MAJOR,
b7025c
	PERK_TAG_VER_MINOR,
b7025c
	PERK_TAG_VER_PATCH,
b7025c
	PERK_GIT_VERSION
b7025c
};
b7025c
59b920
/* perk command names */
59b920
static const char * const perk_cmd_name[PERK_CMD_CAP] = {
59b920
	[PERK_CMD_PERK]     = "perk",
59b920
};
59b920
59b920
/* perk command options */
59b920
static const struct argv_option * perk_cmd_options[PERK_CMD_CAP] = {
59b920
	[PERK_CMD_DEFAULT]  = pe_default_options,
59b920
	[PERK_CMD_PERK]     = pe_perk_options,
59b920
};
59b920
d7016c
/* default fd context */
d7016c
static const struct pe_fd_ctx pe_default_fdctx = {
d7016c
	.fdin  = STDIN_FILENO,
d7016c
	.fdout = STDOUT_FILENO,
d7016c
	.fderr = STDERR_FILENO,
d7016c
	.fdcwd = AT_FDCWD,
d7016c
	.fddst = AT_FDCWD,
d7016c
	.fdlog = (-1),
d7016c
};
d7016c
1711ab
struct pe_driver_ctx_alloc {
44d7e6
	struct argv_meta *		meta;
c61328
	struct pe_driver_ctx_impl	ctx;
44d7e6
	uint64_t			guard;
44d7e6
	const char *			units[];
81e8b3
};
81e8b3
81e8b3
static uint32_t pe_argv_flags(uint32_t flags)
81e8b3
{
42b537
	uint32_t ret = ARGV_CLONE_VECTOR;
81e8b3
81e8b3
	if (flags & PERK_DRIVER_VERBOSITY_NONE)
81e8b3
		ret |= ARGV_VERBOSITY_NONE;
81e8b3
81e8b3
	if (flags & PERK_DRIVER_VERBOSITY_ERRORS)
81e8b3
		ret |= ARGV_VERBOSITY_ERRORS;
81e8b3
81e8b3
	if (flags & PERK_DRIVER_VERBOSITY_STATUS)
81e8b3
		ret |= ARGV_VERBOSITY_STATUS;
81e8b3
81e8b3
	return ret;
81e8b3
}
81e8b3
dd97ea
static int pe_driver_usage(
a85d3e
	int				fdout,
612b7d
	const char *			program,
612b7d
	const char *			arg,
cfa4ad
	const struct argv_option **	optv,
59b920
	struct argv_meta *		meta,
59b920
	enum pe_cmd                     cmd)
dd97ea
{
59b920
	char		header[512];
59b920
	char *		cmdarg[2];
59b920
	const char *	cmdname;
59b920
59b920
	if (cmd == PERK_CMD_DEFAULT) {
59b920
		cmdarg[0] = "";
59b920
		cmdarg[1] = "";
59b920
		cmdname   = "";
59b920
	} else {
59b920
		cmdarg[0] = " (--cmd=";
59b920
		cmdarg[1] = ")";
59b920
		cmdname   = perk_cmd_name[cmd];
59b920
	}
dd97ea
dd97ea
	snprintf(header,sizeof(header),
59b920
		"Usage: %s [options] ...\n"
59b920
		"Usage: %s [options] [--cmd=<command>] <arg> <arg> ...\n\n"
59b920
		"Notes: --cmd must precede all non-option arguments, as well as\n"
59b920
		"       all arguments that are specific to the selected command.\n\n"
59b920
		"Options%s%s%s:\n",
59b920
		program,program,cmdarg[0],cmdname,cmdarg[1]);
dd97ea
a85d3e
	argv_usage(fdout,header,optv,arg);
dd97ea
	argv_free(meta);
dd97ea
dd97ea
	return PERK_USAGE;
dd97ea
}
dd97ea
99763f
static struct pe_driver_ctx_impl * pe_driver_ctx_alloc(
99763f
	struct argv_meta *		meta,
a85d3e
	const struct pe_fd_ctx *	fdctx,
99763f
	const struct pe_common_ctx *	cctx,
99763f
	size_t				nunits)
81e8b3
{
1711ab
	struct pe_driver_ctx_alloc *	ictx;
81e8b3
	size_t				size;
81e8b3
	struct argv_entry *		entry;
81e8b3
	const char **			units;
c9c5f7
	int				elements;
81e8b3
1711ab
	size =  sizeof(struct pe_driver_ctx_alloc);
81e8b3
	size += (nunits+1)*sizeof(const char *);
81e8b3
ffcd00
	if (!(ictx = calloc(1,size)))
81e8b3
		return 0;
81e8b3
a85d3e
	memcpy(&ictx->ctx.fdctx,fdctx,sizeof(*fdctx));
a85d3e
	memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx));
99763f
c9c5f7
	elements = sizeof(ictx->ctx.erribuf) / sizeof(*ictx->ctx.erribuf);
c9c5f7
c9c5f7
	ictx->ctx.errinfp  = &ictx->ctx.erriptr[0];
c9c5f7
	ictx->ctx.erricap  = &ictx->ctx.erriptr[--elements];
c9c5f7
f11c1f
	ictx->meta = meta;
81e8b3
81e8b3
	for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++)
81e8b3
		if (!entry->fopt)
81e8b3
			*units++ = entry->arg;
81e8b3
c61328
	ictx->ctx.ctx.units = ictx->units;
c9c5f7
	ictx->ctx.ctx.errv  = ictx->ctx.errinfp;
81e8b3
	return &ictx->ctx;
81e8b3
}
81e8b3
59b920
static int pe_cmd_from_program(const char * program)
81e8b3
{
59b920
	const char *	dot;
59b920
	const char *	hyphen;
59b920
	const char *	mark;
81e8b3
59b920
	dot    = strrchr(program,'.');
59b920
	hyphen = strrchr(program,'-');
209439
59b920
	if (hyphen > dot)
59b920
		mark = ++hyphen;
59b920
	else if (dot > hyphen)
59b920
		mark = ++dot;
59b920
	else
59b920
		mark = program;
a85d3e
59b920
	if (!strcmp(mark,"perk")) {
59b920
		return PERK_CMD_PERK;
249590
59b920
	} else {
59b920
		return PERK_CMD_DEFAULT;
59b920
	}
59b920
}
81e8b3
59b920
static int pe_cctx_update(
59b920
	const char *                    program,
59b920
	const struct argv_option **     optv,
59b920
	struct argv_meta *              meta,
59b920
	const struct pe_fd_ctx *        fdctx,
59b920
	struct pe_common_ctx *          cctx,
59b920
	size_t *                        nunits)
59b920
{
59b920
	struct argv_entry *		entry;
59b920
	const char *			pretty;
81e8b3
59b920
	pretty = 0;
dd97ea
81e8b3
	/* get options, count units */
81e8b3
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
81e8b3
		if (entry->fopt) {
81e8b3
			switch (entry->tag) {
81e8b3
				case TAG_HELP:
51125f
					return pe_driver_usage(
51125f
						fdctx->fdout,
51125f
						program,entry->arg,
51125f
						optv,0,cctx->cmd);
59b920
					break;
59b920
59b920
				case TAG_CMD:
59b920
					if (*nunits)
59b920
						return pe_driver_usage(
59b920
							fdctx->fderr,
59b920
							program,0,
59b920
							optv,0,cctx->cmd);
59b920
59b920
					cctx->cmd = pe_cmd_from_program(entry->arg);
ef6791
					break;
81e8b3
81e8b3
				case TAG_VERSION:
59b920
					cctx->drvflags |= PERK_DRIVER_VERSION;
81e8b3
					break;
81e8b3
0d3dde
				case TAG_PRETTY:
0d3dde
					pretty = entry->arg;
0d3dde
					break;
0d3dde
cd4ace
				case TAG_CATEGORY:
59b920
					cctx->fmtflags |= PERK_OUTPUT_IMAGE_CATEGORY;
665ac3
					break;
665ac3
9d75af
				case TAG_SECTIONS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_IMAGE_SECTIONS;
9d75af
					break;
9d75af
016925
				case TAG_SYMBOLS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_IMAGE_SYMBOLS;
016925
					break;
016925
ecf121
				case TAG_STRINGS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_IMAGE_STRINGS;
ecf121
					break;
ecf121
81e8b3
				case TAG_EXPSYMS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_EXPORT_SYMS;
81e8b3
					break;
9c5807
9c5807
				case TAG_IMPLIBS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_IMPORT_LIBS;
9c5807
					break;
9c5807
9c5807
				case TAG_IMPSYMS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_IMPORT_SYMS;
9c5807
					break;
ee981e
ee981e
				case TAG_DSOLIBS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_MDSO_LIBS;
ee981e
					break;
ee981e
ee981e
				case TAG_DSOSYMS:
59b920
					cctx->fmtflags |= PERK_OUTPUT_MDSO_SYMS;
ee981e
					break;
d38fc0
d38fc0
				case TAG_HDRDUMP:
d38fc0
					if (!entry->arg) {
59b920
						cctx->hdrdump = 0;
59b920
						cctx->hdrdump = ~cctx->hdrdump;
d38fc0
					} else if (!strcmp(entry->arg,"dos")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_IMAGE_DOS_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"image.dos")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_IMAGE_DOS_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"coff")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_COFF_IMAGE_HEADER;
59b920
						cctx->hdrdump |= PERK_HDRDUMP_COFF_OBJECT_HEADER;
59b920
						cctx->hdrdump |= PERK_HDRDUMP_COFF_OPT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"coff.image")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_COFF_IMAGE_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"coff.obj")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_COFF_OBJECT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"coff.object")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_COFF_OBJECT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"coff.opt")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_COFF_OPT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"coff.optional")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_COFF_OPT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"sectbl")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_SECTION_TABLE;
d38fc0
					} else if (!strcmp(entry->arg,"section.table")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_SECTION_TABLE;
d38fc0
					} else if (!strcmp(entry->arg,"exphdr")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_EXPORT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"export.header")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_EXPORT_HEADER;
d38fc0
					} else if (!strcmp(entry->arg,"imptbl")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_IMPORT_TABLE;
d38fc0
					} else if (!strcmp(entry->arg,"import.table")) {
59b920
						cctx->hdrdump  = PERK_HDRDUMP_IMPORT_TABLE;
d38fc0
					}
d38fc0
					break;
81e8b3
			}
59b920
		} else {
59b920
			(*nunits)++;
59b920
		}
59b920
	}
59b920
59b920
	if (pretty && !strcmp(pretty,"yaml")) {
59b920
		cctx->fmtflags |= PERK_PRETTY_YAML;
59b920
59b920
	} else if (pretty && !strcmp(pretty,"dlltool")) {
59b920
		cctx->fmtflags |= PERK_PRETTY_DLLTOOL;
81e8b3
	}
81e8b3
59b920
	return 0;
59b920
}
59b920
118b74
static int pe_lib_get_driver_ctx_fail(struct argv_meta * meta)
59b920
{
59b920
	argv_free(meta);
59b920
	return -1;
59b920
}
59b920
118b74
int pe_lib_get_driver_ctx(
59b920
	char **				argv,
59b920
	char **				envp,
59b920
	uint32_t			flags,
59b920
	const struct pe_fd_ctx *	fdctx,
59b920
	struct pe_driver_ctx ** 	pctx)
59b920
{
59b920
	struct pe_driver_ctx_impl *	ctx;
59b920
	struct pe_common_ctx		cctx;
59b920
	const struct argv_option *	optv[PERK_OPTV_ELEMENTS];
59b920
	struct argv_meta *		meta;
59b920
	size_t				nunits;
59b920
	const char *			program;
59b920
	char **				parg;
59b920
	char **				pargcap;
59b920
	char **				cmdargv;
59b920
	char *				cmdmark;
59b920
	struct argv_ctx			actx = {ARGV_VERBOSITY_NONE,
59b920
                                               ARGV_MODE_SCAN,
59b920
                                               0,0,0,0,0,0,0,0};
59b920
59b920
59b920
	(void)envp;
59b920
59b920
	/* fdctx */
59b920
	if (!fdctx)
59b920
		fdctx = &pe_default_fdctx;
59b920
59b920
	/* cctx */
59b920
	memset(&cctx,0,sizeof(cctx));
59b920
59b920
	program       = argv_program_name(argv[0]);\
59b920
	cctx.cmd      = pe_cmd_from_program(program);
59b920
	cctx.drvflags = flags;
59b920
	nunits	      = 0;
59b920
59b920
	/* missing arguments? */
59b920
	argv_optv_init(perk_cmd_options[cctx.cmd],optv);
59b920
51125f
	if (!argv[1] && (flags & PERK_DRIVER_VERBOSITY_UTILITY))
59b920
		return pe_driver_usage(
59b920
			fdctx->fderr,
59b920
			program,0,
59b920
			optv,0,cctx.cmd);
59b920
59b920
	/* initial argv scan: ... --cmd=xxx ... */
59b920
	argv_scan(argv,optv,&actx,0);
59b920
59b920
	/* position of last base perk argument */
59b920
	if (actx.erridx && actx.unitidx)
59b920
		pargcap = &argv[actx.unitidx];
59b920
59b920
	else if (actx.erridx)
59b920
		pargcap = &argv[actx.erridx];
59b920
59b920
	else
59b920
		for (pargcap=argv; *pargcap; pargcap++)
59b920
			(void)0;
59b920
59b920
	/* scan for --cmd */
59b920
	for (parg=argv, cmdargv=0; (parg
59b920
		if (!strcmp(*parg,"--cmd") && parg[1]) {
59b920
				cmdargv = &parg[2];
59b920
				cmdmark = parg[2];
59b920
		} else if (!strncmp(*parg,"--cmd=",6)) {
59b920
				cmdargv = &parg[1];
59b920
				cmdmark = parg[1];
59b920
		}
59b920
	}
59b920
59b920
	/* invalid perk arguments? */
59b920
	if (!actx.erridx) {
59b920
		(void)0;
59b920
59b920
	} else if (&argv[actx.erridx] >= cmdargv) {
59b920
		(void)0;
59b920
59b920
	} else {
59b920
		if (flags & PERK_DRIVER_VERBOSITY_ERRORS)
59b920
			argv_get(
59b920
				argv,optv,
59b920
				ARGV_VERBOSITY_ERRORS,
59b920
				fdctx->fderr);
59b920
		return -1;
59b920
	}
59b920
59b920
	/* process argv entries preceding --cmd */
59b920
	if (cmdargv) {
59b920
		*cmdargv = 0;
59b920
59b920
		if (!(meta = argv_get(
59b920
				argv,optv,
59b920
				pe_argv_flags(flags),
59b920
				fdctx->fderr)))
59b920
			return -1;
59b920
59b920
		if (pe_cctx_update(
51125f
				program,optv,meta,
59b920
				fdctx,&cctx,&nunits)) {
59b920
			argv_free(meta);
59b920
			return -1;
59b920
		}
59b920
59b920
		argv_free(meta);
59b920
59b920
		*cmdargv-- = cmdmark;
59b920
		*cmdargv   = argv[0];
59b920
		argv       = cmdargv;
59b920
	}
59b920
59b920
	/* set option vector by command */
59b920
	if (cctx.cmd == PERK_CMD_DEFAULT) {
59b920
		argv_optv_init(pe_default_options,optv);
59b920
59b920
	} else if (cctx.cmd == PERK_CMD_PERK) {
59b920
		argv_optv_init(pe_perk_options,optv);
59b920
	}
59b920
59b920
	/* process the selected tool's command-line arguments */
59b920
	if (!(meta = argv_get(
59b920
			argv,optv,
59b920
			pe_argv_flags(flags),
59b920
			fdctx->fderr)))
59b920
		return -1;
59b920
59b920
	if (pe_cctx_update(
51125f
			program,optv,meta,
59b920
			fdctx,&cctx,&nunits)) {
59b920
		argv_free(meta);
59b920
		return -1;
59b920
	}
99763f
51125f
	/* utility mode and no action to take? */
51125f
	if (cctx.drvflags & PERK_DRIVER_VERBOSITY_UTILITY)
51125f
		if (!nunits && !(cctx.drvflags & PERK_DRIVER_VERSION))
51125f
			return pe_driver_usage(
51125f
				fdctx->fdout,
51125f
				program,0,
51125f
				optv,meta,cctx.cmd);
51125f
51125f
	/* context allocation */
a85d3e
	if (!(ctx = pe_driver_ctx_alloc(meta,fdctx,&cctx,nunits)))
118b74
		return pe_lib_get_driver_ctx_fail(meta);
81e8b3
c61328
	ctx->ctx.program	= program;
99763f
	ctx->ctx.cctx		= &ctx->cctx;
81e8b3
c61328
	*pctx = &ctx->ctx;
81e8b3
	return PERK_OK;
81e8b3
}
81e8b3
118b74
static void pe_lib_free_driver_ctx_impl(struct pe_driver_ctx_alloc * ictx)
81e8b3
{
81e8b3
	argv_free(ictx->meta);
81e8b3
	free(ictx);
81e8b3
}
81e8b3
118b74
void pe_lib_free_driver_ctx(struct pe_driver_ctx * ctx)
81e8b3
{
1711ab
	struct pe_driver_ctx_alloc *	ictx;
81e8b3
	uintptr_t			addr;
81e8b3
81e8b3
	if (ctx) {
95fab3
		addr = (uintptr_t)ctx - offsetof(struct pe_driver_ctx_impl,ctx);
95fab3
		addr = addr - offsetof(struct pe_driver_ctx_alloc,ctx);
1711ab
		ictx = (struct pe_driver_ctx_alloc *)addr;
118b74
		pe_lib_free_driver_ctx_impl(ictx);
81e8b3
	}
81e8b3
}
b7025c
b7025c
const struct pe_source_version * pe_source_version(void)
b7025c
{
b7025c
	return &pe_src_version;
b7025c
}
a85d3e
118b74
int pe_lib_get_driver_fdctx(
a85d3e
	const struct pe_driver_ctx *	dctx,
a85d3e
	struct pe_fd_ctx *		fdctx)
a85d3e
{
a85d3e
	struct pe_driver_ctx_impl *	ictx;
a85d3e
a85d3e
	ictx = pe_get_driver_ictx(dctx);
a85d3e
a85d3e
	fdctx->fdin  = ictx->fdctx.fdin;
a85d3e
	fdctx->fdout = ictx->fdctx.fdout;
a85d3e
	fdctx->fderr = ictx->fdctx.fderr;
a85d3e
	fdctx->fdlog = ictx->fdctx.fdlog;
a85d3e
	fdctx->fdcwd = ictx->fdctx.fdcwd;
a85d3e
	fdctx->fddst = ictx->fdctx.fddst;
a85d3e
a85d3e
	return 0;
a85d3e
}
a85d3e
118b74
int pe_lib_set_driver_fdctx(
a85d3e
	struct pe_driver_ctx *	dctx,
a85d3e
	const struct pe_fd_ctx *	fdctx)
a85d3e
{
a85d3e
	struct pe_driver_ctx_impl *	ictx;
a85d3e
a85d3e
	ictx = pe_get_driver_ictx(dctx);
a85d3e
a85d3e
	ictx->fdctx.fdin  = fdctx->fdin;
a85d3e
	ictx->fdctx.fdout = fdctx->fdout;
a85d3e
	ictx->fdctx.fderr = fdctx->fderr;
a85d3e
	ictx->fdctx.fdlog = fdctx->fdlog;
a85d3e
	ictx->fdctx.fdcwd = fdctx->fdcwd;
a85d3e
	ictx->fdctx.fddst = fdctx->fddst;
a85d3e
a85d3e
	return 0;
a85d3e
}