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 }