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 * Copyright (c) 2012 Joyent, Inc. All rights reserved.
23 */
24
25 /*
26 * This is a local link auditing library for libumem(3LIB). It provides a means
27 * for us to implement the per-thread caching component of libumem. When any
28 * binary or library attempts to bind to libumem's malloc and free symbols we
29 * instead point them to a private buffer in our own BSS. Our mapfile ensures
30 * that this BSS is readable, writeable, and executable. By default these
31 * private buffers contain a jmp instruction to the original libumem malloc and
32 * free.
33 *
34 * When libumem tries to generate its assembly, we key off of private symbol
35 * names and replace their values with pointers to our values. For more
36 * information on this process, see section 8 of the big theory statement for
37 * libumem in lib/libumem/common/umem.c.
38 *
39 * Note that this is very x86 specific currently. This includes x86 instructions
40 * and making assumptions about alignment of variables, see the lint warnings.
41 * By the current construction, SPARC is basically a no-op.
42 */
43 #include <dlfcn.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <libelf.h>
47 #include <link.h>
48
49 #if defined(__i386) || defined(__amd64)
50 #define LIBUMEM_TRAMPOLINE_JMP32 0xe9
51 #endif /* defined(__i386) || defined(__amd64) */
52
53 /*
54 * This is our malloc trampoline. We give it the name "malloc" to make it
55 * appear somewhat like malloc.
56 */
57 static uint8_t malloc[4096];
58 static uint8_t free[4096];
59 static size_t msize = sizeof (malloc);
60 static size_t fsize = sizeof (free);
61
62 /*
63 * We don't want to link against libc, so we define our own versions of the
64 * string functions as necessary.
65 */
66 static int
67 la_strcmp(const char *s1, const char *s2)
68 {
69 if (s1 == s2)
70 return (0);
71 while (*s1 == *s2++)
72 if (*s1++ == '\0')
73 return (0);
74
75 return (*(unsigned char *)s1 - *(unsigned char *)--s2);
76 }
77
78 static char *
79 la_strrchr(char *str, char c)
80 {
81 char *r;
82
83 r = NULL;
84 do {
85 if (*str == c)
86 r = str;
87 } while (*str++);
88 return (r);
89 }
90
91 /*ARGSUSED*/
92 uint_t
93 la_version(uint_t version)
94 {
95 return (LAV_CURRENT);
96 }
97
98 /*ARGSUSED*/
99 uint_t
100 la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie)
101 {
102 #if defined(__i386) || defined(__amd64)
103 char *objname;
104
105 if ((objname = la_strrchr(lmp->l_name, '/')) == NULL ||
106 *(++objname) == '\0')
107 objname = lmp->l_name;
108
109 if (la_strcmp(objname, "libumem.so.1") == 0 ||
110 la_strcmp(objname, "libumem.so") == 0)
111 return (LA_FLG_BINDFROM | LA_FLG_BINDTO);
112 #endif /* defined(__i386) || defined(__amd64) */
113
114 return (0);
115 }
116
117 #if defined(_LP64)
118 /*ARGSUSED*/
119 uintptr_t
120 la_symbind64(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcook,
121 uintptr_t *defcook, uint_t *sb_flags, char const *sym_name)
122 #else
123 /*ARGSUSED*/
124 uintptr_t
125 la_symbind32(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcook,
126 uintptr_t *defcook, uint_t *sb_flags)
127 #endif
128 {
129 #if defined(__i386) || defined(__amd64)
130 int i = 0;
131
132 #if !defined(_LP64)
133 char const *sym_name = (char const *) symp->st_name;
134 #endif
135
136 if (la_strcmp(sym_name, "malloc") == 0) {
137 if (malloc[i] == '\0') {
138 malloc[i++] = LIBUMEM_TRAMPOLINE_JMP32;
139 /*LINTED E_BAD_PTR_CAST_ALIGN*/
140 *(uint32_t *)&malloc[i] = (uint32_t)(symp->st_value -
141 (uintptr_t)&malloc[i + sizeof (uint32_t)]);
142 }
143
144 return ((uintptr_t)malloc);
145 } else if (la_strcmp(sym_name, "free") == 0) {
146 if (free[i] == '\0') {
147 free[i++] = LIBUMEM_TRAMPOLINE_JMP32;
148 /*LINTED E_BAD_PTR_CAST_ALIGN*/
149 *(uint32_t *)&free[i] = (uint32_t)(symp->st_value -
150 (uintptr_t)&free[i + sizeof (uint32_t)]);
151 }
152
153 return ((uintptr_t)free);
154 } else if (la_strcmp(sym_name, "umem_genasm_mptr") == 0) {
155 return ((uintptr_t)malloc);
156 } else if (la_strcmp(sym_name, "umem_genasm_msize") == 0) {
157 return ((uintptr_t)&msize);
158 } else if (la_strcmp(sym_name, "umem_genasm_fptr") == 0) {
159 return ((uintptr_t)free);
160 } else if (la_strcmp(sym_name, "umem_genasm_fsize") == 0) {
161 return ((uintptr_t)&fsize);
162 } else {
163 return (symp->st_value);
164 }
165 #endif /* defined(__i386) || defined(__amd64) */
166 }