1/* Trace calls through PLTs and show caller, callee, and parameters.
2 Copyright (C) 2011-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <err.h>
20#include <error.h>
21#include <fcntl.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <sys/param.h>
27#include <sys/uio.h>
28
29#include <ldsodefs.h>
30
31
32extern const char *__progname;
33extern const char *__progname_full;
34
35
36/* List of objects to trace calls from. */
37static const char *fromlist;
38/* List of objects to trace calls to. */
39static const char *tolist;
40
41/* If non-zero, also trace returns of the calls. */
42static int do_exit;
43/* If non-zero print PID for each line. */
44static int print_pid;
45
46/* The output stream to use. */
47static FILE *out_file;
48
49
50static int
51match_pid (pid_t pid, const char *which)
52{
53 if (which == NULL || which[0] == '\0')
54 {
55 print_pid = 1;
56 return 1;
57 }
58
59 char *endp;
60 unsigned long n = strtoul (nptr: which, endptr: &endp, base: 0);
61 return *endp == '\0' && n == pid;
62}
63
64
65static void
66init (void)
67{
68 fromlist = getenv (name: "SOTRUSS_FROMLIST");
69 if (fromlist != NULL && fromlist[0] == '\0')
70 fromlist = NULL;
71 tolist = getenv (name: "SOTRUSS_TOLIST");
72 if (tolist != NULL && tolist[0] == '\0')
73 tolist = NULL;
74 do_exit = (getenv (name: "SOTRUSS_EXIT") ?: "")[0] != '\0';
75
76 /* Determine whether this process is supposed to be traced and if
77 yes, whether we should print into a file. */
78 const char *which_process = getenv (name: "SOTRUSS_WHICH");
79 pid_t pid = getpid ();
80 int out_fd = -1;
81 if (match_pid (pid, which: which_process))
82 {
83 const char *out_filename = getenv (name: "SOTRUSS_OUTNAME");
84
85 if (out_filename != NULL && out_filename[0] != 0)
86 {
87 size_t out_filename_len = strlen (s: out_filename) + 13;
88 char fullname[out_filename_len];
89 char *endp = stpcpy (fullname, out_filename);
90 if (which_process == NULL || which_process[0] == '\0')
91 snprintf (s: endp, maxlen: 13, format: ".%ld", (long int) pid);
92
93 out_fd = open64 (file: fullname, O_RDWR | O_CREAT | O_TRUNC, 0666);
94 if (out_fd != -1)
95 print_pid = 0;
96 }
97 }
98
99 /* If we do not write into a file write to stderr. Duplicate the
100 descriptor so that we can keep printing in case the program
101 closes stderr. Try first to allocate a descriptor with a value
102 usually not used as to minimize interference with the
103 program. */
104 if (out_fd == -1)
105 {
106 out_fd = fcntl64 (STDERR_FILENO, F_DUPFD, 1000);
107 if (out_fd == -1)
108 out_fd = dup (STDERR_FILENO);
109 }
110
111 if (out_fd != -1)
112 {
113 /* Convert file descriptor into a stream. */
114 out_file = fdopen (fd: out_fd, modes: "w");
115 if (out_file != NULL)
116 setlinebuf (out_file);
117 }
118}
119
120
121/* Audit interface verification. We also initialize everything if
122 everything checks out OK. */
123unsigned int
124la_version (unsigned int v)
125{
126 if (v != LAV_CURRENT)
127 error (status: 1, errnum: 0, format: "cannot handle interface version %u", v);
128
129 init ();
130
131 return v;
132}
133
134
135/* Check whether a file name is on the colon-separated list of file
136 names. */
137static unsigned int
138match_file (const char *list, const char *name, size_t name_len,
139 unsigned int mask)
140{
141 if (list[0] == '\0')
142 return 0;
143
144 const char *cp = list;
145 while (1)
146 {
147 if (strncmp (s1: cp, s2: name, n: name_len) == 0
148 && (cp[name_len] == ':' || cp[name_len] == '\0'))
149 return mask;
150
151 cp = strchr (s: cp, c: ':');
152 if (cp == NULL)
153 return 0;
154 ++cp;
155 }
156}
157
158
159unsigned int
160la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
161{
162 if (out_file == NULL)
163 return 0;
164
165 const char *full_name = map->l_name ?: "";
166 if (full_name[0] == '\0')
167 full_name = __progname_full;
168 size_t full_name_len = strlen (s: full_name);
169 const char *base_name = basename (filename: full_name);
170 if (base_name[0] == '\0')
171 base_name = __progname;
172 size_t base_name_len = strlen (s: base_name);
173
174 int result = 0;
175 const char *print_name = NULL;
176 for (struct libname_list *l = map->l_libname; l != NULL; l = l->next)
177 {
178 if (print_name == NULL || (print_name[0] == '/' && l->name[0] != '/'))
179 print_name = l->name;
180
181 if (fromlist != NULL)
182 result |= match_file (list: fromlist, name: l->name, name_len: strlen (s: l->name),
183 mask: LA_FLG_BINDFROM);
184
185 if (tolist != NULL)
186 result |= match_file (list: tolist, name: l->name, name_len: strlen (s: l->name),mask: LA_FLG_BINDTO);
187 }
188
189 if (print_name == NULL)
190 print_name = base_name;
191 if (print_name[0] == '\0')
192 print_name = __progname;
193
194 /* We cannot easily get to the object name in the PLT handling
195 functions. Use the cookie to get the string pointer passed back
196 to us. */
197 *cookie = (uintptr_t) print_name;
198
199 /* The object name has to be on the list of objects to trace calls
200 from or that list must be empty. In the latter case we trace
201 only calls from the main binary. */
202 if (fromlist == NULL)
203 result |= map->l_name[0] == '\0' ? LA_FLG_BINDFROM : 0;
204 else
205 result |= (match_file (list: fromlist, name: full_name, name_len: full_name_len,
206 mask: LA_FLG_BINDFROM)
207 | match_file (list: fromlist, name: base_name, name_len: base_name_len,
208 mask: LA_FLG_BINDFROM));
209
210 /* The object name has to be on the list of objects to trace calls
211 to or that list must be empty. In the latter case we trace
212 calls toall objects. */
213 if (tolist == NULL)
214 result |= LA_FLG_BINDTO;
215 else
216 result |= (match_file (list: tolist, name: full_name, name_len: full_name_len, mask: LA_FLG_BINDTO)
217 | match_file (list: tolist, name: base_name, name_len: base_name_len, mask: LA_FLG_BINDTO));
218
219 return result;
220}
221
222
223#if __ELF_NATIVE_CLASS == 32
224# define la_symbind la_symbind32
225typedef Elf32_Sym Elf_Sym;
226#else
227# define la_symbind la_symbind64
228typedef Elf64_Sym Elf_Sym;
229#endif
230
231uintptr_t
232la_symbind (Elf_Sym *sym, unsigned int ndx, uintptr_t *refcook,
233 uintptr_t *defcook, unsigned int *flags, const char *symname)
234{
235 if (*flags & LA_SYMB_NOPLTENTER)
236 warnx (format: "cannot trace PLT enter (bind-now enabled)");
237
238 if (do_exit && *flags & LA_SYMB_NOPLTEXIT)
239 warnx (format: "cannot trace PLT exit (bind-now enabled)");
240
241 if (!do_exit)
242 *flags = LA_SYMB_NOPLTEXIT;
243
244 return sym->st_value;
245}
246
247
248static void
249print_enter (uintptr_t *refcook, uintptr_t *defcook, const char *symname,
250 unsigned long int reg1, unsigned long int reg2,
251 unsigned long int reg3, unsigned int flags)
252{
253 char buf[3 * sizeof (pid_t) + 3];
254 buf[0] = '\0';
255 if (print_pid)
256 snprintf (s: buf, maxlen: sizeof (buf), format: "%5ld: ", (long int) getpid ());
257
258 fprintf (stream: out_file, format: "%s%15s -> %-15s:%s%s(0x%lx, 0x%lx, 0x%lx)\n",
259 buf, (char *) *refcook, (char *) *defcook,
260 (flags & LA_SYMB_NOPLTEXIT) ? "*" : " ", symname, reg1, reg2, reg3);
261}
262
263
264#ifdef __i386__
265Elf32_Addr
266la_i86_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
267 unsigned int ndx __attribute__ ((unused)),
268 uintptr_t *refcook, uintptr_t *defcook,
269 La_i86_regs *regs, unsigned int *flags,
270 const char *symname, long int *framesizep)
271{
272 unsigned long int *sp = (unsigned long int *) regs->lr_esp;
273
274 print_enter (refcook, defcook, symname, sp[1], sp[2], sp[3], *flags);
275
276 /* No need to copy anything, we will not need the parameters in any case. */
277 *framesizep = 0;
278
279 return sym->st_value;
280}
281#elif defined __x86_64__
282Elf64_Addr
283la_x86_64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
284 unsigned int ndx __attribute__ ((unused)),
285 uintptr_t *refcook, uintptr_t *defcook,
286 La_x86_64_regs *regs, unsigned int *flags,
287 const char *symname, long int *framesizep)
288{
289 print_enter (refcook, defcook, symname,
290 reg1: regs->lr_rdi, reg2: regs->lr_rsi, reg3: regs->lr_rdx, flags: *flags);
291
292 /* No need to copy anything, we will not need the parameters in any case. */
293 *framesizep = 0;
294
295 return sym->st_value;
296}
297#elif defined __sparc__ && !defined __arch64__
298Elf32_Addr
299la_sparc32_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
300 unsigned int ndx __attribute__ ((unused)),
301 uintptr_t *refcook, uintptr_t *defcook,
302 La_sparc32_regs *regs, unsigned int *flags,
303 const char *symname, long int *framesizep)
304{
305 print_enter (refcook, defcook, symname,
306 regs->lr_reg[0], regs->lr_reg[1], regs->lr_reg[2],
307 *flags);
308
309 /* No need to copy anything, we will not need the parameters in any case. */
310 *framesizep = 0;
311
312 return sym->st_value;
313}
314#elif defined __sparc__ && defined __arch64__
315Elf64_Addr
316la_sparc64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
317 unsigned int ndx __attribute__ ((unused)),
318 uintptr_t *refcook, uintptr_t *defcook,
319 La_sparc64_regs *regs, unsigned int *flags,
320 const char *symname, long int *framesizep)
321{
322 print_enter (refcook, defcook, symname,
323 regs->lr_reg[0], regs->lr_reg[1], regs->lr_reg[2],
324 *flags);
325
326 /* No need to copy anything, we will not need the parameters in any case. */
327 *framesizep = 0;
328
329 return sym->st_value;
330}
331#elif !defined HAVE_ARCH_PLTENTER
332# warning "pltenter for architecture not supported"
333#endif
334
335
336static void
337print_exit (uintptr_t *refcook, uintptr_t *defcook, const char *symname,
338 unsigned long int reg)
339{
340 char buf[3 * sizeof (pid_t) + 3];
341 buf[0] = '\0';
342 if (print_pid)
343 snprintf (s: buf, maxlen: sizeof (buf), format: "%5ld: ", (long int) getpid ());
344
345 fprintf (stream: out_file, format: "%s%15s -> %-15s:%s%s - 0x%lx\n",
346 buf, (char *) *refcook, (char *) *defcook, " ", symname, reg);
347}
348
349
350#ifdef __i386__
351unsigned int
352la_i86_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
353 uintptr_t *defcook, const struct La_i86_regs *inregs,
354 struct La_i86_retval *outregs, const char *symname)
355{
356 print_exit (refcook, defcook, symname, outregs->lrv_eax);
357
358 return 0;
359}
360#elif defined __x86_64__
361unsigned int
362la_x86_64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
363 uintptr_t *defcook, const struct La_x86_64_regs *inregs,
364 struct La_x86_64_retval *outregs, const char *symname)
365{
366 print_exit (refcook, defcook, symname, reg: outregs->lrv_rax);
367
368 return 0;
369}
370#elif defined __sparc__ && !defined __arch64__
371unsigned int
372la_sparc32_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
373 uintptr_t *defcook, const struct La_sparc32_regs *inregs,
374 struct La_sparc32_retval *outregs, const char *symname)
375{
376 print_exit (refcook, defcook, symname, outregs->lrv_reg[0]);
377
378 return 0;
379}
380#elif defined __sparc__ && defined __arch64__
381unsigned int
382la_sparc64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
383 uintptr_t *defcook, const struct La_sparc64_regs *inregs,
384 struct La_sparc64_retval *outregs, const char *symname)
385{
386 print_exit (refcook, defcook, symname, outregs->lrv_reg[0]);
387
388 return 0;
389}
390#elif !defined HAVE_ARCH_PLTEXIT
391# warning "pltexit for architecture not supported"
392#endif
393

source code of glibc/elf/sotruss-lib.c