1 /*
2  * CDDL HEADER START
3  *
4  * This file and its contents are supplied under the terms of the
5  * Common Development and Distribution License ("CDDL"), version 1.0.
6  * You may only use this file in accordance with the terms of version
7  * 1.0 of the CDDL.
8  *
9  * A full copy of the text of the CDDL should have accompanied this
10  * source.  A copy of the CDDL is also available via the Internet at
11  * http://www.illumos.org/license/CDDL.
12  *
13  * CDDL HEADER END
14  */
15 
16 /*
17  * Copyright (c) 2012 by Delphix. All rights reserved.
18  */
19 
20 /*
21  * This file implements an audit library that can be used to force the loading
22  * of helper providers. The default disposition for a helper provider -- USDT
23  * and ustack helpers -- is to load itself from it's containing object's .init
24  * section. In cases where startup time is deemed critical, USDT authors can
25  * use the -xlazyload option to dtrace(8) to disable automatic loading (it's
26  * difficult to make the case for the utility of this feature for anything
27  * other than libc which, indeed, was the sole motivation). If a binary has
28  * been compiled with automatic loading disabled, this audit library may be
29  * used to force automatic loading:
30  *
31  *	LD_AUDIT_32=/usr/lib/dtrace/libdaudit.so
32  *	LD_AUDIT_64=/usr/lib/dtrace/64/libdaudit.so
33  */
34 
35 #include <link.h>
36 #include <stdio.h>
37 #include <libproc.h>
38 #include <strings.h>
39 
40 #include <dlink.h>
41 
42 typedef struct obj_list {
43 	struct obj_list *ol_next;
44 	char *ol_name;
45 	uintptr_t ol_addr;
46 	Lmid_t ol_lmid;
47 } obj_list_t;
48 
49 static obj_list_t *list;
50 
51 #pragma init(dtrace_daudit_init)
52 static void
dtrace_daudit_init(void)53 dtrace_daudit_init(void)
54 {
55 	dtrace_link_init();
56 }
57 
58 /*ARGSUSED*/
59 uint_t
la_version(uint_t version)60 la_version(uint_t version)
61 {
62 	return (LAV_CURRENT);
63 }
64 
65 /*
66  * Record objects into our linked list as they're loaded.
67  */
68 /*ARGSUSED*/
69 uint_t
la_objopen(Link_map * lmp,Lmid_t lmid,uintptr_t * cookie)70 la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie)
71 {
72 	obj_list_t *node;
73 
74 	/*
75 	 * If we can't allocate the next node in our list, we'll try to emit a
76 	 * message, but it's possible that might fail as well.
77 	 */
78 	if ((node = malloc(sizeof (obj_list_t))) == NULL) {
79 		dprintf(0, "libdaudit: failed to allocate");
80 		return (0);
81 	}
82 	node->ol_next = list;
83 	node->ol_name = strdup(lmp->l_name);
84 	node->ol_addr = lmp->l_addr;
85 	node->ol_lmid = lmid;
86 	list = node;
87 
88 	return (0);
89 }
90 
91 /*
92  * Once the link maps have reached a consistent state, process the list of
93  * objects that were loaded. We need to use libproc to search for the
94  * ___SUNW_dof symbol rather than dlsym(3C) since the symbol is not in the
95  * dynamic (run-time) symbol table (though it is, of course, in the symtab).
96  * Once we find it, we ioctl(2) it to the kernel just as we would have from
97  * the .init section if automatic loading were enabled.
98  */
99 /*ARGSUSED*/
100 void
la_activity(uintptr_t * cookie,uint_t flags)101 la_activity(uintptr_t *cookie, uint_t flags)
102 {
103 	struct ps_prochandle *P;
104 	int err, ret;
105 	GElf_Sym sym;
106 
107 	if (flags != LA_ACT_CONSISTENT)
108 		return;
109 
110 	while (list != NULL) {
111 		obj_list_t *node = list;
112 		char *name = node->ol_name;
113 
114 		list = node->ol_next;
115 
116 		P = Pgrab(getpid(), PGRAB_RDONLY, &err);
117 		ret = Plookup_by_name(P, name, "___SUNW_dof", &sym);
118 		Prelease(P, 0);
119 
120 		if (ret == 0) {
121 			dtrace_link_dof((void *)(uintptr_t)sym.st_value,
122 			    node->ol_lmid, node->ol_name, node->ol_addr);
123 		}
124 
125 		free(node->ol_name);
126 		free(node);
127 	}
128 }
129