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