[BACK]Return to trace.c CVS log [TXT][DIR] Up to [local] / src / libexec / ld.so

File: [local] / src / libexec / ld.so / trace.c (download)

Revision 1.5, Sat Jan 8 06:49:41 2022 UTC (2 years, 5 months ago) by guenther
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, HEAD
Changes since 1.4: +2 -2 lines

Prep .c files for removing the #includes from */archdep.h
 * replace #include "archdep.h" with #includes of what is used, pulling in
   "syscall.h", "util.h", and "archdep.h" as needed
 * delete #include <sys/syscall.h> from syscall.h
 * only pull in <sys/stat.h> to the three files that use _dl_fstat(),
   forward declare struct stat in syscall.h for the others
 * NBBY is for <sys/select.h> macros; just use '8' in dl_printf.c
 * <machine/vmparam.h> is only needed on i386; conditionalize it
 * stop using __LDPGSZ: use _MAX_PAGE_SHIFT (already used by malloc.c)
   where necessary
 * delete other bogus #includes, order legit per style: <sys/*> then
   <*/*>, then <*>, then "*"

dir.c improvement from jsg@
ok and testing assistance deraadt@

/*	$OpenBSD: trace.c,v 1.5 2022/01/08 06:49:41 guenther Exp $	*/

/*
 * Copyright (c) 2013 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>

#include "syscall.h"
#include "util.h"
#include "resolve.h"

/*
 * Library call tracing routines.
 */

static	int _dl_traceplt;

struct tracespec {
	int	inverse;	/* blacklist instead of whitelist */
	char	*spec;		/* comma separated spec entries */
};

static struct tracespec _dl_tracelib, _dl_tracefunc;

static const char *_dl_trace_parse_spec(const char *, struct tracespec *);
static int _dl_trace_match(const char *, struct tracespec *, int);

void
_dl_trace_setup(char **envp)
{
	const char *var;
	int inherit;

	var = _dl_getenv("LD_TRACE_PLT", envp);
	if (var == NULL)
		return;

	if (!_dl_trust) {
		_dl_unsetenv("LD_TRACE_PLT", envp);
		return;
	}

	_dl_traceplt = 1;

	/*
	 * We expect LD_TRACE_PLT to be empty unless trace inheritance has
	 * been setup by ltrace(1).  We can then clear the environment
	 * variable to avoid useless work in our children, should we fork
	 * any.
	 */
	inherit = *var != '\0';
	if (!inherit)
		_dl_unsetenv("LD_TRACE_PLT", envp);

	/*
	 * Check for a fine-grained trace specification, and extract the
	 * library and function lists, if any.
	 */

	var = _dl_getenv("LD_TRACE_PLTSPEC", envp);
	if (var != NULL) {
		var = _dl_trace_parse_spec(var, &_dl_tracelib);
		(void)_dl_trace_parse_spec(var, &_dl_tracefunc);
		if (!inherit)
			_dl_unsetenv("LD_TRACE_PLTSPEC", envp);
	}
}

void
_dl_trace_object_setup(elf_object_t *object)
{
	const char *basename, *slash;

	object->traced = 0;

	if (_dl_traceplt) {
		basename = object->load_name;
		while (*basename == '/') {
			basename++;
			slash = _dl_strchr(basename, '/');
			if (slash == NULL)
				break;
			basename = slash;
		}
		if (_dl_trace_match(basename, &_dl_tracelib, 1))
			object->traced = 1;
	}
}

int
_dl_trace_plt(const elf_object_t *object, const char *symname)
{
	if (!_dl_trace_match(symname, &_dl_tracefunc, 0))
		return 0;

	_dl_utrace(".plt object",
	    object->load_name, _dl_strlen(object->load_name));
	_dl_utrace(".plt symbol",
	    symname, _dl_strlen(symname));

	return 1;	/* keep tracing */
}

/*
 * Extract a trace specification field, and setup the tracespec struct
 * accordingly.
 */
const char *
_dl_trace_parse_spec(const char *var, struct tracespec *spec)
{
	const char *start, *end;

	if (*var == '!') {
		spec->inverse = 1;
		var++;
	}

	start = var;
	end = _dl_strchr(start, ':');
	if (end == NULL)
		end = start + _dl_strlen(start);

	if (end != start) {
		spec->spec = _dl_malloc(1 + end - start);
		if (spec->spec == NULL)
			_dl_oom();

		_dl_bcopy(start, spec->spec, end - start);
		spec->spec[end - start] = '\0';
	}

	if (*end == ':')
		end++;

	return end;
}

/*
 * Check if a given name matches a trace specification list.
 */
static int
_dl_trace_match(const char *name, struct tracespec *spec, int allow_so)
{
	const char *list, *end, *next;
	size_t span;
	int match;

	/* no spec means trace everything */
	if (spec->spec == NULL)
		return 1;

	match = 0;
	list = spec->spec;
	end = list + _dl_strlen(list);

	while (*list != '\0') {
		next = _dl_strchr(list, ',');
		if (next == NULL)
			next = end;

		span = next - list;
		if (span != 0 && *(next - 1) == '*')
			span--;

		if (span != 0 && _dl_strncmp(name, list, span) == 0) {
			/*
			 * If the object name matches the specification
			 * fragment so far, it's a match if:
			 *   + the specification ends in a star (wildcard
			 *     match)
			 *   + there are no remaining chars in both the
			 *     object name and the specification (exact
			 *     match)
			 *   + the specification ends (no star) and the
			 *     object name continues with ".so" (radix
			 *     match) and `allow_so' is nonzero.
			 */
			if (list[span] == '*' ||
			    name[span] == '\0' ||
			    (allow_so &&
			    _dl_strncmp(name + span, ".so", 3) == 0)) {
				match = 1;
				break;
			}
		}

		while (*next == ',')
			next++;
		list = next;
	}

	return spec->inverse ? !match : match;
}