diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2013-06-01 09:57:59 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2013-06-01 09:57:59 +0000 |
commit | 68aee1affc9e9f623b3824770f05f34d7c2fe60a (patch) | |
tree | ff6bb4ec4eab9b40d691aebff013e69dbf58683a /libexec/ld.so/trace.c | |
parent | ef21a2cb5b0d81e925c90a50449ba06a311298e0 (diff) |
Introduce ltrace(1). This tool works with ld.so to inject utrace record for
each plt call, allowing to trace a binary linked against shared library at the
public function call level.
To do so, ltrace(1) sets up some environment variables to enable plt tracing
in ld.so, and invokes ktrace(2) for utrace events. ld.so will force lazy
binding and will send an utrace record in the plt resolver, without updating
the plt.
Minimal filtering capabilities are provided, inspired by Solaris' truss -u,
to limit tracing to libraries and/or symbol names. Non-traced libraries and
symbols will have the regular resolver processing, with the expected plt
update.
"Get it in" deraadt
Diffstat (limited to 'libexec/ld.so/trace.c')
-rw-r--r-- | libexec/ld.so/trace.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/libexec/ld.so/trace.c b/libexec/ld.so/trace.c new file mode 100644 index 00000000000..12f4f63bc64 --- /dev/null +++ b/libexec/ld.so/trace.c @@ -0,0 +1,209 @@ +/* $OpenBSD: trace.c,v 1.1 2013/06/01 09:57:55 miod 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 <sys/param.h> + +#include <string.h> + +#include "syscall.h" +#include "resolve.h" +#include "util.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_exit(8); + + _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; +} |