1 /* $OpenBSD: rtld_machine.c,v 1.25 2003/07/28 03:11:00 drahn Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Dale Rahn 5 * Copyright (c) 2001 Niklas Hallqvist 6 * Copyright (c) 2001 Artur Grabowski 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #define _DYN_LOADER 32 33 #include <sys/types.h> 34 #include <sys/cdefs.h> 35 #include <sys/mman.h> 36 #include <sys/exec.h> 37 38 #include <machine/elf_machdep.h> 39 40 #include <nlist.h> 41 #include <link.h> 42 #include <signal.h> 43 44 #include "syscall.h" 45 #include "archdep.h" 46 #include "resolve.h" 47 48 void 49 _dl_bcopy(const void *src, void *dest, int size) 50 { 51 unsigned const char *psrc = src; 52 unsigned char *pdest = dest; 53 int i; 54 55 for (i = 0; i < size; i++) { 56 pdest[i] = psrc[i]; 57 } 58 } 59 60 int 61 _dl_md_reloc(elf_object_t *object, int rel, int relasz) 62 { 63 long i; 64 long numrela; 65 long fails = 0; 66 Elf64_Addr loff; 67 Elf64_Rela *relas; 68 struct load_list *llist; 69 70 loff = object->load_offs; 71 numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela); 72 relas = (Elf64_Rela *)(object->Dyn.info[rel]); 73 74 if (relas == NULL) 75 return(0); 76 77 /* 78 * unprotect some segments if we need it. 79 * XXX - we unprotect way to much. only the text can have cow 80 * relocations. 81 */ 82 if ((rel == DT_REL || rel == DT_RELA)) { 83 for (llist = object->load_list; llist != NULL; llist = llist->next) { 84 if (!(llist->prot & PROT_WRITE)) { 85 _dl_mprotect(llist->start, llist->size, 86 llist->prot|PROT_WRITE); 87 } 88 } 89 } 90 91 for (i = 0; i < numrela; i++, relas++) { 92 Elf64_Addr *r_addr; 93 Elf64_Addr ooff; 94 const Elf64_Sym *sym, *this; 95 const char *symn; 96 97 r_addr = (Elf64_Addr *)(relas->r_offset + loff); 98 99 if (ELF64_R_SYM(relas->r_info) == 0xffffffff) 100 continue; 101 102 103 sym = object->dyn.symtab; 104 sym += ELF64_R_SYM(relas->r_info); 105 symn = object->dyn.strtab + sym->st_name; 106 107 this = NULL; 108 switch (ELF64_R_TYPE(relas->r_info)) { 109 case R_TYPE(REFQUAD): 110 ooff = _dl_find_symbol(symn, _dl_objects, &this, 111 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, 112 sym->st_size, object->load_name); 113 if (this == NULL) 114 goto resolve_failed; 115 *r_addr += ooff + this->st_value + relas->r_addend; 116 break; 117 case R_TYPE(RELATIVE): 118 /* 119 * There is a lot of unaligned RELATIVE 120 * relocs generated by gcc in the exception handlers. 121 */ 122 if ((((Elf_Addr) r_addr) & 0x7) != 0) { 123 Elf_Addr tmp; 124 #if 0 125 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr, 126 ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff); 127 #endif 128 _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); 129 tmp += loff; 130 _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); 131 } else 132 *r_addr += loff; 133 break; 134 case R_TYPE(JMP_SLOT): 135 ooff = _dl_find_symbol(symn, _dl_objects, &this, 136 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 137 sym->st_size, object->load_name); 138 if (this == NULL) 139 goto resolve_failed; 140 *r_addr = ooff + this->st_value + relas->r_addend; 141 break; 142 case R_TYPE(GLOB_DAT): 143 ooff = _dl_find_symbol(symn, _dl_objects, &this, 144 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, 145 sym->st_size, object->load_name); 146 if (this == NULL) 147 goto resolve_failed; 148 *r_addr = ooff + this->st_value + relas->r_addend; 149 break; 150 case R_TYPE(NONE): 151 break; 152 default: 153 _dl_printf("%s:" 154 " %s: unsupported relocation '%s' %d at %lx\n", 155 _dl_progname, object->load_name, symn, 156 ELF64_R_TYPE(relas->r_info), r_addr ); 157 _dl_exit(1); 158 } 159 continue; 160 resolve_failed: 161 _dl_printf("%s: %s :can't resolve reference '%s'\n", 162 _dl_progname, object->load_name, symn); 163 fails++; 164 } 165 __asm __volatile("imb" : : : "memory"); 166 167 /* reprotect the unprotected segments */ 168 if ((rel == DT_REL || rel == DT_RELA)) { 169 for (llist = object->load_list; llist != NULL; llist = llist->next) { 170 if (!(llist->prot & PROT_WRITE)) 171 _dl_mprotect(llist->start, llist->size, 172 llist->prot); 173 } 174 } 175 return (fails); 176 } 177 178 /* 179 * Resolve a symbol at run-time. 180 */ 181 Elf_Addr 182 _dl_bind(elf_object_t *object, int reloff) 183 { 184 Elf_RelA *rela; 185 Elf_Addr *addr, ooff; 186 const Elf_Sym *sym, *this; 187 const char *symn; 188 sigset_t omask, nmask; 189 190 rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); 191 192 sym = object->dyn.symtab; 193 sym += ELF64_R_SYM(rela->r_info); 194 symn = object->dyn.strtab + sym->st_name; 195 196 addr = (Elf_Addr *)(object->load_offs + rela->r_offset); 197 this = NULL; 198 ooff = _dl_find_symbol(symn, _dl_objects, &this, 199 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym->st_size, 200 object->load_name); 201 if (this == NULL) { 202 _dl_printf("lazy binding failed!\n"); 203 *((int *)0) = 0; /* XXX */ 204 } 205 /* if PLT is protected, allow the write */ 206 if (object->plt_size != 0) { 207 sigfillset(&nmask); 208 _dl_sigprocmask(SIG_BLOCK, &nmask, &omask); 209 _dl_mprotect(addr, sizeof(Elf_Addr), 210 PROT_READ|PROT_WRITE|PROT_EXEC); 211 } 212 213 *addr = ooff + this->st_value + rela->r_addend; 214 215 /* if PLT is (to be protected, change back to RO/X */ 216 if (object->plt_size != 0) { 217 _dl_mprotect(addr, sizeof(Elf_Addr), 218 PROT_READ|PROT_EXEC); 219 _dl_sigprocmask(SIG_SETMASK, &omask, NULL); 220 } 221 222 return *addr; 223 } 224 225 /* 226 * Relocate the Global Offset Table (GOT). 227 */ 228 void 229 _dl_md_reloc_got(elf_object_t *object, int lazy) 230 { 231 Elf_Addr *pltgot; 232 extern void _dl_bind_start(void); /* XXX */ 233 Elf_Addr ooff; 234 Elf_Addr plt_addr; 235 const Elf_Sym *this; 236 237 238 pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; 239 240 object->got_addr = NULL; 241 object->got_size = 0; 242 this = NULL; 243 ooff = _dl_find_symbol("__got_start", object, &this, 244 SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL); 245 if (this != NULL) 246 object->got_addr = ooff + this->st_value; 247 248 this = NULL; 249 ooff = _dl_find_symbol("__got_end", object, &this, 250 SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL); 251 if (this != NULL) 252 object->got_size = ooff + this->st_value - object->got_addr; 253 254 plt_addr = NULL; 255 object->plt_size = 0; 256 this = NULL; 257 ooff = _dl_find_symbol("__plt_start", object, &this, 258 SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL); 259 if (this != NULL) 260 plt_addr = ooff + this->st_value; 261 262 this = NULL; 263 ooff = _dl_find_symbol("__plt_end", object, &this, 264 SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL); 265 if (this != NULL) 266 object->plt_size = ooff + this->st_value - plt_addr; 267 268 if (object->got_addr == NULL) 269 object->got_start = NULL; 270 else { 271 object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz); 272 object->got_size += object->got_addr - object->got_start; 273 object->got_size = ELF_ROUND(object->got_size, _dl_pagesz); 274 } 275 if (plt_addr == NULL) 276 object->plt_start = NULL; 277 else { 278 object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz); 279 object->plt_size += plt_addr - object->plt_start; 280 object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz); 281 } 282 283 if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) { 284 _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); 285 return; 286 } 287 288 if (object->obj_type != OBJTYPE_EXE) { 289 int i, size; 290 Elf_Addr *addr; 291 Elf_RelA *rela; 292 293 size = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); 294 rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]); 295 296 for (i = 0; i < size; i++) { 297 addr = (Elf_Addr *)(object->load_offs + rela[i].r_offset); 298 *addr += object->load_offs; 299 } 300 } 301 302 pltgot[2] = (Elf_Addr)_dl_bind_start; 303 pltgot[3] = (Elf_Addr)object; 304 if (object->got_size != 0) 305 _dl_mprotect((void*)object->got_addr, object->got_size, 306 PROT_READ); 307 if (object->plt_size != 0) 308 _dl_mprotect((void*)object->plt_start, object->plt_size, 309 PROT_READ|PROT_EXEC); 310 } 311 312 /* relocate the GOT early */ 313 314 void _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase); 315 316 void 317 _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase) 318 { 319 const Elf_RelA *rela = 0, *relalim; 320 Elf_Addr relasz = 0; 321 Elf_Addr *where; 322 323 for (; dynp->d_tag != DT_NULL; dynp++) { 324 switch (dynp->d_tag) { 325 case DT_RELA: 326 rela = (const Elf_RelA *)(relocbase + dynp->d_un.d_ptr); 327 break; 328 case DT_RELASZ: 329 relasz = dynp->d_un.d_val; 330 break; 331 } 332 } 333 relalim = (const Elf_RelA *)((caddr_t)rela + relasz); 334 for (; rela < relalim; rela++) { 335 where = (Elf_Addr *)(relocbase + rela->r_offset); 336 /* XXX For some reason I see a few GLOB_DAT relocs here. */ 337 *where += (Elf_Addr)relocbase; 338 } 339 } 340