| 1 | /* $NetBSD: mdreloc.c,v 1.41 2018/04/03 21:10:27 joerg Exp $ */ |
| 2 | |
| 3 | #include <sys/cdefs.h> |
| 4 | #ifndef lint |
| 5 | __RCSID("$NetBSD: mdreloc.c,v 1.41 2018/04/03 21:10:27 joerg Exp $" ); |
| 6 | #endif /* not lint */ |
| 7 | |
| 8 | #include <sys/types.h> |
| 9 | #include <sys/ucontext.h> |
| 10 | |
| 11 | #include "debug.h" |
| 12 | #include "rtld.h" |
| 13 | |
| 14 | void _rtld_bind_start(void); |
| 15 | void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); |
| 16 | caddr_t _rtld_bind(const Obj_Entry *, Elf_Word); |
| 17 | |
| 18 | void |
| 19 | _rtld_setup_pltgot(const Obj_Entry *obj) |
| 20 | { |
| 21 | obj->pltgot[1] = (Elf_Addr) obj; |
| 22 | obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; |
| 23 | } |
| 24 | |
| 25 | void |
| 26 | _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) |
| 27 | { |
| 28 | const Elf_Rel *rel = 0, *rellim; |
| 29 | Elf_Addr relsz = 0; |
| 30 | Elf_Addr *where; |
| 31 | |
| 32 | for (; dynp->d_tag != DT_NULL; dynp++) { |
| 33 | switch (dynp->d_tag) { |
| 34 | case DT_REL: |
| 35 | rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); |
| 36 | break; |
| 37 | case DT_RELSZ: |
| 38 | relsz = dynp->d_un.d_val; |
| 39 | break; |
| 40 | } |
| 41 | } |
| 42 | if (rel == 0 || relsz == 0) |
| 43 | return; |
| 44 | rellim = (const Elf_Rel *)((const uint8_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 | int |
| 52 | _rtld_relocate_nonplt_objects(Obj_Entry *obj) |
| 53 | { |
| 54 | const Elf_Rel *rel; |
| 55 | Elf_Addr target = 0; |
| 56 | const Elf_Sym *def = NULL; |
| 57 | const Obj_Entry *defobj = NULL; |
| 58 | unsigned long last_symnum = ULONG_MAX; |
| 59 | |
| 60 | for (rel = obj->rel; rel < obj->rellim; rel++) { |
| 61 | Elf_Addr *where; |
| 62 | Elf_Addr tmp; |
| 63 | unsigned long symnum; |
| 64 | |
| 65 | where = (Elf_Addr *)(obj->relocbase + rel->r_offset); |
| 66 | |
| 67 | switch (ELF_R_TYPE(rel->r_info)) { |
| 68 | case R_TYPE(PC32): |
| 69 | case R_TYPE(GOT32): |
| 70 | case R_TYPE(32): |
| 71 | case R_TYPE(GLOB_DAT): |
| 72 | case R_TYPE(TLS_TPOFF): |
| 73 | case R_TYPE(TLS_TPOFF32): |
| 74 | case R_TYPE(TLS_DTPMOD32): |
| 75 | case R_TYPE(TLS_DTPOFF32): |
| 76 | symnum = ELF_R_SYM(rel->r_info); |
| 77 | if (symnum != last_symnum) { |
| 78 | last_symnum = symnum; |
| 79 | def = _rtld_find_symdef(symnum, obj, &defobj, |
| 80 | false); |
| 81 | if (def == NULL) |
| 82 | return -1; |
| 83 | } |
| 84 | break; |
| 85 | default: |
| 86 | break; |
| 87 | } |
| 88 | |
| 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(PC32): |
| 96 | target = (Elf_Addr)(defobj->relocbase + def->st_value); |
| 97 | |
| 98 | *where += target - (Elf_Addr)where; |
| 99 | rdbg(("PC32 %s in %s --> %p in %s" , |
| 100 | obj->strtab + obj->symtab[symnum].st_name, |
| 101 | obj->path, (void *)*where, defobj->path)); |
| 102 | break; |
| 103 | |
| 104 | case R_TYPE(GOT32): |
| 105 | #endif |
| 106 | case R_TYPE(32): |
| 107 | case R_TYPE(GLOB_DAT): |
| 108 | target = (Elf_Addr)(defobj->relocbase + def->st_value); |
| 109 | |
| 110 | tmp = target + *where; |
| 111 | if (*where != tmp) |
| 112 | *where = tmp; |
| 113 | rdbg(("32/GLOB_DAT %s in %s --> %p in %s" , |
| 114 | obj->strtab + obj->symtab[symnum].st_name, |
| 115 | obj->path, (void *)*where, defobj->path)); |
| 116 | break; |
| 117 | |
| 118 | |
| 119 | case R_TYPE(IRELATIVE): |
| 120 | /* IFUNC relocations are handled in _rtld_call_ifunc */ |
| 121 | if (obj->ifunc_remaining_nonplt == 0) { |
| 122 | obj->ifunc_remaining_nonplt = |
| 123 | obj->rellim - rel; |
| 124 | } |
| 125 | /* FALL-THROUGH */ |
| 126 | |
| 127 | case R_TYPE(RELATIVE): |
| 128 | *where += (Elf_Addr)obj->relocbase; |
| 129 | rdbg(("RELATIVE in %s --> %p" , obj->path, |
| 130 | (void *)*where)); |
| 131 | break; |
| 132 | |
| 133 | case R_TYPE(COPY): |
| 134 | /* |
| 135 | * These are deferred until all other relocations have |
| 136 | * been done. All we do here is make sure that the |
| 137 | * COPY relocation is not in a shared library. They |
| 138 | * are allowed only in executable files. |
| 139 | */ |
| 140 | if (obj->isdynamic) { |
| 141 | _rtld_error( |
| 142 | "%s: Unexpected R_COPY relocation in shared library" , |
| 143 | obj->path); |
| 144 | return -1; |
| 145 | } |
| 146 | rdbg(("COPY (avoid in main)" )); |
| 147 | break; |
| 148 | |
| 149 | case R_TYPE(TLS_TPOFF): |
| 150 | if (!defobj->tls_done && |
| 151 | _rtld_tls_offset_allocate(obj)) |
| 152 | return -1; |
| 153 | |
| 154 | *where += (Elf_Addr)(def->st_value - defobj->tlsoffset); |
| 155 | |
| 156 | rdbg(("TLS_TPOFF %s in %s --> %p" , |
| 157 | obj->strtab + obj->symtab[symnum].st_name, |
| 158 | obj->path, (void *)*where)); |
| 159 | break; |
| 160 | |
| 161 | case R_TYPE(TLS_TPOFF32): |
| 162 | if (!defobj->tls_done && |
| 163 | _rtld_tls_offset_allocate(obj)) |
| 164 | return -1; |
| 165 | |
| 166 | *where += (Elf_Addr)(defobj->tlsoffset - def->st_value); |
| 167 | rdbg(("TLS_TPOFF32 %s in %s --> %p" , |
| 168 | obj->strtab + obj->symtab[symnum].st_name, |
| 169 | obj->path, (void *)*where)); |
| 170 | break; |
| 171 | |
| 172 | case R_TYPE(TLS_DTPMOD32): |
| 173 | *where = (Elf_Addr)(defobj->tlsindex); |
| 174 | |
| 175 | rdbg(("TLS_DTPMOD32 %s in %s --> %p" , |
| 176 | obj->strtab + obj->symtab[symnum].st_name, |
| 177 | obj->path, (void *)*where)); |
| 178 | break; |
| 179 | |
| 180 | case R_TYPE(TLS_DTPOFF32): |
| 181 | *where = (Elf_Addr)(def->st_value); |
| 182 | |
| 183 | rdbg(("TLS_DTPOFF32 %s in %s --> %p" , |
| 184 | obj->strtab + obj->symtab[symnum].st_name, |
| 185 | obj->path, (void *)*where)); |
| 186 | |
| 187 | break; |
| 188 | |
| 189 | default: |
| 190 | rdbg(("sym = %lu, type = %lu, offset = %p, " |
| 191 | "contents = %p, symbol = %s" , |
| 192 | (u_long)ELF_R_SYM(rel->r_info), |
| 193 | (u_long)ELF_R_TYPE(rel->r_info), |
| 194 | (void *)rel->r_offset, (void *)*where, |
| 195 | obj->strtab + obj->symtab[symnum].st_name)); |
| 196 | _rtld_error("%s: Unsupported relocation type %ld " |
| 197 | "in non-PLT relocations" , |
| 198 | obj->path, (u_long) ELF_R_TYPE(rel->r_info)); |
| 199 | return -1; |
| 200 | } |
| 201 | } |
| 202 | return 0; |
| 203 | } |
| 204 | |
| 205 | int |
| 206 | _rtld_relocate_plt_lazy(Obj_Entry *obj) |
| 207 | { |
| 208 | const Elf_Rel *rel; |
| 209 | |
| 210 | for (rel = obj->pltrellim; rel-- > obj->pltrel; ) { |
| 211 | Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); |
| 212 | |
| 213 | assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JMP_SLOT) || |
| 214 | ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)); |
| 215 | |
| 216 | if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) |
| 217 | obj->ifunc_remaining = obj->pltrellim - rel; |
| 218 | |
| 219 | /* Just relocate the GOT slots pointing into the PLT */ |
| 220 | *where += (Elf_Addr)obj->relocbase; |
| 221 | rdbg(("fixup !main in %s --> %p" , obj->path, (void *)*where)); |
| 222 | } |
| 223 | |
| 224 | return 0; |
| 225 | } |
| 226 | |
| 227 | static inline int |
| 228 | _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel, |
| 229 | Elf_Addr *tp) |
| 230 | { |
| 231 | Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); |
| 232 | Elf_Addr target; |
| 233 | const Elf_Sym *def; |
| 234 | const Obj_Entry *defobj; |
| 235 | unsigned long info = rel->r_info; |
| 236 | |
| 237 | if (ELF_R_TYPE(info) == R_TYPE(IRELATIVE)) |
| 238 | return 0; |
| 239 | |
| 240 | assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT)); |
| 241 | |
| 242 | def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); |
| 243 | if (__predict_false(def == NULL)) |
| 244 | return -1; |
| 245 | if (__predict_false(def == &_rtld_sym_zero)) |
| 246 | return 0; |
| 247 | |
| 248 | if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { |
| 249 | if (tp == NULL) |
| 250 | return 0; |
| 251 | target = _rtld_resolve_ifunc(defobj, def); |
| 252 | } else { |
| 253 | target = (Elf_Addr)(defobj->relocbase + def->st_value); |
| 254 | } |
| 255 | |
| 256 | rdbg(("bind now/fixup in %s --> old=%p new=%p" , |
| 257 | defobj->strtab + def->st_name, (void *)*where, |
| 258 | (void *)target)); |
| 259 | if (*where != target) |
| 260 | *where = target; |
| 261 | if (tp) |
| 262 | *tp = target; |
| 263 | return 0; |
| 264 | } |
| 265 | |
| 266 | caddr_t |
| 267 | _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) |
| 268 | { |
| 269 | const Elf_Rel *rel = (const Elf_Rel *)((const uint8_t *)obj->pltrel |
| 270 | + reloff); |
| 271 | Elf_Addr new_value; |
| 272 | int err; |
| 273 | |
| 274 | new_value = 0; /* XXX gcc */ |
| 275 | |
| 276 | _rtld_shared_enter(); |
| 277 | err = _rtld_relocate_plt_object(obj, rel, &new_value); |
| 278 | if (err) |
| 279 | _rtld_die(); |
| 280 | _rtld_shared_exit(); |
| 281 | |
| 282 | return (caddr_t)new_value; |
| 283 | } |
| 284 | |
| 285 | int |
| 286 | _rtld_relocate_plt_objects(const Obj_Entry *obj) |
| 287 | { |
| 288 | const Elf_Rel *rel; |
| 289 | int err = 0; |
| 290 | |
| 291 | for (rel = obj->pltrel; rel < obj->pltrellim; rel++) { |
| 292 | err = _rtld_relocate_plt_object(obj, rel, NULL); |
| 293 | if (err) |
| 294 | break; |
| 295 | } |
| 296 | return err; |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | * i386 specific GNU variant of __tls_get_addr using register based |
| 301 | * argument passing. |
| 302 | */ |
| 303 | #define DTV_MAX_INDEX(dtv) ((size_t)((dtv)[-1])) |
| 304 | |
| 305 | __dso_public __attribute__((__regparm__(1))) void * |
| 306 | ___tls_get_addr(void *arg_) |
| 307 | { |
| 308 | size_t *arg = (size_t *)arg_; |
| 309 | void **dtv; |
| 310 | struct tls_tcb *tcb = __lwp_getprivate_fast(); |
| 311 | size_t idx = arg[0], offset = arg[1]; |
| 312 | |
| 313 | dtv = tcb->tcb_dtv; |
| 314 | |
| 315 | if (__predict_true(idx < DTV_MAX_INDEX(dtv) && dtv[idx] != NULL)) |
| 316 | return (uint8_t *)dtv[idx] + offset; |
| 317 | |
| 318 | return _rtld_tls_get_addr(tcb, idx, offset); |
| 319 | } |
| 320 | |