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(1M) 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
42typedef 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
49static obj_list_t *list;
50
51#pragma init(dtrace_daudit_init)
52static void
53dtrace_daudit_init(void)
54{
55	dtrace_link_init();
56}
57
58/*ARGSUSED*/
59uint_t
60la_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*/
69uint_t
70la_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*/
100void
101la_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