1 /* $OpenBSD: trace.c,v 1.2 2014/07/06 17:33:10 otto Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/param.h> 21 22 #include "syscall.h" 23 #include "resolve.h" 24 #include "util.h" 25 26 /* 27 * Library call tracing routines. 28 */ 29 30 static int _dl_traceplt; 31 32 struct tracespec { 33 int inverse; /* blacklist instead of whitelist */ 34 char *spec; /* comma separated spec entries */ 35 }; 36 37 static struct tracespec _dl_tracelib, _dl_tracefunc; 38 39 static const char *_dl_trace_parse_spec(const char *, struct tracespec *); 40 static int _dl_trace_match(const char *, struct tracespec *, int); 41 42 void 43 _dl_trace_setup(char **envp) 44 { 45 const char *var; 46 int inherit; 47 48 var = _dl_getenv("LD_TRACE_PLT", envp); 49 if (var == NULL) 50 return; 51 52 if (!_dl_trust) { 53 _dl_unsetenv("LD_TRACE_PLT", envp); 54 return; 55 } 56 57 _dl_traceplt = 1; 58 59 /* 60 * We expect LD_TRACE_PLT to be empty unless trace inheritance has 61 * been setup by ltrace(1). We can then clear the environment 62 * variable to avoid useless work in our children, should we fork 63 * any. 64 */ 65 inherit = *var != '\0'; 66 if (!inherit) 67 _dl_unsetenv("LD_TRACE_PLT", envp); 68 69 /* 70 * Check for a fine-grained trace specification, and extract the 71 * library and function lists, if any. 72 */ 73 74 var = _dl_getenv("LD_TRACE_PLTSPEC", envp); 75 if (var != NULL) { 76 var = _dl_trace_parse_spec(var, &_dl_tracelib); 77 (void)_dl_trace_parse_spec(var, &_dl_tracefunc); 78 if (!inherit) 79 _dl_unsetenv("LD_TRACE_PLTSPEC", envp); 80 } 81 } 82 83 void 84 _dl_trace_object_setup(elf_object_t *object) 85 { 86 const char *basename, *slash; 87 88 object->traced = 0; 89 90 if (_dl_traceplt) { 91 basename = object->load_name; 92 while (*basename == '/') { 93 basename++; 94 slash = _dl_strchr(basename, '/'); 95 if (slash == NULL) 96 break; 97 basename = slash; 98 } 99 if (_dl_trace_match(basename, &_dl_tracelib, 1)) 100 object->traced = 1; 101 } 102 } 103 104 int 105 _dl_trace_plt(const elf_object_t *object, const char *symname) 106 { 107 if (!_dl_trace_match(symname, &_dl_tracefunc, 0)) 108 return 0; 109 110 _dl_utrace(".plt object", 111 object->load_name, _dl_strlen(object->load_name)); 112 _dl_utrace(".plt symbol", 113 symname, _dl_strlen(symname)); 114 115 return 1; /* keep tracing */ 116 } 117 118 /* 119 * Extract a trace specification field, and setup the tracespec struct 120 * accordingly. 121 */ 122 const char * 123 _dl_trace_parse_spec(const char *var, struct tracespec *spec) 124 { 125 const char *start, *end; 126 127 if (*var == '!') { 128 spec->inverse = 1; 129 var++; 130 } 131 132 start = var; 133 end = _dl_strchr(start, ':'); 134 if (end == NULL) 135 end = start + _dl_strlen(start); 136 137 if (end != start) { 138 spec->spec = _dl_malloc(1 + end - start); 139 if (spec->spec == NULL) 140 _dl_exit(8); 141 142 _dl_bcopy(start, spec->spec, end - start); 143 spec->spec[end - start] = '\0'; 144 } 145 146 if (*end == ':') 147 end++; 148 149 return end; 150 } 151 152 /* 153 * Check if a given name matches a trace specification list. 154 */ 155 static int 156 _dl_trace_match(const char *name, struct tracespec *spec, int allow_so) 157 { 158 const char *list, *end, *next; 159 size_t span; 160 int match; 161 162 /* no spec means trace everything */ 163 if (spec->spec == NULL) 164 return 1; 165 166 match = 0; 167 list = spec->spec; 168 end = list + _dl_strlen(list); 169 170 while (*list != '\0') { 171 next = _dl_strchr(list, ','); 172 if (next == NULL) 173 next = end; 174 175 span = next - list; 176 if (span != 0 && *(next - 1) == '*') 177 span--; 178 179 if (span != 0 && _dl_strncmp(name, list, span) == 0) { 180 /* 181 * If the object name matches the specification 182 * fragment so far, it's a match if: 183 * + the specification ends in a star (wildcard 184 * match) 185 * + there are no remaining chars in both the 186 * object name and the specification (exact 187 * match) 188 * + the specification ends (no star) and the 189 * object name continues with ".so" (radix 190 * match) and `allow_so' is nonzero. 191 */ 192 if (list[span] == '*' || 193 name[span] == '\0' || 194 (allow_so && 195 _dl_strncmp(name + span, ".so", 3) == 0)) { 196 match = 1; 197 break; 198 } 199 } 200 201 while (*next == ',') 202 next++; 203 list = next; 204 } 205 206 return spec->inverse ? !match : match; 207 } 208