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