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