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