1 /* $NetBSD: mdreloc.c,v 1.27 2005/12/24 20:59:30 perry Exp $ */ 2 3 #include <sys/cdefs.h> 4 #ifndef lint 5 __RCSID("$NetBSD: mdreloc.c,v 1.27 2005/12/24 20:59:30 perry Exp $"); 6 #endif /* not lint */ 7 8 #include <sys/types.h> 9 #include <sys/stat.h> 10 11 #include <string.h> 12 13 #include "debug.h" 14 #include "rtld.h" 15 16 void _rtld_bind_start(void); 17 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 18 caddr_t _rtld_bind(const Obj_Entry *, Elf_Word); 19 20 void 21 _rtld_setup_pltgot(const Obj_Entry *obj) 22 { 23 obj->pltgot[1] = (Elf_Addr) obj; 24 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 25 } 26 27 void 28 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 29 { 30 const Elf_Rel *rel = 0, *rellim; 31 Elf_Addr relsz = 0; 32 Elf_Addr *where; 33 34 for (; dynp->d_tag != DT_NULL; dynp++) { 35 switch (dynp->d_tag) { 36 case DT_REL: 37 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); 38 break; 39 case DT_RELSZ: 40 relsz = dynp->d_un.d_val; 41 break; 42 } 43 } 44 rellim = (const Elf_Rel *)((caddr_t)rel + relsz); 45 for (; rel < rellim; rel++) { 46 where = (Elf_Addr *)(relocbase + rel->r_offset); 47 *where += (Elf_Addr)relocbase; 48 } 49 } 50 51 /* 52 * It is possible for the compiler to emit relocations for unaligned data. 53 * We handle this situation with these inlines. 54 */ 55 #define RELOC_ALIGNED_P(x) \ 56 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 57 58 static inline Elf_Addr 59 load_ptr(void *where) 60 { 61 Elf_Addr res; 62 63 memcpy(&res, where, sizeof(res)); 64 65 return (res); 66 } 67 68 static inline void 69 store_ptr(void *where, Elf_Addr val) 70 { 71 72 memcpy(where, &val, sizeof(val)); 73 } 74 75 int 76 _rtld_relocate_nonplt_objects(const Obj_Entry *obj) 77 { 78 const Elf_Rel *rel; 79 80 for (rel = obj->rel; rel < obj->rellim; rel++) { 81 Elf_Addr *where; 82 const Elf_Sym *def; 83 const Obj_Entry *defobj; 84 Elf_Addr tmp; 85 unsigned long symnum; 86 87 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 88 symnum = ELF_R_SYM(rel->r_info); 89 90 switch (ELF_R_TYPE(rel->r_info)) { 91 case R_TYPE(NONE): 92 break; 93 94 #if 1 /* XXX should not occur */ 95 case R_TYPE(PC24): { /* word32 S - P + A */ 96 Elf32_Sword addend; 97 98 /* 99 * Extract addend and sign-extend if needed. 100 */ 101 addend = *where; 102 if (addend & 0x00800000) 103 addend |= 0xff000000; 104 105 def = _rtld_find_symdef(symnum, obj, &defobj, false); 106 if (def == NULL) 107 return -1; 108 tmp = (Elf_Addr)obj->relocbase + def->st_value 109 - (Elf_Addr)where + (addend << 2); 110 if ((tmp & 0xfe000000) != 0xfe000000 && 111 (tmp & 0xfe000000) != 0) { 112 _rtld_error( 113 "%s: R_ARM_PC24 relocation @ %p to %s failed " 114 "(displacement %ld (%#lx) out of range)", 115 obj->path, where, 116 obj->strtab + obj->symtab[symnum].st_name, 117 (long) tmp, (long) tmp); 118 return -1; 119 } 120 tmp >>= 2; 121 *where = (*where & 0xff000000) | (tmp & 0x00ffffff); 122 rdbg(("PC24 %s in %s --> %p @ %p in %s", 123 obj->strtab + obj->symtab[symnum].st_name, 124 obj->path, (void *)*where, where, defobj->path)); 125 break; 126 } 127 #endif 128 129 case R_TYPE(ABS32): /* word32 B + S + A */ 130 case R_TYPE(GLOB_DAT): /* word32 B + S */ 131 def = _rtld_find_symdef(symnum, obj, &defobj, false); 132 if (def == NULL) 133 return -1; 134 if (__predict_true(RELOC_ALIGNED_P(where))) { 135 tmp = *where + (Elf_Addr)defobj->relocbase + 136 def->st_value; 137 /* Set the Thumb bit, if needed. */ 138 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 139 tmp |= 1; 140 *where = tmp; 141 } else { 142 tmp = load_ptr(where) + 143 (Elf_Addr)defobj->relocbase + 144 def->st_value; 145 /* Set the Thumb bit, if needed. */ 146 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 147 tmp |= 1; 148 store_ptr(where, tmp); 149 } 150 rdbg(("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s", 151 obj->strtab + obj->symtab[symnum].st_name, 152 obj->path, (void *)tmp, where, defobj->path)); 153 break; 154 155 case R_TYPE(RELATIVE): /* word32 B + A */ 156 if (__predict_true(RELOC_ALIGNED_P(where))) { 157 tmp = *where + (Elf_Addr)obj->relocbase; 158 *where = tmp; 159 } else { 160 tmp = load_ptr(where) + 161 (Elf_Addr)obj->relocbase; 162 store_ptr(where, tmp); 163 } 164 rdbg(("RELATIVE in %s --> %p", obj->path, 165 (void *)tmp)); 166 break; 167 168 case R_TYPE(COPY): 169 /* 170 * These are deferred until all other relocations have 171 * been done. All we do here is make sure that the 172 * COPY relocation is not in a shared library. They 173 * are allowed only in executable files. 174 */ 175 if (obj->isdynamic) { 176 _rtld_error( 177 "%s: Unexpected R_COPY relocation in shared library", 178 obj->path); 179 return -1; 180 } 181 rdbg(("COPY (avoid in main)")); 182 break; 183 184 default: 185 rdbg(("sym = %lu, type = %lu, offset = %p, " 186 "contents = %p, symbol = %s", 187 symnum, (u_long)ELF_R_TYPE(rel->r_info), 188 (void *)rel->r_offset, (void *)load_ptr(where), 189 obj->strtab + obj->symtab[symnum].st_name)); 190 _rtld_error("%s: Unsupported relocation type %ld " 191 "in non-PLT relocations\n", 192 obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 193 return -1; 194 } 195 } 196 return 0; 197 } 198 199 int 200 _rtld_relocate_plt_lazy(const Obj_Entry *obj) 201 { 202 const Elf_Rel *rel; 203 204 if (!obj->relocbase) 205 return 0; 206 207 for (rel = obj->pltrel; rel < obj->pltrellim; rel++) { 208 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 209 210 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JUMP_SLOT)); 211 212 /* Just relocate the GOT slots pointing into the PLT */ 213 *where += (Elf_Addr)obj->relocbase; 214 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 215 } 216 217 return 0; 218 } 219 220 caddr_t 221 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 222 { 223 const Elf_Rel *rel = (const Elf_Rel *)((caddr_t)obj->pltrel + reloff); 224 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 225 Elf_Addr new_value; 226 const Elf_Sym *def; 227 const Obj_Entry *defobj; 228 229 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JUMP_SLOT)); 230 231 def = _rtld_find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true); 232 if (def == NULL) 233 _rtld_die(); 234 235 new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 236 /* Set the Thumb bit, if needed. */ 237 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 238 new_value |= 1; 239 rdbg(("bind now/fixup in %s --> old=%p new=%p", 240 defobj->strtab + def->st_name, (void *)*where, (void *)new_value)); 241 if (*where != new_value) 242 *where = new_value; 243 244 return (caddr_t)new_value; 245 } 246 247 int 248 _rtld_relocate_plt_objects(const Obj_Entry *obj) 249 { 250 const Elf_Rel *rel; 251 252 for (rel = obj->pltrel; rel < obj->pltrellim; rel++) { 253 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 254 Elf_Addr target; 255 const Elf_Sym *def; 256 const Obj_Entry *defobj; 257 258 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JUMP_SLOT)); 259 260 def = _rtld_find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, 261 true); 262 if (def == NULL) 263 return -1; 264 target = (Elf_Addr)(defobj->relocbase + def->st_value); 265 /* Set the Thumb bit, if needed. */ 266 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 267 target |= 1; 268 269 rdbg(("bind now/fixup in %s --> old=%p new=%p", 270 defobj->strtab + def->st_name, (void *)*where, 271 (void *)target)); 272 if (*where != target) 273 *where = target; 274 } 275 return 0; 276 } 277