1 /* $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */ 2 3 #include <sys/cdefs.h> 4 __FBSDID("$FreeBSD$"); 5 #include <sys/param.h> 6 #include <sys/mman.h> 7 8 #include <errno.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <unistd.h> 13 #include "debug.h" 14 #include "rtld.h" 15 16 void 17 init_pltgot(Obj_Entry *obj) 18 { 19 if (obj->pltgot != NULL) { 20 obj->pltgot[1] = (Elf_Addr) obj; 21 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 22 } 23 } 24 25 int 26 do_copy_relocations(Obj_Entry *dstobj) 27 { 28 const Elf_Rel *rellim; 29 const Elf_Rel *rel; 30 31 assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ 32 33 rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); 34 for (rel = dstobj->rel; rel < rellim; rel++) { 35 if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) { 36 void *dstaddr; 37 const Elf_Sym *dstsym; 38 const char *name; 39 unsigned long hash; 40 size_t size; 41 const void *srcaddr; 42 const Elf_Sym *srcsym; 43 Obj_Entry *srcobj; 44 45 dstaddr = (void *) (dstobj->relocbase + rel->r_offset); 46 dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); 47 name = dstobj->strtab + dstsym->st_name; 48 hash = elf_hash(name); 49 size = dstsym->st_size; 50 51 for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) 52 if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) 53 break; 54 55 if (srcobj == NULL) { 56 _rtld_error("Undefined symbol \"%s\" referenced from COPY" 57 " relocation in %s", name, dstobj->path); 58 return -1; 59 } 60 61 srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value); 62 memcpy(dstaddr, srcaddr, size); 63 } 64 } 65 return 0; 66 } 67 68 void _rtld_bind_start(void); 69 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 70 71 int open(); 72 int _open(); 73 void 74 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 75 { 76 const Elf_Rel *rel = 0, *rellim; 77 Elf_Addr relsz = 0; 78 Elf_Addr *where; 79 uint32_t size; 80 81 for (; dynp->d_tag != DT_NULL; dynp++) { 82 switch (dynp->d_tag) { 83 case DT_REL: 84 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); 85 break; 86 case DT_RELSZ: 87 relsz = dynp->d_un.d_val; 88 break; 89 } 90 } 91 rellim = (const Elf_Rel *)((caddr_t)rel + relsz); 92 size = (rellim - 1)->r_offset - rel->r_offset; 93 mprotect((void*)relocbase, size, PROT_READ|PROT_WRITE|PROT_EXEC); 94 for (; rel < rellim; rel++) { 95 where = (Elf_Addr *)(relocbase + rel->r_offset); 96 97 *where += (Elf_Addr)relocbase; 98 } 99 mprotect((void*)relocbase, size, PROT_READ|PROT_EXEC); 100 } 101 /* 102 * It is possible for the compiler to emit relocations for unaligned data. 103 * We handle this situation with these inlines. 104 */ 105 #define RELOC_ALIGNED_P(x) \ 106 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 107 108 static __inline Elf_Addr 109 load_ptr(void *where) 110 { 111 Elf_Addr res; 112 113 memcpy(&res, where, sizeof(res)); 114 115 return (res); 116 } 117 118 static __inline void 119 store_ptr(void *where, Elf_Addr val) 120 { 121 122 memcpy(where, &val, sizeof(val)); 123 } 124 125 static int 126 reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache) 127 { 128 Elf_Addr *where; 129 const Elf_Sym *def; 130 const Obj_Entry *defobj; 131 Elf_Addr tmp; 132 unsigned long symnum; 133 134 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 135 symnum = ELF_R_SYM(rel->r_info); 136 137 switch (ELF_R_TYPE(rel->r_info)) { 138 case R_ARM_NONE: 139 break; 140 141 #if 1 /* XXX should not occur */ 142 case R_ARM_PC24: { /* word32 S - P + A */ 143 Elf32_Sword addend; 144 145 /* 146 * Extract addend and sign-extend if needed. 147 */ 148 addend = *where; 149 if (addend & 0x00800000) 150 addend |= 0xff000000; 151 152 def = find_symdef(symnum, obj, &defobj, false, cache); 153 if (def == NULL) 154 return -1; 155 tmp = (Elf_Addr)obj->relocbase + def->st_value 156 - (Elf_Addr)where + (addend << 2); 157 if ((tmp & 0xfe000000) != 0xfe000000 && 158 (tmp & 0xfe000000) != 0) { 159 _rtld_error( 160 "%s: R_ARM_PC24 relocation @ %p to %s failed " 161 "(displacement %ld (%#lx) out of range)", 162 obj->path, where, 163 obj->strtab + obj->symtab[symnum].st_name, 164 (long) tmp, (long) tmp); 165 return -1; 166 } 167 tmp >>= 2; 168 *where = (*where & 0xff000000) | (tmp & 0x00ffffff); 169 dbg("PC24 %s in %s --> %p @ %p in %s", 170 obj->strtab + obj->symtab[symnum].st_name, 171 obj->path, (void *)*where, where, defobj->path); 172 break; 173 } 174 #endif 175 176 case R_ARM_ABS32: /* word32 B + S + A */ 177 case R_ARM_GLOB_DAT: /* word32 B + S */ 178 def = find_symdef(symnum, obj, &defobj, false, cache); 179 if (def == NULL) 180 return -1; 181 if (__predict_true(RELOC_ALIGNED_P(where))) { 182 tmp = *where + (Elf_Addr)defobj->relocbase + 183 def->st_value; 184 *where = tmp; 185 } else { 186 tmp = load_ptr(where) + 187 (Elf_Addr)defobj->relocbase + 188 def->st_value; 189 store_ptr(where, tmp); 190 } 191 dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s", 192 obj->strtab + obj->symtab[symnum].st_name, 193 obj->path, (void *)tmp, where, defobj->path); 194 break; 195 196 case R_ARM_RELATIVE: /* word32 B + A */ 197 if (__predict_true(RELOC_ALIGNED_P(where))) { 198 tmp = *where + (Elf_Addr)obj->relocbase; 199 *where = tmp; 200 } else { 201 tmp = load_ptr(where) + 202 (Elf_Addr)obj->relocbase; 203 store_ptr(where, tmp); 204 } 205 dbg("RELATIVE in %s --> %p", obj->path, 206 (void *)tmp); 207 break; 208 209 case R_ARM_COPY: 210 /* 211 * These are deferred until all other relocations have 212 * been done. All we do here is make sure that the 213 * COPY relocation is not in a shared library. They 214 * are allowed only in executable files. 215 */ 216 if (!obj->mainprog) { 217 _rtld_error( 218 "%s: Unexpected R_COPY relocation in shared library", 219 obj->path); 220 return -1; 221 } 222 dbg("COPY (avoid in main)"); 223 break; 224 225 default: 226 dbg("sym = %lu, type = %lu, offset = %p, " 227 "contents = %p, symbol = %s", 228 symnum, (u_long)ELF_R_TYPE(rel->r_info), 229 (void *)rel->r_offset, (void *)load_ptr(where), 230 obj->strtab + obj->symtab[symnum].st_name); 231 _rtld_error("%s: Unsupported relocation type %ld " 232 "in non-PLT relocations\n", 233 obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 234 return -1; 235 } 236 return 0; 237 } 238 239 /* 240 * * Process non-PLT relocations 241 * */ 242 int 243 reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) 244 { 245 const Elf_Rel *rellim; 246 const Elf_Rel *rel; 247 SymCache *cache; 248 int bytes = obj->nchains * sizeof(SymCache); 249 int r = -1; 250 251 /* 252 * The dynamic loader may be called from a thread, we have 253 * limited amounts of stack available so we cannot use alloca(). 254 * */ 255 cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); 256 if (cache == MAP_FAILED) 257 cache = NULL; 258 259 rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); 260 for (rel = obj->rel; rel < rellim; rel++) { 261 if (reloc_nonplt_object(obj, rel, cache) < 0) 262 goto done; 263 } 264 r = 0; 265 done: 266 if (cache) { 267 munmap(cache, bytes); 268 } 269 return (r); 270 } 271 272 /* 273 * * Process the PLT relocations. 274 * */ 275 int 276 reloc_plt(Obj_Entry *obj) 277 { 278 const Elf_Rel *rellim; 279 const Elf_Rel *rel; 280 281 rellim = (const Elf_Rel *)((char *)obj->pltrel + 282 obj->pltrelsize); 283 for (rel = obj->pltrel; rel < rellim; rel++) { 284 Elf_Addr *where; 285 286 assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); 287 288 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 289 *where += (Elf_Addr )obj->relocbase; 290 } 291 292 return (0); 293 } 294 295 /* 296 * * LD_BIND_NOW was set - force relocation for all jump slots 297 * */ 298 int 299 reloc_jmpslots(Obj_Entry *obj) 300 { 301 const Obj_Entry *defobj; 302 const Elf_Rel *rellim; 303 const Elf_Rel *rel; 304 const Elf_Sym *def; 305 Elf_Addr *where; 306 Elf_Addr target; 307 308 rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); 309 for (rel = obj->pltrel; rel < rellim; rel++) { 310 assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); 311 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 312 def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, 313 true, NULL); 314 if (def == NULL) { 315 dbg("reloc_jmpslots: sym not found"); 316 return (-1); 317 } 318 319 target = (Elf_Addr)(defobj->relocbase + def->st_value); 320 reloc_jmpslot(where, target, defobj, obj, 321 (const Elf_Rel *) rel); 322 } 323 324 obj->jmpslots_done = true; 325 326 return (0); 327 } 328 329 Elf_Addr 330 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, 331 const Obj_Entry *obj, const Elf_Rel *rel) 332 { 333 334 assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); 335 336 if (*where != target) 337 *where = target; 338 339 return target; 340 } 341 342