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