diff options
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; +} |