xref: /titanic_44/usr/src/cmd/sgs/link_audit/common/bindings.c (revision e1c679fa4b0ab8c4bcaa6263974ca0c46e5b027f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 #include	<link.h>
31 #include	<stdlib.h>
32 #include	<unistd.h>
33 #include	<sys/types.h>
34 #include	<sys/stat.h>
35 #include	<sys/regset.h>
36 #include	<sys/frame.h>
37 #include	<sys/lwp.h>
38 #include	<fcntl.h>
39 #include	<stdio.h>
40 #include	<sys/mman.h>
41 #include	<errno.h>
42 #include	<signal.h>
43 #include	<synch.h>
44 #include	<string.h>
45 
46 #include	"bindings.h"
47 #include	"env.h"
48 
49 static Elist		*bindto_list = 0;
50 static Elist		*bindfrom_list = 0;
51 
52 static bindhead		*bhp = NULL;
53 static unsigned int	current_map_len = 0;
54 static char		*buffer_name;
55 static const sigset_t	iset = { ~0U, ~0U, ~0U, ~0U };
56 static lwp_mutex_t	sharedmutex = SHAREDMUTEX;
57 
58 /*
59  * This routine was stolen from libelf.so.1
60  */
61 static unsigned long
62 ehash(const char *name)
63 {
64 	register unsigned int		g, h = 0;
65 	register const unsigned char	*nm = (unsigned char *)name;
66 
67 	while (*nm != '\0') {
68 		h = (h << 4) + *nm++;
69 		/* LINTED */
70 		if ((g = (unsigned int)(h & MASK)) != 0)
71 			h ^= g >> 24;
72 		h &= ~MASK;
73 	}
74 	return ((unsigned long)h);
75 }
76 
77 
78 static void
79 output_err_message(const char *msg)
80 {
81 	int fd;
82 	if ((fd = open("/tmp/bind_err", O_RDWR | O_CREAT, 0666)) == -1) {
83 		(void) fprintf(stderr, "bindings.so: unable to open err_log\n");
84 		perror("open");
85 	}
86 	(void) lseek(fd, 0, SEEK_END);
87 	(void) write(fd, msg, strlen(msg));
88 	(void) close(fd);
89 }
90 
91 /*
92  * common mutex locking & unlocking routines for this module.  This is to
93  * control the setting of 'lock_held'.
94  */
95 static void
96 bt_lock(lwp_mutex_t *lock)
97 {
98 	if (_lwp_mutex_lock(lock) != 0) {
99 		output_err_message("bt_lock failed!!\n");
100 		(void) fprintf(stderr, "bindings.so: unable to obtain lock\n");
101 		perror("_lwp_mutex_lock");
102 	}
103 }
104 
105 static void
106 bt_unlock(lwp_mutex_t *lock)
107 {
108 	if (_lwp_mutex_unlock(lock) != 0) {
109 		output_err_message("bt_unlock failed!!\n");
110 		(void) fprintf(stderr, "bindings.so: unable to unlock lock\n");
111 		perror("_lwp_mutex_unlock");
112 	}
113 }
114 
115 
116 
117 /*
118  * It's always possible that another process sharing our buffer
119  * has caused it to grow.  If this is the case we must adjust our
120  * mappings to compensate.
121  */
122 static void
123 remap_buffer(int fd)
124 {
125 	void *	new_bhp;
126 	if ((new_bhp = mmap(0, bhp->bh_size, PROT_READ | PROT_WRITE,
127 	    MAP_SHARED, fd, 0)) == MAP_FAILED) {
128 		(void) fprintf(stderr, "bindings: remap: mmap failed\n");
129 		perror("mmap");
130 
131 		bt_unlock(&bhp->bh_lock);
132 		exit(1);
133 	}
134 	/*
135 	 * clean up old mapping
136 	 */
137 	(void) munmap((caddr_t)bhp, current_map_len);
138 	bhp = (bindhead *)new_bhp;
139 	current_map_len = bhp->bh_size;
140 }
141 
142 static void
143 grow_buffer()
144 {
145 	int	fd;
146 	if ((fd = open(buffer_name, O_RDWR)) == -1) {
147 		(void) fprintf(stderr,
148 			"bidings: grow_buffer: open failed: %s\n",
149 			buffer_name);
150 		perror("open");
151 		bt_unlock(&bhp->bh_lock);
152 		exit(1);
153 	}
154 	if (ftruncate(fd, bhp->bh_size + BLKSIZE) == -1) {
155 		(void) fprintf(stderr, "grow_buffer failed\n");
156 		perror("ftruncate");
157 		bt_unlock(&bhp->bh_lock);
158 		exit(1);
159 	}
160 	bhp->bh_size += BLKSIZE;
161 	remap_buffer(fd);
162 	(void) close(fd);
163 }
164 
165 static void
166 get_new_strbuf()
167 {
168 	bt_lock(&bhp->bh_lock);
169 	while (bhp->bh_end + STRBLKSIZE > bhp->bh_size)
170 		grow_buffer();
171 
172 	bhp->bh_strcur = bhp->bh_end;
173 	bhp->bh_end = bhp->bh_strend = bhp->bh_strcur + STRBLKSIZE;
174 	bt_unlock(&bhp->bh_lock);
175 }
176 
177 static unsigned int
178 save_str(const char *str)
179 {
180 	char		*sptr;
181 	unsigned int	bptr;
182 	unsigned int	slen;
183 
184 	bt_lock(&bhp->bh_strlock);
185 	/* LINTED */
186 	slen = (unsigned int)strlen(str);
187 
188 	/*
189 	 * will string fit into our current string buffer?
190 	 */
191 	if ((slen + 1) > (bhp->bh_strend - bhp->bh_strcur))
192 		get_new_strbuf();
193 	bptr = bhp->bh_strcur;
194 	sptr = (char *)bhp + bhp->bh_strcur;
195 	bhp->bh_strcur += slen + 1;
196 	(void) strncpy(sptr, str, slen);
197 	sptr[slen] = '\0';
198 	bt_unlock(&bhp->bh_strlock);
199 	return (bptr);
200 }
201 
202 
203 static unsigned int
204 get_new_entry()
205 {
206 	unsigned int	new_ent;
207 	bt_lock(&bhp->bh_lock);
208 	while ((sizeof (binding_entry) + bhp->bh_end) > bhp->bh_size)
209 		grow_buffer();
210 	new_ent = bhp->bh_end;
211 	bhp->bh_end += sizeof (binding_entry);
212 	bt_unlock(&bhp->bh_lock);
213 	return (new_ent);
214 }
215 
216 
217 
218 static void
219 init_locks()
220 {
221 	int i;
222 
223 	/*
224 	 * NOTE: I should call _lwp_mutex_init() but it doesn't
225 	 *	 yet exist.  see bug#1179352
226 	 */
227 	(void) memcpy(&bhp->bh_lock, &sharedmutex, sizeof (lwp_mutex_t));
228 	for (i = 0; i < DEFBKTS; i++)
229 		(void) memcpy(&bhp->bh_bkts[i].bb_lock, &sharedmutex,
230 		    sizeof (lwp_mutex_t));
231 
232 	(void) memcpy(&bhp->bh_strlock, &sharedmutex, sizeof (lwp_mutex_t));
233 }
234 
235 uint_t
236 la_version(uint_t version)
237 {
238 	int	fd;
239 	sigset_t	omask;
240 
241 	if (version < LAV_CURRENT) {
242 		(void) fprintf(stderr,
243 			"bindings.so: unexpected link_audit version: %d\n",
244 			version);
245 		return (0);
246 	}
247 
248 	build_env_list(&bindto_list, (const char *)"BT_BINDTO");
249 	build_env_list(&bindfrom_list, (const char *)"BT_BINDFROM");
250 
251 	if ((buffer_name = getenv(FILEENV)) == NULL)
252 		buffer_name = DEFFILE;
253 
254 	(void) sigprocmask(SIG_BLOCK, &iset, &omask);
255 	if ((fd = open(buffer_name, O_RDWR | O_CREAT | O_EXCL, 0666)) != -1) {
256 		int	init_size = sizeof (bindhead) + BLKSIZE;
257 		if (ftruncate(fd, init_size) == -1) {
258 			perror("ftruncate");
259 			return (0);
260 		}
261 
262 		/* LINTED */
263 		if ((bhp = (bindhead *)mmap(0, init_size,
264 		    PROT_READ | PROT_WRITE,
265 		    MAP_SHARED, fd, 0)) == MAP_FAILED) {
266 			perror("bindings.so: mmap");
267 			return (0);
268 		}
269 
270 		(void) close(fd);
271 
272 		init_locks();
273 		/*
274 		 * Lock our structure and then initialize the data
275 		 */
276 		bt_lock(&bhp->bh_lock);
277 		bhp->bh_vers = BINDCURVERS;
278 		current_map_len = bhp->bh_size = init_size;
279 		bhp->bh_end = sizeof (bindhead);
280 		bhp->bh_bktcnt = DEFBKTS;
281 		bt_unlock(&bhp->bh_lock);
282 		/*
283 		 * Set up our initial string buffer
284 		 */
285 		get_new_strbuf();
286 	} else if ((fd = open(buffer_name, O_RDWR)) != -1) {
287 		struct stat	stbuf;
288 		int		i;
289 		for (i = 0; i < 4; i++) {
290 			if (fstat(fd, &stbuf) == -1) {
291 				(void) sleep(1);
292 				continue;
293 			}
294 			if (stbuf.st_size < sizeof (bindhead)) {
295 				(void) sleep(1);
296 				continue;
297 			}
298 			/* LINTED */
299 			if ((bhp = (bindhead *)mmap(0, stbuf.st_size,
300 			    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
301 			    MAP_FAILED) {
302 				(void) fprintf(stderr,
303 					"bindings: mmap failed\n");
304 				perror("mmap");
305 				return (0);
306 			}
307 
308 			/* LINTED */
309 			current_map_len = (unsigned int)stbuf.st_size;
310 		}
311 		if (bhp == NULL) {
312 			(void) fprintf(stderr,
313 				"bindings: buffer mapping timed out\n");
314 			return (0);
315 		}
316 		for (i = 0; i < 4; i++) {
317 			if (bhp->bh_vers == 0) {
318 				(void) sleep(1);
319 				continue;
320 			}
321 		}
322 		if (bhp->bh_vers == 0) {
323 			(void) fprintf(stderr,
324 				"bindings: %s not initialized\n", buffer_name);
325 			return (0);
326 		}
327 
328 		bt_lock(&bhp->bh_lock);
329 
330 		if (bhp->bh_size != current_map_len)
331 			remap_buffer(fd);
332 		(void) close(fd);
333 	} else {
334 		(void) fprintf(stderr, "bindings: unable to open %s\n",
335 			buffer_name);
336 		perror("open");
337 		return (0);
338 	}
339 
340 	(void) sigprocmask(SIG_SETMASK, &omask, NULL);
341 	bt_unlock(&bhp->bh_lock);
342 
343 	return (LAV_CURRENT);
344 }
345 
346 /* ARGSUSED 0 */
347 uint_t
348 la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie)
349 {
350 	uint_t	flags;
351 
352 	if ((bindto_list == 0) ||
353 	    (check_list(bindto_list, lmp->l_name)))
354 		flags = LA_FLG_BINDTO;
355 	else
356 		flags = 0;
357 
358 	if ((bindfrom_list == 0) ||
359 	    (check_list(bindfrom_list, lmp->l_name)))
360 		flags |= LA_FLG_BINDFROM;
361 
362 	return (flags);
363 }
364 
365 
366 /* ARGSUSED 1 */
367 #if	defined(__sparcv9)
368 uintptr_t
369 la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcooke,
370 	uintptr_t *defcook, La_sparcv9_regs *regset, uint_t *sb_flags,
371 	const char *sym_name)
372 #elif	defined(__sparc)
373 uintptr_t
374 la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke,
375 	uintptr_t *defcook, La_sparcv8_regs *regset, uint_t *sb_flags)
376 #elif	defined(__amd64)
377 uintptr_t
378 la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcooke,
379 	uintptr_t *defcook, La_amd64_regs *regset, uint_t *sb_flags,
380 	const char *sym_name)
381 #elif	defined(__i386)
382 uintptr_t
383 la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke,
384 	uintptr_t *defcook, La_i86_regs *regset, uint_t *sb_flags)
385 #endif
386 {
387 	unsigned long	bktno;
388 	Link_map	*dlmp = (Link_map *)*defcook;
389 	const char	*lib_name;
390 	sigset_t	omask;
391 #if	!defined(_LP64)
392 	const char	*sym_name = (const char *)symp->st_name;
393 #endif
394 
395 
396 	lib_name = dlmp->l_name;
397 
398 	(void) sigprocmask(SIG_BLOCK, &iset, &omask);
399 	if (sym_name == 0) {
400 		output_err_message("null symname\n");
401 		return (symp->st_value);
402 	}
403 
404 	bktno = ehash(sym_name) % bhp->bh_bktcnt;
405 
406 	bt_lock(&bhp->bh_bkts[bktno].bb_lock);
407 
408 	/*
409 	 * The buffer has been grown (by another process) and
410 	 * we need to remap it into memory.
411 	 */
412 	if (bhp->bh_size != current_map_len) {
413 		int fd;
414 		if ((fd = open(buffer_name, O_RDWR)) == -1) {
415 			(void) fprintf(stderr,
416 				"bidings: plt_enter: open failed: %s\n",
417 				buffer_name);
418 			perror("open");
419 			bt_unlock(&bhp->bh_lock);
420 			exit(1);
421 		}
422 		bt_lock(&bhp->bh_lock);
423 		remap_buffer(fd);
424 		bt_unlock(&bhp->bh_lock);
425 		(void) close(fd);
426 	}
427 
428 	if (bhp->bh_bkts[bktno].bb_head == NULL) {
429 		binding_entry *	bep;
430 		unsigned int	be_off;
431 		unsigned int	sym_off;
432 		unsigned int	lib_off;
433 
434 		be_off = get_new_entry();
435 		sym_off = save_str(sym_name);
436 		lib_off = save_str(lib_name);
437 		/* LINTED */
438 		bep = (binding_entry *)((char *)bhp + be_off);
439 		bep->be_next = 0;
440 		bep->be_sym_name = sym_off;
441 		bep->be_lib_name = lib_off;
442 		bep->be_count = 1;
443 		bhp->bh_bkts[bktno].bb_head = be_off;
444 	} else {
445 		int		strcmp_res;
446 		unsigned int	prev_off = 0;
447 		binding_entry *	prev_bep = NULL;
448 		unsigned int	cur_off;
449 		binding_entry *	cur_bep;
450 		unsigned int	lib_off = 0;
451 
452 		/*
453 		 * Once we get to the bucket, we do a two tiered
454 		 * search.  First we search for a library match, then
455 		 * we search for a symbol match.
456 		 */
457 		cur_off = bhp->bh_bkts[bktno].bb_head;
458 		/* LINTED */
459 		cur_bep = (binding_entry *)((char *)bhp +
460 			cur_off);
461 		while (cur_off && (strcmp_res = strcmp((char *)bhp +
462 		    cur_bep->be_lib_name, lib_name)) < 0) {
463 			prev_off = cur_off;
464 			cur_off = cur_bep->be_next;
465 			/* LINTED */
466 			cur_bep = (binding_entry *)((char *)bhp +
467 				cur_off);
468 		}
469 		if (cur_off && (strcmp_res == 0)) {
470 			/*
471 			 * This is a small optimization.  For
472 			 * each bucket we will only record a library
473 			 * name once.  Once it has been recorded in
474 			 * a bucket we will just re-use the same
475 			 * string.
476 			 */
477 			lib_off = cur_bep->be_lib_name;
478 			while (cur_off && (strcmp_res = strcmp((char *)bhp +
479 			    cur_bep->be_sym_name, sym_name)) < 0) {
480 				prev_off = cur_off;
481 				cur_off = cur_bep->be_next;
482 				/* LINTED */
483 				cur_bep = (binding_entry *)((char *)bhp +
484 					cur_off);
485 			}
486 		}
487 		if (strcmp_res == 0) {
488 			/*
489 			 * We've got a match
490 			 */
491 			cur_bep->be_count++;
492 		} else {
493 			unsigned int	new_off;
494 			binding_entry *	new_bep;
495 			unsigned int	sym_off;
496 
497 			new_off = get_new_entry();
498 			if (lib_off == 0)
499 				lib_off = save_str(lib_name);
500 			sym_off = save_str(sym_name);
501 
502 			/* LINTED */
503 			new_bep = (binding_entry *)((char *)bhp +
504 				new_off);
505 			new_bep->be_sym_name = sym_off;
506 			new_bep->be_lib_name = lib_off;
507 			new_bep->be_count = 1;
508 			new_bep->be_next = cur_off;
509 			if (prev_off) {
510 				/* LINTED */
511 				prev_bep = (binding_entry *)((char *)bhp +
512 					prev_off);
513 				prev_bep->be_next = new_off;
514 			} else
515 				/*
516 				 * Insert at head of list.
517 				 */
518 				bhp->bh_bkts[bktno].bb_head = new_off;
519 
520 		}
521 	}
522 	bt_unlock(&bhp->bh_bkts[bktno].bb_lock);
523 	(void) sigprocmask(SIG_SETMASK, &omask, NULL);
524 	return (symp->st_value);
525 }
526