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