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 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * x86 relocation code.
  29  */
  30 
  31 #include <sys/types.h>
  32 #include <sys/param.h>
  33 #include <sys/sysmacros.h>
  34 #include <sys/systm.h>
  35 #include <sys/user.h>
  36 #include <sys/bootconf.h>
  37 #include <sys/modctl.h>
  38 #include <sys/elf.h>
  39 #include <sys/kobj.h>
  40 #include <sys/kobj_impl.h>
  41 #include <sys/tnf.h>
  42 #include <sys/tnf_probe.h>
  43 
  44 #include "reloc.h"
  45 
  46 
  47 /*
  48  * Probe Discovery
  49  */
  50 
  51 #define PROBE_MARKER_SYMBOL     "__tnf_probe_version_1"
  52 #define TAG_MARKER_SYMBOL       "__tnf_tag_version_1"
  53 
  54 extern int tnf_splice_probes(int, tnf_probe_control_t *, tnf_tag_data_t *);
  55 
  56 /*
  57  * The kernel run-time linker calls this to try to resolve a reference
  58  * it can't otherwise resolve.  We see if it's marking a probe control
  59  * block; if so, we do the resolution and return 0.  If not, we return
  60  * 1 to show that we can't resolve it, either.
  61  */
  62 static int
  63 tnf_reloc_resolve(char *symname, Addr *value_p,
  64     Elf64_Sxword *addend_p,
  65     long offset,
  66     tnf_probe_control_t **probelist,
  67     tnf_tag_data_t **taglist)
  68 {
  69         if (strcmp(symname, PROBE_MARKER_SYMBOL) == 0) {
  70                 *addend_p = 0;
  71                 ((tnf_probe_control_t *)offset)->next = *probelist;
  72                 *probelist = (tnf_probe_control_t *)offset;
  73                 return (0);
  74         }
  75         if (strcmp(symname, TAG_MARKER_SYMBOL) == 0) {
  76                 *addend_p = 0;
  77                 *value_p = (Addr)*taglist;
  78                 *taglist = (tnf_tag_data_t *)offset;
  79                 return (0);
  80         }
  81         return (1);
  82 }
  83 
  84 #define SDT_NOP         0x90
  85 #define SDT_NOPS        5
  86 
  87 static int
  88 sdt_reloc_resolve(struct module *mp, char *symname, uint8_t *instr)
  89 {
  90         sdt_probedesc_t *sdp;
  91         int i;
  92 
  93         /*
  94          * The "statically defined tracing" (SDT) provider for DTrace uses
  95          * a mechanism similar to TNF, but somewhat simpler.  (Surprise,
  96          * surprise.)  The SDT mechanism works by replacing calls to the
  97          * undefined routine __dtrace_probe_[name] with nop instructions.
  98          * The relocations are logged, and SDT itself will later patch the
  99          * running binary appropriately.
 100          */
 101         if (strncmp(symname, sdt_prefix, strlen(sdt_prefix)) != 0)
 102                 return (1);
 103 
 104         symname += strlen(sdt_prefix);
 105 
 106         sdp = kobj_alloc(sizeof (sdt_probedesc_t), KM_WAIT);
 107         sdp->sdpd_name = kobj_alloc(strlen(symname) + 1, KM_WAIT);
 108         bcopy(symname, sdp->sdpd_name, strlen(symname) + 1);
 109 
 110         sdp->sdpd_offset = (uintptr_t)instr;
 111         sdp->sdpd_next = mp->sdt_probes;
 112         mp->sdt_probes = sdp;
 113 
 114         for (i = 0; i < SDT_NOPS; i++)
 115                 instr[i - 1] = SDT_NOP;
 116 
 117         return (0);
 118 }
 119 
 120 int
 121 do_relocate(struct module *mp, char *reltbl, int nreloc, int relocsize,
 122     Addr baseaddr)
 123 {
 124         unsigned long stndx;
 125         unsigned long off;      /* can't be register for tnf_reloc_resolve() */
 126         register unsigned long reladdr, rend;
 127         register unsigned int rtype;
 128         unsigned long value;
 129         Elf64_Sxword addend;
 130         Sym *symref = NULL;
 131         int err = 0;
 132         tnf_probe_control_t *probelist = NULL;
 133         tnf_tag_data_t *taglist = NULL;
 134         int symnum;
 135         reladdr = (unsigned long)reltbl;
 136         rend = reladdr + nreloc * relocsize;
 137 
 138 #ifdef  KOBJ_DEBUG
 139         if (kobj_debug & D_RELOCATIONS) {
 140                 _kobj_printf(ops, "krtld:\ttype\t\t\toffset\t   addend"
 141                     "      symbol\n");
 142                 _kobj_printf(ops, "krtld:\t\t\t\t\t   value\n");
 143         }
 144 #endif
 145 
 146         symnum = -1;
 147         /* loop through relocations */
 148         while (reladdr < rend) {
 149                 symnum++;
 150                 rtype = ELF_R_TYPE(((Rela *)reladdr)->r_info);
 151                 off = ((Rela *)reladdr)->r_offset;
 152                 stndx = ELF_R_SYM(((Rela *)reladdr)->r_info);
 153                 if (stndx >= mp->nsyms) {
 154                         _kobj_printf(ops, "do_relocate: bad strndx %d\n",
 155                             symnum);
 156                         return (-1);
 157                 }
 158                 if ((rtype > R_AMD64_NUM) || IS_TLS_INS(rtype)) {
 159                         _kobj_printf(ops, "krtld: invalid relocation type %d",
 160                             rtype);
 161                         _kobj_printf(ops, " at 0x%llx:", off);
 162                         _kobj_printf(ops, " file=%s\n", mp->filename);
 163                         err = 1;
 164                         continue;
 165                 }
 166 
 167 
 168                 addend = (long)(((Rela *)reladdr)->r_addend);
 169                 reladdr += relocsize;
 170 
 171 
 172                 if (rtype == R_AMD64_NONE)
 173                         continue;
 174 
 175 #ifdef  KOBJ_DEBUG
 176                 if (kobj_debug & D_RELOCATIONS) {
 177                         Sym *   symp;
 178                         symp = (Sym *)
 179                             (mp->symtbl+(stndx * mp->symhdr->sh_entsize));
 180                         _kobj_printf(ops, "krtld:\t%s",
 181                             conv_reloc_amd64_type(rtype));
 182                         _kobj_printf(ops, "\t0x%8llx", off);
 183                         _kobj_printf(ops, " 0x%8llx", addend);
 184                         _kobj_printf(ops, "  %s\n",
 185                             (const char *)mp->strings + symp->st_name);
 186                 }
 187 #endif
 188 
 189                 if (!(mp->flags & KOBJ_EXEC))
 190                         off += baseaddr;
 191 
 192                 /*
 193                  * if R_AMD64_RELATIVE, simply add base addr
 194                  * to reloc location
 195                  */
 196 
 197                 if (rtype == R_AMD64_RELATIVE) {
 198                         value = baseaddr;
 199                 } else {
 200                         /*
 201                          * get symbol table entry - if symbol is local
 202                          * value is base address of this object
 203                          */
 204                         symref = (Sym *)
 205                             (mp->symtbl+(stndx * mp->symhdr->sh_entsize));
 206 
 207                         if (ELF_ST_BIND(symref->st_info) == STB_LOCAL) {
 208                                 /* *** this is different for .o and .so */
 209                                 value = symref->st_value;
 210                         } else {
 211                                 /*
 212                                  * It's global. Allow weak references.  If
 213                                  * the symbol is undefined, give TNF (the
 214                                  * kernel probes facility) a chance to see
 215                                  * if it's a probe site, and fix it up if so.
 216                                  */
 217                                 if (symref->st_shndx == SHN_UNDEF &&
 218                                     sdt_reloc_resolve(mp, mp->strings +
 219                                     symref->st_name, (uint8_t *)off) == 0)
 220                                         continue;
 221 
 222                                 if (symref->st_shndx == SHN_UNDEF &&
 223                                     tnf_reloc_resolve(mp->strings +
 224                                     symref->st_name, &symref->st_value,
 225                                     &addend, off, &probelist, &taglist) != 0) {
 226                                         if (ELF_ST_BIND(symref->st_info)
 227                                             != STB_WEAK) {
 228                                                 _kobj_printf(ops,
 229                                                     "not found: %s\n",
 230                                                     mp->strings +
 231                                                     symref->st_name);
 232                                                 err = 1;
 233                                         }
 234                                         continue;
 235                                 } else { /* symbol found  - relocate */
 236                                         /*
 237                                          * calculate location of definition
 238                                          * - symbol value plus base address of
 239                                          * containing shared object
 240                                          */
 241                                         value = symref->st_value;
 242 
 243                                 } /* end else symbol found */
 244                         } /* end global or weak */
 245                 } /* end not R_AMD64_RELATIVE */
 246 
 247                 value += addend;
 248                 /*
 249                  * calculate final value -
 250                  * if PC-relative, subtract ref addr
 251                  */
 252                 if (IS_PC_RELATIVE(rtype))
 253                         value -= off;
 254 
 255 #ifdef  KOBJ_DEBUG
 256                 if (kobj_debug & D_RELOCATIONS) {
 257                         _kobj_printf(ops, "krtld:\t\t\t\t0x%8llx", off);
 258                         _kobj_printf(ops, " 0x%8llx\n", value);
 259                 }
 260 #endif
 261 
 262                 if (do_reloc_krtld(rtype, (unsigned char *)off, &value,
 263                     (const char *)mp->strings + symref->st_name,
 264                     mp->filename) == 0)
 265                         err = 1;
 266 
 267         } /* end of while loop */
 268         if (err)
 269                 return (-1);
 270 
 271         if (tnf_splice_probes(mp->flags & KOBJ_PRIM, probelist, taglist))
 272                 mp->flags |= KOBJ_TNF_PROBE;
 273 
 274         return (0);
 275 }
 276 
 277 int
 278 do_relocations(struct module *mp)
 279 {
 280         uint_t shn;
 281         Shdr *shp, *rshp;
 282         uint_t nreloc;
 283 
 284         /* do the relocations */
 285         for (shn = 1; shn < mp->hdr.e_shnum; shn++) {
 286                 rshp = (Shdr *)
 287                     (mp->shdrs + shn * mp->hdr.e_shentsize);
 288                 if (rshp->sh_type == SHT_REL) {
 289                         _kobj_printf(ops, "%s can't process type SHT_REL\n",
 290                             mp->filename);
 291                         return (-1);
 292                 }
 293                 if (rshp->sh_type != SHT_RELA)
 294                         continue;
 295                 if (rshp->sh_link != mp->symtbl_section) {
 296                         _kobj_printf(ops, "%s reloc for non-default symtab\n",
 297                             mp->filename);
 298                         return (-1);
 299                 }
 300                 if (rshp->sh_info >= mp->hdr.e_shnum) {
 301                         _kobj_printf(ops, "do_relocations: %s sh_info ",
 302                             mp->filename);
 303                         _kobj_printf(ops, "out of range %d\n", shn);
 304                         goto bad;
 305                 }
 306                 nreloc = rshp->sh_size / rshp->sh_entsize;
 307 
 308                 /* get the section header that this reloc table refers to */
 309                 shp = (Shdr *)
 310                     (mp->shdrs + rshp->sh_info * mp->hdr.e_shentsize);
 311 
 312                 /*
 313                  * Do not relocate any section that isn't loaded into memory.
 314                  * Most commonly this will skip over the .rela.stab* sections
 315                  */
 316                 if (!(shp->sh_flags & SHF_ALLOC))
 317                         continue;
 318 #ifdef  KOBJ_DEBUG
 319                 if (kobj_debug & D_RELOCATIONS) {
 320                         _kobj_printf(ops, "krtld: relocating: file=%s ",
 321                             mp->filename);
 322                         _kobj_printf(ops, "section=%d\n", shn);
 323                 }
 324 #endif
 325 
 326                 if (do_relocate(mp, (char *)rshp->sh_addr, nreloc,
 327                     rshp->sh_entsize, shp->sh_addr) < 0) {
 328                         _kobj_printf(ops,
 329                             "do_relocations: %s do_relocate failed\n",
 330                             mp->filename);
 331                         goto bad;
 332                 }
 333                 kobj_free((void *)rshp->sh_addr, rshp->sh_size);
 334                 rshp->sh_addr = 0;
 335         }
 336         mp->flags |= KOBJ_RELOCATED;
 337         return (0);
 338 bad:
 339         kobj_free((void *)rshp->sh_addr, rshp->sh_size);
 340         rshp->sh_addr = 0;
 341         return (-1);
 342 }