1 /* $NetBSD: mdreloc.c,v 1.29 2011/11/22 15:25:28 joerg Exp $ */ 2 3 #include <sys/cdefs.h> 4 #ifndef lint 5 __RCSID("$NetBSD: mdreloc.c,v 1.29 2011/11/22 15:25:28 joerg Exp $"); 6 #endif /* not lint */ 7 8 #include <sys/cdefs.h> 9 #ifndef lint 10 __RCSID("$NetBSD: mdreloc.c,v 1.29 2011/11/22 15:25:28 joerg Exp $"); 11 #endif /* not lint */ 12 13 #include <sys/types.h> 14 15 #include "debug.h" 16 #include "rtld.h" 17 18 void _rtld_bind_start(void); 19 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 20 caddr_t _rtld_bind(const Obj_Entry *, Elf_Word); 21 static inline int _rtld_relocate_plt_object(const Obj_Entry *, 22 const Elf_Rela *, Elf_Addr *); 23 24 25 void 26 _rtld_setup_pltgot(const Obj_Entry *obj) 27 { 28 obj->pltgot[1] = (Elf_Addr) obj; 29 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 30 } 31 32 void 33 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 34 { 35 const Elf_Rela *rela = 0, *relalim; 36 Elf_Addr relasz = 0; 37 Elf_Addr *where; 38 39 for (; dynp->d_tag != DT_NULL; dynp++) { 40 switch (dynp->d_tag) { 41 case DT_RELA: 42 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 43 break; 44 case DT_RELASZ: 45 relasz = dynp->d_un.d_val; 46 break; 47 } 48 } 49 relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz); 50 for (; rela < relalim; rela++) { 51 where = (Elf_Addr *)(relocbase + rela->r_offset); 52 *where += (Elf_Addr)relocbase; 53 } 54 } 55 56 int 57 _rtld_relocate_nonplt_objects(Obj_Entry *obj) 58 { 59 const Elf_Rela *rela; 60 61 for (rela = obj->rela; rela < obj->relalim; rela++) { 62 Elf_Addr *where; 63 const Elf_Sym *def; 64 const Obj_Entry *defobj; 65 Elf_Addr tmp; 66 unsigned long symnum; 67 68 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 69 symnum = ELF_R_SYM(rela->r_info); 70 71 switch (ELF_R_TYPE(rela->r_info)) { 72 case R_TYPE(NONE): 73 break; 74 75 #if 1 /* XXX should not occur */ 76 case R_TYPE(PC32): 77 def = _rtld_find_symdef(symnum, obj, &defobj, false); 78 if (def == NULL) 79 return -1; 80 81 tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 82 rela->r_addend) - (Elf_Addr)where; 83 if (*where != tmp) 84 *where = tmp; 85 rdbg(("PC32 %s in %s --> %p in %s", 86 obj->strtab + obj->symtab[symnum].st_name, 87 obj->path, (void *)*where, defobj->path)); 88 break; 89 90 case R_TYPE(GOT32): 91 #endif 92 case R_TYPE(32): 93 case R_TYPE(GLOB_DAT): 94 def = _rtld_find_symdef(symnum, obj, &defobj, false); 95 if (def == NULL) 96 return -1; 97 98 tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 99 rela->r_addend); 100 if (*where != tmp) 101 *where = tmp; 102 rdbg(("32/GLOB_DAT %s in %s --> %p in %s", 103 obj->strtab + obj->symtab[symnum].st_name, 104 obj->path, (void *)*where, defobj->path)); 105 break; 106 107 case R_TYPE(RELATIVE): 108 *where += (Elf_Addr)obj->relocbase; 109 rdbg(("RELATIVE in %s --> %p", obj->path, 110 (void *)*where)); 111 break; 112 113 case R_TYPE(COPY): 114 /* 115 * These are deferred until all other relocations have 116 * been done. All we do here is make sure that the 117 * COPY relocation is not in a shared library. They 118 * are allowed only in executable files. 119 */ 120 if (obj->isdynamic) { 121 _rtld_error( 122 "%s: Unexpected R_COPY relocation in shared library", 123 obj->path); 124 return -1; 125 } 126 rdbg(("COPY (avoid in main)")); 127 break; 128 129 case R_TYPE(TLS_DTPMOD32): 130 def = _rtld_find_symdef(symnum, obj, &defobj, false); 131 if (def == NULL) 132 return -1; 133 134 *where = (Elf_Addr)defobj->tlsindex; 135 rdbg(("DTPMOD32 %s in %s --> %p in %s", 136 obj->strtab + obj->symtab[symnum].st_name, 137 obj->path, (void *)*where, defobj->path)); 138 break; 139 140 case R_TYPE(TLS_DTPREL32): 141 def = _rtld_find_symdef(symnum, obj, &defobj, false); 142 if (def == NULL) 143 return -1; 144 145 if (!defobj->tls_done && _rtld_tls_offset_allocate(obj)) 146 return -1; 147 148 *where = (Elf_Addr)(def->st_value + rela->r_addend 149 - TLS_DTV_OFFSET); 150 rdbg(("DTPREL32 %s in %s --> %p in %s", 151 obj->strtab + obj->symtab[symnum].st_name, 152 obj->path, (void *)*where, defobj->path)); 153 break; 154 155 case R_TYPE(TLS_TPREL32): 156 def = _rtld_find_symdef(symnum, obj, &defobj, false); 157 if (def == NULL) 158 return -1; 159 160 if (!defobj->tls_done && _rtld_tls_offset_allocate(obj)) 161 return -1; 162 163 *where = (Elf_Addr)(def->st_value + rela->r_addend 164 + defobj->tlsoffset - TLS_TP_OFFSET); 165 rdbg(("TPREL32 %s in %s --> %p in %s", 166 obj->strtab + obj->symtab[symnum].st_name, 167 obj->path, (void *)*where, defobj->path)); 168 break; 169 170 default: 171 rdbg(("sym = %lu, type = %lu, offset = %p, " 172 "addend = %p, contents = %p, symbol = %s", 173 symnum, (u_long)ELF_R_TYPE(rela->r_info), 174 (void *)rela->r_offset, (void *)rela->r_addend, 175 (void *)*where, 176 obj->strtab + obj->symtab[symnum].st_name)); 177 _rtld_error("%s: Unsupported relocation type %ld " 178 "in non-PLT relocations", 179 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 180 return -1; 181 } 182 } 183 return 0; 184 } 185 186 int 187 _rtld_relocate_plt_lazy(const Obj_Entry *obj) 188 { 189 const Elf_Rela *rela; 190 191 if (!obj->relocbase) 192 return 0; 193 194 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 195 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 196 197 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); 198 199 /* Just relocate the GOT slots pointing into the PLT */ 200 *where += (Elf_Addr)obj->relocbase; 201 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 202 } 203 204 return 0; 205 } 206 207 static inline int 208 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 209 Elf_Addr *tp) 210 { 211 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 212 Elf_Addr new_value; 213 const Elf_Sym *def; 214 const Obj_Entry *defobj; 215 unsigned long info = rela->r_info; 216 217 assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT)); 218 219 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); 220 if (__predict_false(def == NULL)) 221 return -1; 222 if (__predict_false(def == &_rtld_sym_zero)) 223 return 0; 224 225 assert(rela->r_addend == 0); 226 new_value = (Elf_Addr)(defobj->relocbase + def->st_value + 227 rela->r_addend); 228 rdbg(("bind now/fixup in %s --> old=%p new=%p", 229 defobj->strtab + def->st_name, (void *)*where, (void *)new_value)); 230 if (*where != new_value) 231 *where = new_value; 232 233 if (tp) 234 *tp = new_value - rela->r_addend; 235 236 return 0; 237 } 238 239 caddr_t 240 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 241 { 242 const Elf_Rela *rela = (const Elf_Rela *)((const uint8_t *)obj->pltrela + reloff); 243 Elf_Addr result; 244 int err; 245 246 result = 0; /* XXX gcc */ 247 248 _rtld_shared_enter(); 249 err = _rtld_relocate_plt_object(obj, rela, &result); 250 if (err) 251 _rtld_die(); 252 _rtld_shared_exit(); 253 254 return (caddr_t)result; 255 } 256 257 int 258 _rtld_relocate_plt_objects(const Obj_Entry *obj) 259 { 260 const Elf_Rela *rela; 261 262 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) 263 if (_rtld_relocate_plt_object(obj, rela, NULL) < 0) 264 return -1; 265 266 return 0; 267 } 268