1 /* $OpenBSD: rtld_machine.c,v 1.22 2016/06/18 02:40:46 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 1998-2004 Opsycon AB, Sweden. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 #define _DYN_LOADER 30 31 #include <sys/types.h> 32 #include <sys/mman.h> 33 #include <sys/syscall.h> 34 #include <sys/unistd.h> 35 36 #include <link.h> 37 38 #include "resolve.h" 39 #include "syscall.h" 40 #include "archdep.h" 41 42 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; 43 44 45 int 46 _dl_md_reloc(elf_object_t *object, int rel, int relsz) 47 { 48 int i; 49 int numrel; 50 int fails = 0; 51 struct load_list *load_list; 52 Elf64_Addr loff; 53 Elf64_Addr ooff; 54 Elf64_Rel *relocs; 55 const Elf64_Sym *sym, *this; 56 Elf64_Addr prev_value = 0; 57 const Elf64_Sym *prev_sym = NULL; 58 59 loff = object->obj_base; 60 numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel); 61 relocs = (Elf64_Rel *)(object->Dyn.info[rel]); 62 63 if (relocs == NULL) 64 return(0); 65 66 /* 67 * Change protection of all write protected segments in the 68 * object so we can do relocations in the .rodata section. 69 * After relocation restore protection. 70 */ 71 if (object->dyn.textrel) { 72 for (load_list = object->load_list; load_list != NULL; load_list = load_list->next) { 73 if ((load_list->prot & PROT_WRITE) == 0) 74 _dl_mprotect(load_list->start, load_list->size, 75 PROT_READ | PROT_WRITE); 76 } 77 } 78 79 DL_DEB(("relocating %d\n", numrel)); 80 for (i = 0; i < numrel; i++, relocs++) { 81 Elf64_Addr r_addr = relocs->r_offset + loff; 82 const char *symn; 83 int type; 84 85 if (ELF64_R_SYM(relocs->r_info) == 0xffffff) 86 continue; 87 88 ooff = 0; 89 sym = object->dyn.symtab; 90 sym += ELF64_R_SYM(relocs->r_info); 91 symn = object->dyn.strtab + sym->st_name; 92 type = ELF64_R_TYPE(relocs->r_info); 93 94 this = NULL; 95 if (ELF64_R_SYM(relocs->r_info)) { 96 if (sym == prev_sym) 97 this = sym; /* XXX non-NULL */ 98 else if (!(ELF64_ST_BIND(sym->st_info) == STB_LOCAL && 99 ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) { 100 ooff = _dl_find_symbol(symn, &this, 101 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, 102 sym, object, NULL); 103 104 if (this == NULL) { 105 if (ELF_ST_BIND(sym->st_info) != 106 STB_WEAK) 107 fails++; 108 continue; 109 } 110 prev_sym = sym; 111 prev_value = this->st_value + ooff; 112 } 113 } 114 115 switch (ELF64_R_TYPE(relocs->r_info)) { 116 /* XXX Handle non aligned relocs. .eh_frame 117 * XXX in libstdc++ seems to have them... */ 118 u_int64_t robj; 119 120 case R_MIPS_REL32_64: 121 if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL && 122 (ELF64_ST_TYPE(sym->st_info) == STT_SECTION || 123 ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { 124 if ((long)r_addr & 7) { 125 _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); 126 robj += loff + sym->st_value; 127 _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); 128 } else { 129 *(u_int64_t *)r_addr += loff + sym->st_value; 130 } 131 } else if (this && ((long)r_addr & 7)) { 132 _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); 133 robj += prev_value; 134 _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); 135 } else if (this) { 136 *(u_int64_t *)r_addr += prev_value; 137 } 138 break; 139 140 case R_MIPS_NONE: 141 break; 142 143 default: 144 _dl_printf("%s: unsupported relocation '%d'\n", 145 __progname, ELF64_R_TYPE(relocs->r_info)); 146 _dl_exit(1); 147 } 148 } 149 DL_DEB(("done %d fails\n", fails)); 150 if (object->dyn.textrel) { 151 for (load_list = object->load_list; load_list != NULL; load_list = load_list->next) { 152 if ((load_list->prot & PROT_WRITE) == 0) 153 _dl_mprotect(load_list->start, load_list->size, 154 load_list->prot); 155 } 156 } 157 return(fails); 158 } 159 160 extern void _dl_bind_start(void); 161 162 /* 163 * Relocate the Global Offset Table (GOT). Currently we don't 164 * do lazy evaluation here because the GNU linker doesn't 165 * follow the ABI spec which says that if an external symbol 166 * is referenced by other relocations than CALL16 and 26 it 167 * should not be given a stub and have a zero value in the 168 * symbol table. By not doing so, we can't use pointers to 169 * external functions and use them in comparisons... 170 */ 171 int 172 _dl_md_reloc_got(elf_object_t *object, int lazy) 173 { 174 int i, n; 175 Elf64_Addr loff; 176 Elf64_Addr ooff; 177 Elf64_Addr *gotp; 178 const Elf64_Sym *symp; 179 const Elf64_Sym *this; 180 const char *strt; 181 182 if (object->status & STAT_GOT_DONE) 183 return (0); 184 185 loff = object->obj_base; 186 strt = object->dyn.strtab; 187 gotp = object->dyn.pltgot; 188 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM]; 189 190 DL_DEB(("loff: '%p'\n", loff)); 191 /* 192 * Set up pointers for run time (lazy) resolving. 193 */ 194 gotp[0] = (long)_dl_bind_start; 195 gotp[1] = (long)object; 196 197 /* First do all local references. */ 198 for (i = 2; i < n; i++) { 199 gotp[i] += loff; 200 } 201 202 gotp += n; 203 204 symp = object->dyn.symtab; 205 symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 206 n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - 207 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 208 209 this = NULL; 210 211 if (object->traced) 212 lazy = 1; 213 214 /* 215 * Then do all global references according to the ABI. 216 * Quickstart is not yet implemented. 217 */ 218 while (n--) { 219 if (symp->st_shndx == SHN_UNDEF && 220 ELF64_ST_TYPE(symp->st_info) == STT_FUNC) { 221 if (symp->st_value == 0 || !lazy) { 222 this = NULL; 223 ooff = _dl_find_symbol(strt + symp->st_name, 224 &this, 225 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 226 symp, object, NULL); 227 if (this) 228 *gotp = this->st_value + ooff; 229 } else 230 *gotp = symp->st_value + loff; 231 } else if (symp->st_shndx == SHN_COMMON || 232 symp->st_shndx == SHN_UNDEF) { 233 this = NULL; 234 ooff = _dl_find_symbol(strt + symp->st_name, &this, 235 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 236 symp, object, NULL); 237 if (this) 238 *gotp = this->st_value + ooff; 239 } else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC && 240 symp->st_value != *gotp) { 241 *gotp += loff; 242 } else { /* Resolve all others immediately */ 243 this = NULL; 244 ooff = _dl_find_symbol(strt + symp->st_name, &this, 245 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 246 symp, object, NULL); 247 if (this) 248 *gotp = this->st_value + ooff; 249 else 250 *gotp = symp->st_value + loff; 251 } 252 gotp++; 253 symp++; 254 } 255 object->status |= STAT_GOT_DONE; 256 257 /* mprotect the GOT */ 258 _dl_protect_segment(object, 0, "__got_start", "__got_end", PROT_READ); 259 260 return (0); 261 } 262 263 Elf_Addr 264 _dl_bind(elf_object_t *object, int symidx) 265 { 266 Elf_Addr *gotp = object->dyn.pltgot; 267 Elf_Addr ooff; 268 const Elf_Sym *sym, *this; 269 const char *symn; 270 const elf_object_t *sobj; 271 int64_t cookie = pcookie; 272 struct { 273 struct __kbind param; 274 Elf_Addr newval; 275 } buf; 276 int n; 277 278 sym = object->dyn.symtab; 279 sym += symidx; 280 symn = object->dyn.strtab + sym->st_name; 281 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] - 282 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 283 284 this = NULL; 285 ooff = _dl_find_symbol(symn, &this, 286 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); 287 if (this == NULL) { 288 _dl_printf("lazy binding failed\n"); 289 *(volatile int *)0 = 0; /* XXX */ 290 } 291 292 buf.newval = ooff + this->st_value; 293 294 if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn)) 295 return (buf.newval); 296 297 buf.param.kb_addr = &gotp[n + symidx]; 298 buf.param.kb_size = sizeof(Elf_Addr); 299 300 /* directly code the syscall, so that it's actually inline here */ 301 { 302 register long syscall_num __asm("v0") = SYS_kbind; 303 register void *arg1 __asm("a0") = &buf; 304 register long arg2 __asm("a1") = sizeof(buf); 305 register long arg3 __asm("a2") = cookie; 306 307 __asm volatile("syscall" : "+r" (syscall_num) 308 : "r" (arg1), "r" (arg2), "r" (arg3) 309 : "v1", "a3", "memory"); 310 } 311 312 return (buf.newval); 313 } 314