rtld_machine.c revision 1.29
1117395Skan/* $OpenBSD: rtld_machine.c,v 1.29 2019/12/07 22:57:48 guenther Exp $ */ 2 3/* 4 * Copyright (c) 2013 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18/* 19 * Copyright (c) 1999 Dale Rahn 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 31 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 34 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 * 42 */ 43 44#define _DYN_LOADER 45 46#include <sys/types.h> 47#include <sys/mman.h> 48#include <sys/syscall.h> 49#include <sys/unistd.h> 50 51#include <nlist.h> 52#include <link.h> 53 54#include "syscall.h" 55#include "archdep.h" 56#include "resolve.h" 57 58Elf_Addr _dl_bind(elf_object_t *object, int reloff); 59void _dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr); 60 61int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; 62 63int 64_dl_md_reloc(elf_object_t *object, int rel, int relasz) 65{ 66 int i; 67 int numrela; 68 int relrela; 69 int fails = 0; 70 Elf_Addr loff; 71 Elf_RelA *relas; 72 Elf_Addr prev_value = 0, prev_ooff = 0; 73 const Elf_Sym *prev_sym = NULL; 74 75 loff = object->obj_base; 76 numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA); 77 relrela = rel == DT_RELA ? object->relacount : 0; 78 79 relas = (Elf_RelA *)(object->Dyn.info[rel]); 80 81 if (relas == NULL) 82 return 0; 83 84 if (relrela > numrela) 85 _dl_die("relacount > numrel: %d > %d", relrela, numrela); 86 87 /* tight loop for leading RELATIVE relocs */ 88 for (i = 0; i < relrela; i++, relas++) { 89 Elf_Addr *r_addr; 90 91 r_addr = (Elf_Addr *)(relas->r_offset + loff); 92 *r_addr = relas->r_addend + loff; 93 } 94 for (; i < numrela; i++, relas++) { 95 Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff); 96 Elf_Addr addend, newval; 97 const Elf_Sym *sym; 98 const char *symn; 99 int type; 100 101 type = ELF_R_TYPE(relas->r_info); 102 103 if (type == RELOC_GOTP_ENT && rel != DT_JMPREL) 104 continue; 105 106 if (type == RELOC_NONE) 107 continue; 108 109 sym = object->dyn.symtab; 110 sym += ELF_R_SYM(relas->r_info); 111 symn = object->dyn.strtab + sym->st_name; 112 113 if (type == RELOC_COPY) { 114 /* 115 * we need to find a symbol, that is not in the current 116 * object, start looking at the beginning of the list, 117 * searching all objects but _not_ the current object, 118 * first one found wins. 119 */ 120 struct sym_res sr; 121 122 sr = _dl_find_symbol(symn, 123 SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT, 124 sym, object); 125 if (sr.sym != NULL) { 126 _dl_bcopy((void *)(sr.obj->obj_base + 127 sr.sym->st_value), r_addr, sym->st_size); 128 } else 129 fails++; 130 131 continue; 132 } 133 134 if (ELF_R_SYM(relas->r_info) && 135 !(ELF_ST_BIND(sym->st_info) == STB_LOCAL && 136 ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) && 137 sym != prev_sym) { 138 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && 139 ELF_ST_TYPE(sym->st_info) == STT_SECTION) { 140 prev_sym = sym; 141 prev_value = 0; 142 prev_ooff = object->obj_base; 143 } else { 144 struct sym_res sr; 145 146 sr = _dl_find_symbol(symn, 147 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | 148 ((type == RELOC_GOTP_ENT) ? 149 SYM_PLT : SYM_NOTPLT), sym, object); 150 151 if (sr.sym == NULL) { 152 if (ELF_ST_BIND(sym->st_info) != 153 STB_WEAK) 154 fails++; 155 continue; 156 } 157 prev_sym = sym; 158 prev_value = sr.sym->st_value; 159 prev_ooff = sr.obj->obj_base; 160 } 161 } 162 163 if (type == RELOC_GOTP_ENT) { 164 _dl_md_reloc_gotp_ent((Elf_Addr)r_addr, 165 relas->r_addend + loff, 166 prev_ooff + prev_value); 167 continue; 168 } 169 170 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && 171 (ELF_ST_TYPE(sym->st_info) == STT_SECTION || 172 ELF_ST_TYPE(sym->st_info) == STT_NOTYPE)) 173 addend = relas->r_addend; 174 else 175 addend = prev_value + relas->r_addend; 176 177 switch (type) { 178 case RELOC_16L: 179 newval = prev_ooff + addend; 180 *(unsigned short *)r_addr = newval & 0xffff; 181 _dl_cacheflush((unsigned long)r_addr, 2); 182 break; 183 case RELOC_16H: 184 newval = prev_ooff + addend; 185 *(unsigned short *)r_addr = newval >> 16; 186 _dl_cacheflush((unsigned long)r_addr, 2); 187 break; 188 case RELOC_DISP26: 189 newval = prev_ooff + addend; 190 newval -= (Elf_Addr)r_addr; 191 if ((newval >> 28) != 0 && (newval >> 28) != 0x0f) 192 _dl_die("%s: out of range DISP26" 193 " relocation to '%s' at %p\n", 194 object->load_name, symn, (void *)r_addr); 195 *r_addr = (*r_addr & 0xfc000000) | 196 (((int32_t)newval >> 2) & 0x03ffffff); 197 _dl_cacheflush((unsigned long)r_addr, 4); 198 break; 199 case RELOC_32: 200 newval = prev_ooff + addend; 201 *r_addr = newval; 202 break; 203 case RELOC_BBASED_32: 204 newval = loff + addend; 205 *r_addr = newval; 206 break; 207 default: 208 _dl_die("%s: unsupported relocation '%s' %d at %p\n", 209 object->load_name, symn, type, (void *)r_addr); 210 } 211 } 212 213 return fails; 214} 215 216/* 217 * GOTP_ENT relocations are special in that they define both a .got and a 218 * .plt relocation. 219 */ 220void 221_dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val) 222{ 223 uint16_t *plt_entry = (uint16_t *)plt_addr; 224 225 /* .got update */ 226 *(Elf_Addr *)got_addr = val; 227 /* .plt update */ 228 plt_entry[1] = got_addr >> 16; 229 plt_entry[3] = got_addr & 0xffff; 230} 231 232/* 233 * Relocate the Global Offset Table (GOT). 234 * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW, 235 * otherwise the lazy binding plt operation is preserved. 236 */ 237int 238_dl_md_reloc_got(elf_object_t *object, int lazy) 239{ 240 extern void _dl_bind_start(void); /* XXX */ 241 int fails = 0; 242 Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; 243 Elf_Addr plt_start, plt_end; 244 245 if (pltgot == NULL) 246 return 0; 247 248 pltgot[1] = (Elf_Addr)object; 249 pltgot[2] = (Elf_Addr)_dl_bind_start; 250 251 if (object->Dyn.info[DT_PLTREL] != DT_RELA) 252 return 0; 253 254 if (!lazy) { 255 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); 256 } else { 257 if (object->obj_base != 0) { 258 int cnt; 259 Elf_Addr *addr; 260 Elf_RelA *rela; 261 262 cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); 263 rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL]; 264 265 for (; cnt != 0; cnt--, rela++) { 266 addr = (Elf_Addr *)(object->obj_base + 267 rela->r_offset); 268 _dl_md_reloc_gotp_ent((Elf_Addr)addr, 269 object->obj_base + rela->r_addend, 270 *addr + object->obj_base); 271 } 272 } 273 } 274 275 /* 276 * Force a cache sync here on the whole PLT if we updated it 277 * (and have the DT entries to find what we need to flush), 278 * otherwise I$ might have stale information. 279 */ 280 plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM]; 281 plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM]; 282 if ((!lazy || object->obj_base != 0) && plt_start != 0 && 283 plt_end != 0) { 284 size_t plt_size = plt_end - plt_start; 285 if (plt_size != 0) 286 _dl_cacheflush(plt_start + object->obj_base, plt_size); 287 } 288 289 return fails; 290} 291 292Elf_Addr 293_dl_bind(elf_object_t *object, int reloff) 294{ 295 Elf_RelA *rel; 296 struct sym_res sr; 297 const Elf_Sym *sym; 298 const char *symn; 299 uint64_t cookie = pcookie; 300 struct { 301 struct __kbind param; 302 Elf_Addr newval; 303 } buf; 304 305 rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); 306 307 sym = object->dyn.symtab; 308 sym += ELF_R_SYM(rel->r_info); 309 symn = object->dyn.strtab + sym->st_name; 310 311 sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 312 sym, object); 313 if (sr.sym == NULL) 314 _dl_die("lazy binding failed!"); 315 316 buf.newval = sr.obj->obj_base + sr.sym->st_value; 317 318 if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn)) 319 return buf.newval; 320 321 buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset); 322 buf.param.kb_size = sizeof(Elf_Addr); 323 324 /* directly code the syscall, so that it's actually inline here */ 325 { 326 register long syscall_num __asm("r13") = SYS_kbind; 327 register void *arg1 __asm("r2") = &buf; 328 register long arg2 __asm("r3") = sizeof(buf); 329 register long arg3 __asm("r4") = 0xffffffff & (cookie >> 32); 330 register long arg4 __asm("r5") = 0xffffffff & cookie; 331 332 __asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0" 333 : "+r" (arg1), "+r" (arg2) : "r" (syscall_num), 334 "r" (arg3), "r" (arg4) : "memory"); 335 } 336 337 return buf.newval; 338} 339