blob: b4ae2c09f0a7333ce6cce2b114f81fae5f7f88d9 [file] [log] [blame]
Petr Machata41498912012-12-04 17:57:34 +01001/*
2 * This file is part of ltrace.
Petr Machata33f0ca52013-01-07 19:42:13 +01003 * Copyright (C) 2012, 2013 Petr Machata
Petr Machata41498912012-12-04 17:57:34 +01004 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 */
20
21#define _POSIX_C_SOURCE 200809L
22#include <sys/types.h>
23#include <alloca.h>
24#include <errno.h>
25#include <pwd.h>
Petr Machatab3d61802013-10-16 17:02:20 +020026#include <stdbool.h>
Petr Machata41498912012-12-04 17:57:34 +010027#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
Petr Machatab3d61802013-10-16 17:02:20 +020032#include "backend.h"
33#include "breakpoint.h"
Petr Machata41498912012-12-04 17:57:34 +010034#include "dict.h"
Petr Machatab3d61802013-10-16 17:02:20 +020035#include "fetch.h"
36#include "library.h"
Petr Machata41498912012-12-04 17:57:34 +010037#include "options.h"
Petr Machatab3d61802013-10-16 17:02:20 +020038#include "prototype.h"
39#include "sysdep.h"
40#include "type.h"
41#include "value.h"
42#include "vect.h"
Petr Machata41498912012-12-04 17:57:34 +010043
44static char *
45append(const char *str1, const char *str2)
46{
47 char *ret = malloc(strlen(str1) + strlen(str2) + 2);
48 if (ret == NULL)
49 return ret;
50 strcpy(stpcpy(ret, str1), str2);
51 return ret;
52}
53
54static void
55add_dir(struct vect *dirs, const char *str1, const char *str2)
56{
57 char *dir = append(str1, str2);
58 if (dir != NULL
59 && VECT_PUSHBACK(dirs, &dir) < 0)
60 fprintf(stderr,
61 "Couldn't store candidate config directory %s%s: %s.\n",
62 str1, str2, strerror(errno));
63}
64
65static enum callback_status
66add_dir_component_cb(struct opt_F_t *entry, void *data)
67{
68 struct vect *dirs = data;
69 if (opt_F_get_kind(entry) == OPT_F_DIR)
70 add_dir(dirs, entry->pathname, "/ltrace");
71 return CBS_CONT;
72}
73
74static void
75destroy_opt_F_cb(struct opt_F_t *entry, void *data)
76{
77 opt_F_destroy(entry);
78}
79
Petr Machata364753a2012-12-06 18:28:17 +010080static char *g_home_dir = NULL;
81
Petr Machata41498912012-12-04 17:57:34 +010082int
83os_get_config_dirs(int private, const char ***retp)
84{
85 /* Vector of char *. Contains first pointers to local paths,
86 * then NULL, then pointers to system paths, then another
87 * NULL. SYS_START points to the beginning of the second
88 * part. */
89 static struct vect dirs;
90 static ssize_t sys_start = 0;
91
92again:
93 if (sys_start != 0) {
94 if (sys_start == -1)
95 return -1;
96
Petr Machata364753a2012-12-06 18:28:17 +010097 if (retp != NULL) {
98 if (private)
99 *retp = VECT_ELEMENT(&dirs, const char *, 0);
100 else
101 *retp = VECT_ELEMENT(&dirs, const char *,
102 (size_t)sys_start);
103 }
104
Petr Machata41498912012-12-04 17:57:34 +0100105 return 0;
106 }
107
108 VECT_INIT(&dirs, char *);
109
110 char *home = getenv("HOME");
111 if (home == NULL) {
112 struct passwd *pwd = getpwuid(getuid());
113 if (pwd != NULL)
114 home = pwd->pw_dir;
115 }
Petr Machata364753a2012-12-06 18:28:17 +0100116
Petr Machata33f0ca52013-01-07 19:42:13 +0100117 size_t home_len = home != NULL ? strlen(home) : 0;
118
Petr Machata41498912012-12-04 17:57:34 +0100119 /* The values coming from getenv and getpwuid may not be
120 * persistent. */
Petr Machata364753a2012-12-06 18:28:17 +0100121 if (home != NULL) {
122 g_home_dir = strdup(home);
123 if (g_home_dir != NULL) {
124 home = g_home_dir;
125 } else {
Petr Machata33f0ca52013-01-07 19:42:13 +0100126 char *tmp = alloca(home_len + 1);
Petr Machata364753a2012-12-06 18:28:17 +0100127 strcpy(tmp, home);
128 home = tmp;
129 }
Petr Machata41498912012-12-04 17:57:34 +0100130 }
131
132 char *xdg_home = getenv("XDG_CONFIG_HOME");
133 if (xdg_home == NULL && home != NULL) {
Petr Machata33f0ca52013-01-07 19:42:13 +0100134 xdg_home = alloca(home_len + sizeof "/.config");
135 sprintf(xdg_home, "%s/.config", home);
Petr Machata41498912012-12-04 17:57:34 +0100136 }
137 if (xdg_home != NULL)
138 add_dir(&dirs, xdg_home, "/ltrace");
139 if (home != NULL)
140 add_dir(&dirs, home, "/.ltrace");
141
142 char *delim = NULL;
143 if (VECT_PUSHBACK(&dirs, &delim) < 0) {
144 fail:
145 /* This can't work :( */
146 fprintf(stderr,
147 "Couldn't initialize list of config directories: %s.\n",
148 strerror(errno));
149 VECT_DESTROY(&dirs, const char *, dict_dtor_string, NULL);
150 sys_start = -1;
151 return -1;
152 }
153 sys_start = vect_size(&dirs);
154
155 /* """preference-ordered set of base directories to search for
156 * configuration files in addition to the $XDG_CONFIG_HOME
157 * base directory. The directories in $XDG_CONFIG_DIRS should
158 * be seperated with a colon ':'.""" */
159 char *xdg_sys = getenv("XDG_CONFIG_DIRS");
160 if (xdg_sys != NULL) {
161 struct vect v;
162 VECT_INIT(&v, struct opt_F_t);
163 if (parse_colon_separated_list(xdg_sys, &v) < 0
164 || VECT_EACH(&v, struct opt_F_t, NULL,
165 add_dir_component_cb, &dirs) != NULL)
166 fprintf(stderr,
167 "Error processing $XDG_CONFIG_DIRS '%s': %s\n",
168 xdg_sys, strerror(errno));
169 VECT_DESTROY(&v, struct opt_F_t, destroy_opt_F_cb, NULL);
170 }
171
Petr Machataaa3db6b2013-10-23 00:52:13 +0200172 /* PKGDATADIR is passed via -D when compiling. */
173 const char *pkgdatadir = PKGDATADIR;
174 if (pkgdatadir != NULL)
175 add_dir(&dirs, pkgdatadir, "");
Petr Machata41498912012-12-04 17:57:34 +0100176
177 if (VECT_PUSHBACK(&dirs, &delim) < 0)
178 goto fail;
179
180 goto again;
181}
Petr Machata364753a2012-12-06 18:28:17 +0100182
183int
Petr Machataaa3db6b2013-10-23 00:52:13 +0200184os_get_ltrace_conf_filenames(struct vect *retp)
Petr Machata364753a2012-12-06 18:28:17 +0100185{
Petr Machataaa3db6b2013-10-23 00:52:13 +0200186 char *homepath = NULL;
187 char *syspath = NULL;
188
189#define FN ".ltrace.conf"
Petr Machata364753a2012-12-06 18:28:17 +0100190 if (g_home_dir == NULL)
191 os_get_config_dirs(0, NULL);
Petr Machataaa3db6b2013-10-23 00:52:13 +0200192
193 if (g_home_dir != NULL) {
194 homepath = malloc(strlen(g_home_dir) + 1 + sizeof FN);
195 if (homepath == NULL
196 || sprintf(homepath, "%s/%s", g_home_dir, FN) < 0) {
197 fail:
198 free(syspath);
199 free(homepath);
200 return -1;
201 }
202 }
203
204 /* SYSCONFDIR is passed via -D when compiling. */
205 const char *sysconfdir = SYSCONFDIR;
206 if (sysconfdir != NULL && *sysconfdir != '\0') {
207 /* No +1, we skip the initial period. */
208 syspath = malloc(strlen(sysconfdir) + sizeof FN);
209 if (syspath == NULL
210 || sprintf(syspath, "%s/%s", sysconfdir, FN + 1) < 0)
211 goto fail;
212 }
213
214 if (VECT_PUSHBACK(retp, &homepath) < 0
215 || VECT_PUSHBACK(retp, &syspath) < 0)
216 goto fail;
217
218 return 0;
Petr Machata364753a2012-12-06 18:28:17 +0100219}
Petr Machatab3d61802013-10-16 17:02:20 +0200220
221static struct prototype *
222void_prototype(void)
223{
224 static struct prototype ret;
225 if (ret.return_info == NULL) {
226 prototype_init(&ret);
227 ret.return_info = type_get_voidptr();
228 ret.own_return_info = 0;
229 }
230 return &ret;
231}
232
233int
234os_library_symbol_init(struct library_symbol *libsym)
235{
236 libsym->os = (struct os_library_symbol_data){};
237 return 0;
238}
239
240void
241os_library_symbol_destroy(struct library_symbol *libsym)
242{
243}
244
245int
246os_library_symbol_clone(struct library_symbol *retp,
247 struct library_symbol *libsym)
248{
249 retp->os = libsym->os;
250 return 0;
251}
252
253enum plt_status
254os_elf_add_func_entry(struct process *proc, struct ltelf *lte,
255 const GElf_Sym *sym,
256 arch_addr_t addr, const char *name,
257 struct library_symbol **ret)
258{
259 if (GELF_ST_TYPE(sym->st_info) == STT_FUNC)
260 return PLT_DEFAULT;
261
262 bool ifunc = false;
263#ifdef STT_GNU_IFUNC
264 ifunc = GELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC;
265#endif
266
267 if (ifunc) {
268#define S ".IFUNC"
269 char *tmp_name = malloc(strlen(name) + sizeof S);
270 struct library_symbol *tmp = malloc(sizeof *tmp);
271 if (tmp_name == NULL || tmp == NULL) {
272 fail:
273 free(tmp_name);
274 free(tmp);
275 return PLT_FAIL;
276 }
277 sprintf(tmp_name, "%s%s", name, S);
278#undef S
279
280 if (library_symbol_init(tmp, addr, tmp_name, 1,
281 LS_TOPLT_NONE) < 0)
282 goto fail;
283 tmp->proto = void_prototype();
284 tmp->os.is_ifunc = 1;
285
286 *ret = tmp;
287 return PLT_OK;
288 }
289
290 *ret = NULL;
291 return PLT_OK;
292}
293
294static enum callback_status
295libsym_at_address(struct library_symbol *libsym, void *addrp)
296{
297 arch_addr_t addr = *(arch_addr_t *)addrp;
298 return CBS_STOP_IF(addr == libsym->enter_addr);
299}
300
301static void
302ifunc_ret_hit(struct breakpoint *bp, struct process *proc)
303{
304 struct fetch_context *fetch = fetch_arg_init(LT_TOF_FUNCTION, proc,
305 type_get_voidptr());
306 if (fetch == NULL)
307 return;
308
309 struct breakpoint *nbp = NULL;
310 int own_libsym = 0;
311
312 struct value value;
313 value_init(&value, proc, NULL, type_get_voidptr(), 0);
314 size_t sz = value_size(&value, NULL);
315 union {
316 uint64_t u64;
317 uint32_t u32;
318 arch_addr_t a;
319 } u;
320
321 if (fetch_retval(fetch, LT_TOF_FUNCTIONR, proc,
322 value.type, &value) < 0
323 || sz > 8 /* Captures failure as well. */
324 || value_extract_buf(&value, (void *) &u, NULL) < 0) {
325 fail:
326 fprintf(stderr,
327 "Couldn't trace the function "
328 "indicated by IFUNC resolver.\n");
329 goto done;
330 }
331
332 assert(sz == 4 || sz == 8);
333 /* XXX double casts below: */
334 if (sz == 4)
335 u.a = (arch_addr_t)(uintptr_t)u.u32;
336 else
337 u.a = (arch_addr_t)(uintptr_t)u.u64;
338
339 assert(bp->os.ret_libsym != NULL);
340
341 struct library *lib = bp->os.ret_libsym->lib;
342 assert(lib != NULL);
343
344 /* Look if we already have a symbol with this address.
345 * Otherwise create a new one. */
346 struct library_symbol *libsym
347 = library_each_symbol(lib, NULL, libsym_at_address, &u.a);
348 if (libsym == NULL) {
349 libsym = malloc(sizeof *libsym);
350 char *name = strdup(bp->os.ret_libsym->name);
351
352 if (libsym == NULL
353 || name == NULL
354 || library_symbol_init(libsym, u.a, name, 1,
355 LS_TOPLT_NONE) < 0) {
356 free(libsym);
357 free(name);
358 goto fail;
359 }
360
361 /* Snip the .IFUNC token. */
362 *strrchr(name, '.') = 0;
363
364 own_libsym = 1;
365 library_add_symbol(lib, libsym);
366 }
367
368 nbp = malloc(sizeof *bp);
369 if (nbp == NULL || breakpoint_init(nbp, proc, u.a, libsym) < 0)
370 goto fail;
371
372 /* If there already is a breakpoint at that address, that is
373 * suspicious, but whatever. */
374 struct breakpoint *pre_bp = insert_breakpoint(proc, nbp);
375 if (pre_bp == NULL)
376 goto fail;
377 if (pre_bp == nbp) {
378 /* PROC took our breakpoint, so these resources are
379 * not ours anymore. */
380 nbp = NULL;
381 own_libsym = 0;
382 }
383
384done:
385 free(nbp);
386 if (own_libsym) {
387 library_symbol_destroy(libsym);
388 free(libsym);
389 }
390 fetch_arg_done(fetch);
391}
392
393static int
394create_ifunc_ret_bp(struct breakpoint **ret,
395 struct breakpoint *bp, struct process *proc)
396{
397 *ret = create_default_return_bp(proc);
398 if (*ret == NULL)
399 return -1;
400 static struct bp_callbacks cbs = {
401 .on_hit = ifunc_ret_hit,
402 };
403 breakpoint_set_callbacks(*ret, &cbs);
404
405 (*ret)->os.ret_libsym = bp->libsym;
406
407 return 0;
408}
409
410int
411os_breakpoint_init(struct process *proc, struct breakpoint *bp)
412{
413 if (bp->libsym != NULL && bp->libsym->os.is_ifunc) {
414 static struct bp_callbacks cbs = {
415 .get_return_bp = create_ifunc_ret_bp,
416 };
417 breakpoint_set_callbacks(bp, &cbs);
418 }
419 return 0;
420}
421
422void
423os_breakpoint_destroy(struct breakpoint *bp)
424{
425}
426
427int
428os_breakpoint_clone(struct breakpoint *retp, struct breakpoint *bp)
429{
430 return 0;
431}