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