library.c revision e8d9076a97f6617868466a99bd18e11e3f6389ac
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
4 * Copyright (C) 2001,2009 Juan Cespedes
5 * Copyright (C) 2006 Ian Wienand
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#include <stdlib.h>
24#include <string.h>
25#include <assert.h>
26
27#include "library.h"
28#include "proc.h" // for enum callback_status
29#include "debug.h"
30
31unsigned int
32target_address_hash(const void *key)
33{
34	/* XXX this assumes that key is passed by value.  */
35	union {
36		target_address_t addr;
37		unsigned int ints[sizeof(target_address_t)
38				  / sizeof(unsigned int)];
39	} u = { .addr = (target_address_t)key };
40
41	size_t i;
42	unsigned int h = 0;
43	for (i = 0; i < sizeof(u.ints) / sizeof(*u.ints); ++i)
44		h ^= dict_key2hash_int(u.ints + i);
45	return h;
46}
47
48int
49target_address_cmp(const void *key1, const void *key2)
50{
51	/* XXX this assumes that key is passed by value.  */
52	target_address_t addr1 = (target_address_t)key1;
53	target_address_t addr2 = (target_address_t)key2;
54	return addr1 < addr2 ? 1
55	     : addr1 > addr2 ? -1 : 0;
56}
57
58/* If the other symbol owns the name, we need to make the copy, so
59 * that the life-times of the two symbols are not dependent on each
60 * other.  */
61static int
62strdup_if_owned(const char **retp, const char *str, int owned)
63{
64	if (!owned || str == NULL) {
65		*retp = str;
66		return 0;
67	} else {
68		*retp = strdup(str);
69		return *retp != NULL ? 0 : -1;
70	}
71}
72
73void
74private_library_symbol_init(struct library_symbol *libsym,
75			    target_address_t addr,
76			    const char *name, int own_name,
77			    enum toplt type_of_plt)
78{
79	libsym->next = NULL;
80	libsym->lib = NULL;
81	libsym->plt_type = type_of_plt;
82	libsym->name = name;
83	libsym->own_name = own_name;
84	libsym->enter_addr = (void *)(uintptr_t)addr;
85}
86
87int
88library_symbol_init(struct library_symbol *libsym,
89		    target_address_t addr, const char *name, int own_name,
90		    enum toplt type_of_plt)
91{
92	private_library_symbol_init(libsym, addr, name, own_name, type_of_plt);
93	return 0;
94
95}
96
97void
98library_symbol_destroy(struct library_symbol *libsym)
99{
100	if (libsym != NULL)
101		library_symbol_set_name(libsym, NULL, 0);
102}
103
104int
105library_symbol_clone(struct library_symbol *retp, struct library_symbol *libsym)
106{
107	const char *name;
108	if (strdup_if_owned(&name, libsym->name, libsym->own_name) < 0)
109		return -1;
110
111	private_library_symbol_init(retp, libsym->enter_addr,
112				    name, libsym->own_name, libsym->plt_type);
113	return 0;
114}
115
116int
117library_symbol_cmp(struct library_symbol *a, struct library_symbol *b)
118{
119	if (a->enter_addr < b->enter_addr)
120		return -1;
121	if (a->enter_addr > b->enter_addr)
122		return 1;
123	if (a->name != NULL && b->name != NULL)
124		return strcmp(a->name, b->name);
125	if (a->name == NULL) {
126		if (b->name == NULL)
127			return 0;
128		return -1;
129	}
130	return 1;
131}
132
133void
134library_symbol_set_name(struct library_symbol *libsym,
135			const char *name, int own_name)
136{
137	if (libsym->own_name)
138		free((char *)libsym->name);
139	libsym->name = name;
140	libsym->own_name = own_name;
141}
142
143enum callback_status
144library_symbol_equal_cb(struct library_symbol *libsym, void *u)
145{
146	struct library_symbol *standard = u;
147	return library_symbol_cmp(libsym, standard) == 0 ? CBS_STOP : CBS_CONT;
148}
149
150void
151library_init(struct library *lib, enum library_type type)
152{
153	lib->next = NULL;
154	lib->soname = NULL;
155	lib->own_soname = 0;
156	lib->pathname = NULL;
157	lib->own_pathname = 0;
158	lib->symbols = NULL;
159	lib->type = type;
160}
161
162int
163library_clone(struct library *retp, struct library *lib)
164{
165	const char *soname = NULL;
166	const char *pathname;
167	if (strdup_if_owned(&soname, lib->soname, lib->own_soname) < 0
168	     || strdup_if_owned(&pathname,
169				lib->pathname, lib->own_pathname) < 0) {
170		if (lib->own_soname)
171			free((char *)soname);
172		return -1;
173	}
174
175	library_init(retp, lib->type);
176	library_set_soname(retp, soname, lib->own_soname);
177	library_set_soname(retp, pathname, lib->own_pathname);
178
179	struct library_symbol *it;
180	struct library_symbol **nsymp = &retp->symbols;
181	for (it = lib->symbols; it != NULL; it = it->next) {
182		*nsymp = malloc(sizeof(**nsymp));
183		if (*nsymp == NULL
184		    || library_symbol_clone(*nsymp, it) < 0) {
185			/* Release what we managed to allocate.  */
186			library_destroy(retp);
187			return -1;
188		}
189
190		(*nsymp)->lib = retp;
191		nsymp = &(*nsymp)->next;
192	}
193	return 0;
194}
195
196void
197library_destroy(struct library *lib)
198{
199	if (lib == NULL)
200		return;
201	library_set_soname(lib, NULL, 0);
202	library_set_pathname(lib, NULL, 0);
203
204	struct library_symbol *sym;
205	for (sym = lib->symbols; sym != NULL; ) {
206		struct library_symbol *next = sym->next;
207		library_symbol_destroy(sym);
208		free(sym);
209		sym = next;
210	}
211}
212
213void
214library_set_soname(struct library *lib, const char *new_name, int own_name)
215{
216	if (lib->own_soname)
217		free((char *)lib->soname);
218	lib->soname = new_name;
219	lib->own_soname = own_name;
220}
221
222void
223library_set_pathname(struct library *lib, const char *new_name, int own_name)
224{
225	if (lib->own_pathname)
226		free((char *)lib->pathname);
227	lib->pathname = new_name;
228	lib->own_pathname = own_name;
229}
230
231struct library_symbol *
232library_each_symbol(struct library *lib, struct library_symbol *start_after,
233		    enum callback_status (*cb)(struct library_symbol *, void *),
234		    void *data)
235{
236	struct library_symbol *it = start_after == NULL ? lib->symbols
237		: start_after->next;
238
239	while (it != NULL) {
240		struct library_symbol *next = it->next;
241
242		switch ((*cb)(it, data)) {
243		case CBS_FAIL:
244			/* XXX handle me  */
245		case CBS_STOP:
246			return it;
247		case CBS_CONT:
248			break;
249		}
250
251		it = next;
252	}
253
254	return NULL;
255}
256
257void
258library_add_symbol(struct library *lib, struct library_symbol *first)
259{
260	struct library_symbol *last;
261	for (last = first; last != NULL; ) {
262		last->lib = lib;
263		if (last->next != NULL)
264			last = last->next;
265		else
266			break;
267	}
268
269	assert(last->next == NULL);
270	last->next = lib->symbols;
271	lib->symbols = first;
272}
273
274enum callback_status
275library_named_cb(struct Process *proc, struct library *lib, void *name)
276{
277	if (name == lib->soname
278	    || strcmp(lib->soname, (char *)name) == 0)
279		return CBS_STOP;
280	else
281		return CBS_CONT;
282}
283
284enum callback_status
285library_with_key_cb(struct Process *proc, struct library *lib, void *keyp)
286{
287	return lib->key == *(target_address_t *)keyp ? CBS_STOP : CBS_CONT;
288}
289