1129204Scognet/* $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */ 2129204Scognet 3129204Scognet#include <sys/cdefs.h> 4129204Scognet__FBSDID("$FreeBSD$"); 5129204Scognet#include <sys/param.h> 6129204Scognet#include <sys/mman.h> 7129204Scognet 8129204Scognet#include <errno.h> 9129204Scognet#include <stdio.h> 10129204Scognet#include <stdlib.h> 11129204Scognet#include <string.h> 12129204Scognet#include <unistd.h> 13237394Smarius 14237394Smarius#include "machine/sysarch.h" 15237394Smarius 16129204Scognet#include "debug.h" 17129204Scognet#include "rtld.h" 18129204Scognet 19129204Scognetvoid 20129204Scognetinit_pltgot(Obj_Entry *obj) 21129204Scognet{ 22129204Scognet if (obj->pltgot != NULL) { 23129204Scognet obj->pltgot[1] = (Elf_Addr) obj; 24129204Scognet obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 25129204Scognet } 26129204Scognet} 27129204Scognet 28129204Scognetint 29129204Scognetdo_copy_relocations(Obj_Entry *dstobj) 30129204Scognet{ 31129204Scognet const Elf_Rel *rellim; 32129204Scognet const Elf_Rel *rel; 33129204Scognet 34129204Scognet assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ 35129204Scognet 36129204Scognet rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); 37129204Scognet for (rel = dstobj->rel; rel < rellim; rel++) { 38129204Scognet if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) { 39129204Scognet void *dstaddr; 40129204Scognet const Elf_Sym *dstsym; 41129204Scognet const char *name; 42129204Scognet size_t size; 43129204Scognet const void *srcaddr; 44129204Scognet const Elf_Sym *srcsym; 45216695Skib const Obj_Entry *srcobj, *defobj; 46216695Skib SymLook req; 47216695Skib int res; 48129204Scognet 49129204Scognet dstaddr = (void *) (dstobj->relocbase + rel->r_offset); 50129204Scognet dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); 51129204Scognet name = dstobj->strtab + dstsym->st_name; 52129204Scognet size = dstsym->st_size; 53216695Skib 54216695Skib symlook_init(&req, name); 55216695Skib req.ventry = fetch_ventry(dstobj, 56216695Skib ELF_R_SYM(rel->r_info)); 57233831Skib req.flags = SYMLOOK_EARLY; 58233831Skib 59216695Skib for (srcobj = dstobj->next; srcobj != NULL; 60216695Skib srcobj = srcobj->next) { 61216695Skib res = symlook_obj(&req, srcobj); 62216695Skib if (res == 0) { 63216695Skib srcsym = req.sym_out; 64216695Skib defobj = req.defobj_out; 65129204Scognet break; 66216695Skib } 67216695Skib } 68129204Scognet if (srcobj == NULL) { 69216695Skib _rtld_error( 70216695Skib"Undefined symbol \"%s\" referenced from COPY relocation in %s", 71216695Skib name, dstobj->path); 72216695Skib return (-1); 73129204Scognet } 74129204Scognet 75216695Skib srcaddr = (const void *)(defobj->relocbase + 76216695Skib srcsym->st_value); 77129204Scognet memcpy(dstaddr, srcaddr, size); 78129204Scognet } 79129204Scognet } 80129204Scognet return 0; 81129204Scognet} 82129204Scognet 83129204Scognetvoid _rtld_bind_start(void); 84129204Scognetvoid _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 85129204Scognet 86129204Scognetint open(); 87129204Scognetint _open(); 88129204Scognetvoid 89129204Scognet_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 90129204Scognet{ 91129204Scognet const Elf_Rel *rel = 0, *rellim; 92129204Scognet Elf_Addr relsz = 0; 93129204Scognet Elf_Addr *where; 94129204Scognet uint32_t size; 95129204Scognet 96129204Scognet for (; dynp->d_tag != DT_NULL; dynp++) { 97129204Scognet switch (dynp->d_tag) { 98129204Scognet case DT_REL: 99129204Scognet rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); 100129204Scognet break; 101129204Scognet case DT_RELSZ: 102129204Scognet relsz = dynp->d_un.d_val; 103129204Scognet break; 104129204Scognet } 105129204Scognet } 106129204Scognet rellim = (const Elf_Rel *)((caddr_t)rel + relsz); 107129204Scognet size = (rellim - 1)->r_offset - rel->r_offset; 108129204Scognet for (; rel < rellim; rel++) { 109129204Scognet where = (Elf_Addr *)(relocbase + rel->r_offset); 110129204Scognet 111129204Scognet *where += (Elf_Addr)relocbase; 112129204Scognet } 113129204Scognet} 114129204Scognet/* 115129204Scognet * It is possible for the compiler to emit relocations for unaligned data. 116129204Scognet * We handle this situation with these inlines. 117129204Scognet */ 118129204Scognet#define RELOC_ALIGNED_P(x) \ 119129204Scognet (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 120129204Scognet 121129204Scognetstatic __inline Elf_Addr 122129204Scognetload_ptr(void *where) 123129204Scognet{ 124129204Scognet Elf_Addr res; 125129204Scognet 126129204Scognet memcpy(&res, where, sizeof(res)); 127129204Scognet 128129204Scognet return (res); 129129204Scognet} 130129204Scognet 131129204Scognetstatic __inline void 132129204Scognetstore_ptr(void *where, Elf_Addr val) 133129204Scognet{ 134129204Scognet 135129204Scognet memcpy(where, &val, sizeof(val)); 136129204Scognet} 137129204Scognet 138129204Scognetstatic int 139216695Skibreloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache, 140233831Skib int flags, RtldLockState *lockstate) 141129204Scognet{ 142129204Scognet Elf_Addr *where; 143129204Scognet const Elf_Sym *def; 144129204Scognet const Obj_Entry *defobj; 145129204Scognet Elf_Addr tmp; 146129204Scognet unsigned long symnum; 147129204Scognet 148129204Scognet where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 149129204Scognet symnum = ELF_R_SYM(rel->r_info); 150129204Scognet 151129204Scognet switch (ELF_R_TYPE(rel->r_info)) { 152129204Scognet case R_ARM_NONE: 153129204Scognet break; 154129204Scognet 155129204Scognet#if 1 /* XXX should not occur */ 156129204Scognet case R_ARM_PC24: { /* word32 S - P + A */ 157129204Scognet Elf32_Sword addend; 158129204Scognet 159129204Scognet /* 160129204Scognet * Extract addend and sign-extend if needed. 161129204Scognet */ 162129204Scognet addend = *where; 163129204Scognet if (addend & 0x00800000) 164129204Scognet addend |= 0xff000000; 165129204Scognet 166233831Skib def = find_symdef(symnum, obj, &defobj, flags, cache, 167216695Skib lockstate); 168129204Scognet if (def == NULL) 169129204Scognet return -1; 170129204Scognet tmp = (Elf_Addr)obj->relocbase + def->st_value 171129204Scognet - (Elf_Addr)where + (addend << 2); 172129204Scognet if ((tmp & 0xfe000000) != 0xfe000000 && 173129204Scognet (tmp & 0xfe000000) != 0) { 174129204Scognet _rtld_error( 175129204Scognet "%s: R_ARM_PC24 relocation @ %p to %s failed " 176129204Scognet "(displacement %ld (%#lx) out of range)", 177129204Scognet obj->path, where, 178129204Scognet obj->strtab + obj->symtab[symnum].st_name, 179129204Scognet (long) tmp, (long) tmp); 180129204Scognet return -1; 181129204Scognet } 182129204Scognet tmp >>= 2; 183129204Scognet *where = (*where & 0xff000000) | (tmp & 0x00ffffff); 184129204Scognet dbg("PC24 %s in %s --> %p @ %p in %s", 185129204Scognet obj->strtab + obj->symtab[symnum].st_name, 186129204Scognet obj->path, (void *)*where, where, defobj->path); 187129204Scognet break; 188129204Scognet } 189129204Scognet#endif 190129204Scognet 191129204Scognet case R_ARM_ABS32: /* word32 B + S + A */ 192129204Scognet case R_ARM_GLOB_DAT: /* word32 B + S */ 193233831Skib def = find_symdef(symnum, obj, &defobj, flags, cache, 194216695Skib lockstate); 195129204Scognet if (def == NULL) 196129204Scognet return -1; 197129204Scognet if (__predict_true(RELOC_ALIGNED_P(where))) { 198129204Scognet tmp = *where + (Elf_Addr)defobj->relocbase + 199129204Scognet def->st_value; 200129204Scognet *where = tmp; 201129204Scognet } else { 202129204Scognet tmp = load_ptr(where) + 203129204Scognet (Elf_Addr)defobj->relocbase + 204129204Scognet def->st_value; 205129204Scognet store_ptr(where, tmp); 206129204Scognet } 207129204Scognet dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s", 208129204Scognet obj->strtab + obj->symtab[symnum].st_name, 209129204Scognet obj->path, (void *)tmp, where, defobj->path); 210129204Scognet break; 211129204Scognet 212129204Scognet case R_ARM_RELATIVE: /* word32 B + A */ 213129204Scognet if (__predict_true(RELOC_ALIGNED_P(where))) { 214129204Scognet tmp = *where + (Elf_Addr)obj->relocbase; 215129204Scognet *where = tmp; 216129204Scognet } else { 217129204Scognet tmp = load_ptr(where) + 218129204Scognet (Elf_Addr)obj->relocbase; 219129204Scognet store_ptr(where, tmp); 220129204Scognet } 221129204Scognet dbg("RELATIVE in %s --> %p", obj->path, 222129204Scognet (void *)tmp); 223129204Scognet break; 224129204Scognet 225129204Scognet case R_ARM_COPY: 226129204Scognet /* 227129204Scognet * These are deferred until all other relocations have 228129204Scognet * been done. All we do here is make sure that the 229129204Scognet * COPY relocation is not in a shared library. They 230129204Scognet * are allowed only in executable files. 231129204Scognet */ 232129204Scognet if (!obj->mainprog) { 233129204Scognet _rtld_error( 234129204Scognet "%s: Unexpected R_COPY relocation in shared library", 235129204Scognet obj->path); 236129204Scognet return -1; 237129204Scognet } 238129204Scognet dbg("COPY (avoid in main)"); 239129204Scognet break; 240129204Scognet 241237394Smarius case R_ARM_TLS_DTPOFF32: 242237394Smarius def = find_symdef(symnum, obj, &defobj, flags, cache, 243237394Smarius lockstate); 244237394Smarius if (def == NULL) 245237394Smarius return -1; 246237394Smarius 247237394Smarius tmp = (Elf_Addr)(def->st_value); 248237394Smarius if (__predict_true(RELOC_ALIGNED_P(where))) 249237394Smarius *where = tmp; 250237394Smarius else 251237394Smarius store_ptr(where, tmp); 252237394Smarius 253237394Smarius dbg("TLS_DTPOFF32 %s in %s --> %p", 254237394Smarius obj->strtab + obj->symtab[symnum].st_name, 255237394Smarius obj->path, (void *)tmp); 256237394Smarius 257237394Smarius break; 258237394Smarius case R_ARM_TLS_DTPMOD32: 259237394Smarius def = find_symdef(symnum, obj, &defobj, flags, cache, 260237394Smarius lockstate); 261237394Smarius if (def == NULL) 262237394Smarius return -1; 263237394Smarius 264237394Smarius tmp = (Elf_Addr)(defobj->tlsindex); 265237394Smarius if (__predict_true(RELOC_ALIGNED_P(where))) 266237394Smarius *where = tmp; 267237394Smarius else 268237394Smarius store_ptr(where, tmp); 269237394Smarius 270237394Smarius dbg("TLS_DTPMOD32 %s in %s --> %p", 271237394Smarius obj->strtab + obj->symtab[symnum].st_name, 272237394Smarius obj->path, (void *)tmp); 273237394Smarius 274237394Smarius break; 275237394Smarius 276237394Smarius case R_ARM_TLS_TPOFF32: 277237394Smarius def = find_symdef(symnum, obj, &defobj, flags, cache, 278237394Smarius lockstate); 279237394Smarius if (def == NULL) 280237394Smarius return -1; 281237394Smarius 282237394Smarius if (!defobj->tls_done && allocate_tls_offset(obj)) 283237394Smarius return -1; 284237394Smarius 285237394Smarius /* XXX: FIXME */ 286237394Smarius tmp = (Elf_Addr)def->st_value + defobj->tlsoffset + 287237394Smarius TLS_TCB_SIZE; 288237394Smarius if (__predict_true(RELOC_ALIGNED_P(where))) 289237394Smarius *where = tmp; 290237394Smarius else 291237394Smarius store_ptr(where, tmp); 292237394Smarius dbg("TLS_TPOFF32 %s in %s --> %p", 293237394Smarius obj->strtab + obj->symtab[symnum].st_name, 294237394Smarius obj->path, (void *)tmp); 295237394Smarius break; 296237394Smarius 297237394Smarius 298129204Scognet default: 299129204Scognet dbg("sym = %lu, type = %lu, offset = %p, " 300129204Scognet "contents = %p, symbol = %s", 301129204Scognet symnum, (u_long)ELF_R_TYPE(rel->r_info), 302129204Scognet (void *)rel->r_offset, (void *)load_ptr(where), 303129204Scognet obj->strtab + obj->symtab[symnum].st_name); 304129204Scognet _rtld_error("%s: Unsupported relocation type %ld " 305129204Scognet "in non-PLT relocations\n", 306129204Scognet obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 307129204Scognet return -1; 308129204Scognet } 309129204Scognet return 0; 310129204Scognet} 311129204Scognet 312129204Scognet/* 313129204Scognet * * Process non-PLT relocations 314129204Scognet * */ 315129204Scognetint 316233831Skibreloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, 317233831Skib RtldLockState *lockstate) 318129204Scognet{ 319129204Scognet const Elf_Rel *rellim; 320129204Scognet const Elf_Rel *rel; 321129204Scognet SymCache *cache; 322129204Scognet int r = -1; 323129204Scognet 324135883Scognet /* The relocation for the dynamic loader has already been done. */ 325135883Scognet if (obj == obj_rtld) 326135883Scognet return (0); 327129204Scognet /* 328129204Scognet * The dynamic loader may be called from a thread, we have 329129204Scognet * limited amounts of stack available so we cannot use alloca(). 330135883Scognet */ 331235396Skib cache = calloc(obj->dynsymcount, sizeof(SymCache)); 332208256Srdivacky /* No need to check for NULL here */ 333208256Srdivacky 334129204Scognet rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); 335129204Scognet for (rel = obj->rel; rel < rellim; rel++) { 336233831Skib if (reloc_nonplt_object(obj, rel, cache, flags, lockstate) < 0) 337129204Scognet goto done; 338129204Scognet } 339129204Scognet r = 0; 340129204Scognetdone: 341208256Srdivacky if (cache != NULL) 342208256Srdivacky free(cache); 343129204Scognet return (r); 344129204Scognet} 345129204Scognet 346129204Scognet/* 347129204Scognet * * Process the PLT relocations. 348129204Scognet * */ 349129204Scognetint 350129204Scognetreloc_plt(Obj_Entry *obj) 351129204Scognet{ 352129204Scognet const Elf_Rel *rellim; 353129204Scognet const Elf_Rel *rel; 354129204Scognet 355129204Scognet rellim = (const Elf_Rel *)((char *)obj->pltrel + 356129204Scognet obj->pltrelsize); 357129204Scognet for (rel = obj->pltrel; rel < rellim; rel++) { 358129204Scognet Elf_Addr *where; 359129204Scognet 360129204Scognet assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); 361129204Scognet 362129204Scognet where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 363129204Scognet *where += (Elf_Addr )obj->relocbase; 364129204Scognet } 365129204Scognet 366129204Scognet return (0); 367129204Scognet} 368129204Scognet 369129204Scognet/* 370129204Scognet * * LD_BIND_NOW was set - force relocation for all jump slots 371129204Scognet * */ 372129204Scognetint 373233831Skibreloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) 374129204Scognet{ 375129204Scognet const Obj_Entry *defobj; 376129204Scognet const Elf_Rel *rellim; 377129204Scognet const Elf_Rel *rel; 378129204Scognet const Elf_Sym *def; 379129204Scognet Elf_Addr *where; 380129204Scognet Elf_Addr target; 381129204Scognet 382129204Scognet rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); 383129204Scognet for (rel = obj->pltrel; rel < rellim; rel++) { 384129204Scognet assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); 385129204Scognet where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 386129204Scognet def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, 387233831Skib SYMLOOK_IN_PLT | flags, NULL, lockstate); 388129204Scognet if (def == NULL) { 389129204Scognet dbg("reloc_jmpslots: sym not found"); 390129204Scognet return (-1); 391129204Scognet } 392129204Scognet 393129204Scognet target = (Elf_Addr)(defobj->relocbase + def->st_value); 394129204Scognet reloc_jmpslot(where, target, defobj, obj, 395129204Scognet (const Elf_Rel *) rel); 396129204Scognet } 397129204Scognet 398129204Scognet obj->jmpslots_done = true; 399129204Scognet 400129204Scognet return (0); 401129204Scognet} 402129204Scognet 403229503Skibint 404229503Skibreloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) 405229503Skib{ 406229503Skib 407229503Skib /* XXX not implemented */ 408229503Skib return (0); 409229503Skib} 410229503Skib 411229503Skibint 412233831Skibreloc_gnu_ifunc(Obj_Entry *obj, int flags, 413233831Skib struct Struct_RtldLockState *lockstate) 414229503Skib{ 415229503Skib 416229503Skib /* XXX not implemented */ 417229503Skib return (0); 418229503Skib} 419229503Skib 420129204ScognetElf_Addr 421129204Scognetreloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, 422129204Scognet const Obj_Entry *obj, const Elf_Rel *rel) 423129204Scognet{ 424129204Scognet 425129204Scognet assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); 426129204Scognet 427129204Scognet if (*where != target) 428129204Scognet *where = target; 429129204Scognet 430129204Scognet return target; 431129204Scognet} 432129204Scognet 433135680Scognetvoid 434135680Scognetallocate_initial_tls(Obj_Entry *objs) 435135680Scognet{ 436237394Smarius void **_tp = (void **)ARM_TP_ADDRESS; 437237394Smarius 438237394Smarius /* 439237394Smarius * Fix the size of the static TLS block by using the maximum 440237394Smarius * offset allocated so far and adding a bit for dynamic modules to 441237394Smarius * use. 442237394Smarius */ 443237394Smarius 444237394Smarius tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; 445237394Smarius 446237394Smarius (*_tp) = (void *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8); 447135680Scognet} 448135680Scognet 449135680Scognetvoid * 450135680Scognet__tls_get_addr(tls_index* ti) 451135680Scognet{ 452237394Smarius void **_tp = (void **)ARM_TP_ADDRESS; 453237394Smarius char *p; 454237394Smarius 455237394Smarius p = tls_get_addr_common((Elf_Addr **)(*_tp), ti->ti_module, ti->ti_offset); 456237394Smarius 457237394Smarius return (p); 458135680Scognet} 459