1 /* $OpenBSD: rtld_machine.c,v 1.5 2001/08/05 15:31:35 drahn Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Dale Rahn 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 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed under OpenBSD by 17 * Dale Rahn. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 */ 34 35 #define _DYN_LOADER 36 37 #include <sys/types.h> 38 #include <sys/mman.h> 39 40 #include <nlist.h> 41 #include <link.h> 42 43 #include "syscall.h" 44 #include "archdep.h" 45 #include "resolve.h" 46 47 void 48 _dl_bcopy(void *src, void *dest, int size) 49 { 50 unsigned char *psrc, *pdest; 51 int i; 52 psrc = src; 53 pdest = dest; 54 for (i = 0; i < size; i++) { 55 pdest[i] = psrc[i]; 56 } 57 } 58 59 int 60 _dl_md_reloc(elf_object_t *object, int rel, int relasz) 61 { 62 int i; 63 int numrela; 64 int fails = 0; 65 load_list_t *load_list; 66 Elf32_Addr loff; 67 Elf32_Rela *relas; 68 /* for jmp table relocations */ 69 Elf32_Addr *pltcall; 70 Elf32_Addr *plttable; 71 72 Elf32_Addr * first_rela; 73 74 loff = object->load_offs; 75 numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela); 76 relas = (Elf32_Rela *)(object->Dyn.info[rel]); 77 78 #ifdef DL_PRINTF_DEBUG 79 _dl_printf("object relocation size %x, numrela %x\n", 80 object->Dyn.info[relasz], numrela); 81 #endif 82 83 if((object->status & STAT_RELOC_DONE) || !relas) { 84 return(0); 85 } 86 /* for plt relocation usage */ 87 if (object->Dyn.info[DT_JMPREL] != 0) { 88 /* resolver stub not set up */ 89 Elf32_Addr val; 90 91 first_rela = (Elf32_Addr *) 92 (((Elf32_Rela *)(object->Dyn.info[DT_JMPREL]))->r_offset 93 + loff); 94 /* Need to construct table to do jumps */ 95 pltcall = (Elf32_Addr *)(first_rela) - 12; 96 #ifdef DL_PRINTF_DEBUG 97 _dl_printf("creating pltcall at %x\n", pltcall); 98 _dl_printf("md_reloc( jumprel %x\n", first_rela ); 99 #endif 100 plttable = (Elf32_Addr *) 101 ((Elf32_Addr)first_rela) + (2 * 102 (object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela)) 103 ); 104 105 #ifdef DL_PRINTF_DEBUG 106 _dl_printf("md_reloc: plttbl size %x\n", 107 (object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela)) 108 ); 109 _dl_printf("md_reloc: plttable %x\n", plttable); 110 #endif 111 pltcall[-1]= 0x504c5400; /* PLT tag :-) */ 112 val = ((Elf32_Addr)plttable >> 16) + 113 (((Elf32_Addr)plttable & 0x00008000) >> 15); 114 pltcall[0] = 0x3d6b0000 | val; /* addis r11,r11,.PLTtable@ha*/ 115 val = (Elf32_Addr)plttable & 0x0000ffff; 116 pltcall[1] = 0x816b0000 | val; /* lwz r11,plttable@l(r11) */ 117 pltcall[2] = 0x7d6903a6; /* mtctr r12 */ 118 pltcall[3] = 0x4e800420; /* bctr */ 119 _dl_dcbf(pltcall); 120 _dl_dcbf(&pltcall[3]); 121 } else { 122 first_rela = NULL; 123 } 124 125 /* 126 * Change protection of all write protected segments in the object 127 * so we can do relocations such as REL24, REL16 etc. After 128 * relocation restore protection. 129 */ 130 load_list = object->load_list; 131 while(load_list != NULL) { 132 _dl_mprotect(load_list->start, load_list->size, 133 load_list->prot|PROT_WRITE); 134 load_list = load_list->next; 135 } 136 137 138 for(i = 0; i < numrela; i++, relas++) { 139 Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff); 140 Elf32_Addr ooff; 141 const Elf32_Sym *sym, *this; 142 const char *symn; 143 144 if(ELF32_R_SYM(relas->r_info) == 0xffffff) { 145 continue; 146 } 147 148 sym = object->dyn.symtab; 149 sym += ELF32_R_SYM(relas->r_info); 150 this = sym; 151 symn = object->dyn.strtab + sym->st_name; 152 153 if(ELF32_R_SYM(relas->r_info) && 154 !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 155 ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE)) { 156 157 ooff = _dl_find_symbol(symn, _dl_objects, &this, 0, 0); 158 if(!this && ELF32_ST_BIND(sym->st_info) == STB_GLOBAL) { 159 _dl_printf("%s:" 160 " %s :can't resolve reference '%s'\n", 161 _dl_progname, object->load_name, 162 symn); 163 fails++; 164 } 165 166 } 167 168 switch(ELF32_R_TYPE(relas->r_info)) { 169 #if 1 170 case RELOC_32: 171 if(ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 172 (ELF32_ST_TYPE(sym->st_info) == STT_SECTION || 173 ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { 174 *r_addr = ooff + relas->r_addend; 175 } else { 176 *r_addr = ooff + this->st_value + 177 relas->r_addend; 178 } 179 break; 180 #endif 181 case RELOC_RELATIVE: 182 if(ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 183 (ELF32_ST_TYPE(sym->st_info) == STT_SECTION || 184 ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { 185 *r_addr = loff + relas->r_addend; 186 187 #ifdef DL_PRINTF_DEBUG 188 _dl_printf("rel1 r_addr %x val %x loff %x ooff %x addend %x\n", r_addr, 189 loff + relas->r_addend, loff, ooff, relas->r_addend); 190 #endif 191 192 } else { 193 *r_addr = loff + this->st_value + 194 relas->r_addend; 195 } 196 break; 197 case RELOC_JMP_SLOT: 198 { 199 Elf32_Addr val = ooff + this->st_value + 200 relas->r_addend - (Elf32_Addr)r_addr; 201 if (!(((val & 0xfe000000) == 0x00000000) || 202 ((val & 0xfe000000) == 0xfe000000))) 203 { 204 int index; 205 #ifdef DL_PRINTF_DEBUG 206 _dl_printf(" ooff %x, sym val %x, addend %x" 207 " r_addr %x symn [%s] -> %x\n", 208 ooff, this->st_value, relas->r_addend, 209 r_addr, symn, val); 210 #endif 211 /* if offset is > RELOC_24 deal with it */ 212 index = (r_addr - first_rela) >> 1; 213 214 if (index > (2 << 14)) { 215 216 /* addis r11,r11,.PLTtable@ha*/ 217 val = (index*4 >> 16) + 218 ((index*4 & 0x00008000) >> 15); 219 r_addr[0] = 0x3d600000 | val; 220 val = (Elf32_Addr)pltcall - 221 (Elf32_Addr)&r_addr[2]; 222 r_addr[1] = 0x396b0000 | val; 223 val &= ~0xfc000000; 224 val |= 0x48000000; 225 r_addr[2] = val; 226 227 } else { 228 #ifdef DL_PRINTF_DEBUG 229 _dl_printf(" index %d, pltcall %x r_addr %x\n", 230 index, pltcall, r_addr); 231 #endif 232 233 r_addr[0] = 0x39600000 | (index * 4); 234 val = (Elf32_Addr)pltcall - 235 (Elf32_Addr)&r_addr[1]; 236 val &= ~0xfc000000; 237 val |= 0x48000000; 238 r_addr[1] = val; 239 240 } 241 _dl_dcbf(r_addr); 242 _dl_dcbf(&r_addr[2]); 243 val= ooff + this->st_value + 244 relas->r_addend; 245 #ifdef DL_PRINTF_DEBUG 246 _dl_printf(" symn [%s] val 0x%x\n", symn, val); 247 #endif 248 plttable[index] = val; 249 } else { 250 /* if the offset is small enough, 251 * branch directy to the dest 252 */ 253 val &= ~0xfc000000; 254 val |= 0x48000000; 255 *r_addr = val; 256 _dl_dcbf(r_addr); 257 } 258 } 259 260 break; 261 case RELOC_GLOB_DAT: 262 *r_addr = ooff + this->st_value + relas->r_addend; 263 break; 264 #if 1 265 /* should not be supported ??? */ 266 case RELOC_REL24: 267 { 268 Elf32_Addr val = ooff + this->st_value + 269 relas->r_addend - (Elf32_Addr)r_addr; 270 if ((val & 0xfe000000 != 0) && 271 (val & 0xfe000000 != 0xfe000000)) { 272 /* invalid offset */ 273 _dl_exit(20); 274 } 275 val &= ~0xfc000003; 276 val |= (*r_addr & 0xfc000003); 277 *r_addr = val; 278 279 _dl_dcbf(r_addr); 280 } 281 break; 282 #endif 283 #if 1 284 case RELOC_16_LO: 285 { 286 Elf32_Addr val; 287 288 val = loff + relas->r_addend; 289 *(Elf32_Half *)r_addr = val; 290 291 _dl_dcbf(r_addr); 292 } 293 break; 294 #endif 295 #if 1 296 case RELOC_16_HI: 297 { 298 Elf32_Addr val; 299 300 val = loff + relas->r_addend; 301 *(Elf32_Half *)r_addr = (val >> 16); 302 303 _dl_dcbf(r_addr); 304 } 305 break; 306 #endif 307 #if 1 308 case RELOC_16_HA: 309 { 310 Elf32_Addr val; 311 312 val = loff + relas->r_addend; 313 *(Elf32_Half *)r_addr = ((val + 0x8000) >> 16); 314 315 _dl_dcbf(r_addr); 316 } 317 break; 318 #endif 319 case RELOC_REL14_TAKEN: 320 /* val |= 1 << (31-10) XXX? */ 321 case RELOC_REL14: 322 case RELOC_REL14_NTAKEN: 323 { 324 Elf32_Addr val = ooff + this->st_value + 325 relas->r_addend - (Elf32_Addr)r_addr; 326 if (((val & 0xffff8000) != 0) && 327 ((val & 0xffff8000) != 0xffff8000)) 328 { 329 /* invalid offset */ 330 _dl_exit(20); 331 } 332 val &= ~0xffff0003; 333 val |= (*r_addr & 0xffff0003); 334 *r_addr = val; 335 #ifdef DL_PRINTF_DEBUG 336 _dl_printf("rel 14 %x val %x\n", 337 r_addr, val); 338 #endif 339 340 _dl_dcbf(r_addr); 341 } 342 break; 343 case RELOC_COPY: 344 #ifdef DL_PRINTF_DEBUG 345 _dl_printf("copy r_addr %x, sym %x [%s] size %d val %x\n", 346 r_addr, sym, symn, sym->st_size, 347 (ooff + this->st_value+ 348 relas->r_addend) 349 350 ); 351 #endif 352 { 353 /* we need to find a symbol, that is not in the current object, 354 * start looking at the beginning of the list, searching all objects 355 * but _not_ the current object, first one found wins. 356 */ 357 elf_object_t *cobj; 358 const Elf32_Sym *cpysrc = NULL; 359 Elf32_Addr src_loff; 360 int size; 361 for (cobj = _dl_objects; 362 cobj != NULL && cpysrc == NULL; 363 cobj = cobj->next) 364 { 365 if (object != cobj) { 366 367 /* only look in this object */ 368 src_loff = _dl_find_symbol(symn, cobj, 369 &cpysrc, 1, 1); 370 } 371 } 372 if (cpysrc == NULL) { 373 _dl_printf("symbol not found [%s] \n", symn); 374 } else { 375 size = sym->st_size; 376 if (sym->st_size != cpysrc->st_size) { 377 _dl_printf("symbols size differ [%s] \n", symn); 378 size = sym->st_size < cpysrc->st_size ? 379 sym->st_size : cpysrc->st_size; 380 } 381 #ifdef DL_PRINTF_DEBUG 382 _dl_printf(" found other symbol at %x size %d\n", 383 src_loff + cpysrc->st_value, cpysrc->st_size); 384 #endif 385 _dl_bcopy((void *)(src_loff + cpysrc->st_value), 386 (void *)(ooff + this->st_value+ relas->r_addend), 387 size); 388 } 389 } 390 break; 391 case RELOC_NONE: 392 break; 393 394 default: 395 _dl_printf("%s:" 396 " %s: unsupported relocation '%s' %d at %x\n", 397 _dl_progname, object->load_name, symn, 398 ELF32_R_TYPE(relas->r_info), r_addr ); 399 _dl_exit(1); 400 } 401 } 402 object->status |= STAT_RELOC_DONE; 403 load_list = object->load_list; 404 while(load_list != NULL) { 405 _dl_mprotect(load_list->start, load_list->size, load_list->prot); 406 load_list = load_list->next; 407 } 408 return(fails); 409 } 410 411 /* 412 * Relocate the Global Offset Table (GOT). Currently we don't 413 * do lazy evaluation here because the GNU linker doesn't 414 * follow the ABI spec which says that if an external symbol 415 * is referenced by other relocations than CALL16 and 26 it 416 * should not be given a stub and have a zero value in the 417 * symbol table. By not doing so, we can't use pointers to 418 * external functions and use them in comparitions... 419 */ 420 void 421 _dl_md_reloc_got(elf_object_t *object, int lazy) 422 { 423 /* relocations all done via rela relocations above */ 424 } 425 426 /* should not be defined here, but is is 32 for all powerpc 603-G4 */ 427 #define CACHELINESIZE 32 428 void 429 _dl_syncicache(char *from, size_t len) 430 { 431 unsigned int off = 0; 432 int l = len + ((int)from & (CACHELINESIZE-1)); 433 434 while (off < l) { 435 asm volatile ("dcbst %1,%0" :: "r"(from), "r"(off)); 436 asm volatile ("sync"); 437 asm volatile ("icbi %1, %0" :: "r"(from), "r"(off)); 438 asm volatile ("sync"); 439 asm volatile ("isync"); 440 441 off += CACHELINESIZE; 442 } 443 } 444