xref: /openbsd-src/libexec/ld.so/trace.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
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