1 /* $OpenBSD: rtld_machine.c,v 1.35 2022/01/08 06:49:42 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 1998-2004 Opsycon AB, Sweden. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 #define _DYN_LOADER 30 31 #include <sys/types.h> 32 #include <sys/exec_elf.h> 33 #include <sys/syscall.h> 34 #include <sys/unistd.h> 35 36 #include <machine/reloc.h> 37 38 #include "util.h" 39 #include "resolve.h" 40 41 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; 42 43 static inline void 44 _dl_reloc_ent(Elf_Addr r_addr, Elf_Addr value) 45 { 46 if ((r_addr & 7) == 0) 47 *(u_int64_t *)r_addr += value; 48 else { 49 /* 50 * XXX Handle non aligned relocs. .eh_frame 51 * XXX in libstdc++ seems to have them... 52 */ 53 u_int64_t robj; 54 55 _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); 56 robj += value; 57 _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); 58 } 59 } 60 61 int 62 _dl_md_reloc(elf_object_t *object, int rel, int relsz) 63 { 64 int i; 65 int numrel; 66 int fails = 0; 67 Elf_Addr loff; 68 Elf_Rel *relocs; 69 const Elf_Sym *sym, *this; 70 Elf_Addr prev_value = 0; 71 const Elf_Sym *prev_sym = NULL; 72 73 loff = object->obj_base; 74 numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel); 75 relocs = (Elf_Rel *)(object->Dyn.info[rel]); 76 77 if (relocs == NULL) 78 return 0; 79 80 DL_DEB(("relocating %d\n", numrel)); 81 for (i = 0; i < numrel; i++, relocs++) { 82 Elf_Addr r_addr = relocs->r_offset + loff; 83 const char *symn; 84 85 if (ELF_R_SYM(relocs->r_info) == 0xffffff) 86 continue; 87 88 sym = object->dyn.symtab; 89 sym += ELF_R_SYM(relocs->r_info); 90 symn = object->dyn.strtab + sym->st_name; 91 92 this = NULL; 93 if (ELF_R_SYM(relocs->r_info)) { 94 if (sym == prev_sym) 95 this = sym; /* XXX non-NULL */ 96 else if (!(ELF_ST_BIND(sym->st_info) == STB_LOCAL && 97 ELF_ST_TYPE (sym->st_info) == STT_NOTYPE)) { 98 struct sym_res sr; 99 100 sr = _dl_find_symbol(symn, 101 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, 102 sym, object); 103 104 if (sr.sym == NULL) { 105 if (ELF_ST_BIND(sym->st_info) != 106 STB_WEAK) 107 fails++; 108 continue; 109 } 110 prev_sym = sym; 111 prev_value = sr.obj->obj_base + 112 sr.sym->st_value; 113 this = sym; /* XXX non-NULL */ 114 } 115 } 116 117 switch (ELF_R_TYPE(relocs->r_info)) { 118 case R_MIPS_REL32_64: 119 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && 120 (ELF_ST_TYPE(sym->st_info) == STT_SECTION || 121 ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) 122 _dl_reloc_ent(r_addr, loff + sym->st_value); 123 else if (this) 124 _dl_reloc_ent(r_addr, prev_value); 125 break; 126 127 case R_MIPS_NONE: 128 break; 129 130 default: 131 _dl_die("unsupported relocation '%llu'", 132 ELF_R_TYPE(relocs->r_info)); 133 } 134 } 135 136 DL_DEB(("done %d fails\n", fails)); 137 return fails; 138 } 139 140 extern void _dl_bind_start(void); 141 142 /* 143 * Relocate the Global Offset Table (GOT). Currently we don't 144 * do lazy evaluation here because the GNU linker doesn't 145 * follow the ABI spec which says that if an external symbol 146 * is referenced by other relocations than CALL16 and 26 it 147 * should not be given a stub and have a zero value in the 148 * symbol table. By not doing so, we can't use pointers to 149 * external functions and use them in comparisons... 150 */ 151 int 152 _dl_md_reloc_got(elf_object_t *object, int lazy) 153 { 154 int i, n; 155 Elf_Addr loff; 156 Elf_Addr *gotp; 157 const Elf_Sym *symp; 158 const char *strt; 159 160 if (object->status & STAT_GOT_DONE) 161 return 0; 162 163 loff = object->obj_base; 164 strt = object->dyn.strtab; 165 gotp = object->dyn.pltgot; 166 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM]; 167 168 DL_DEB(("loff: 0x%lx\n", (unsigned long)loff)); 169 /* 170 * Set up pointers for run time (lazy) resolving. 171 */ 172 gotp[0] = (long)_dl_bind_start; 173 gotp[1] = (long)object; 174 175 /* First do all local references. */ 176 for (i = 2; i < n; i++) { 177 gotp[i] += loff; 178 } 179 180 gotp += n; 181 182 symp = object->dyn.symtab; 183 symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 184 n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - 185 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 186 187 /* 188 * Then do all global references according to the ABI. 189 * Quickstart is not yet implemented. 190 */ 191 while (n--) { 192 const char *symn = strt + symp->st_name; 193 struct sym_res sr; 194 195 if (symp->st_shndx == SHN_UNDEF && 196 ELF_ST_TYPE(symp->st_info) == STT_FUNC) { 197 if (symp->st_value == 0 || !lazy) { 198 sr = _dl_find_symbol(symn, 199 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 200 symp, object); 201 if (sr.sym) 202 *gotp = sr.sym->st_value + 203 sr.obj->obj_base; 204 } else 205 *gotp = symp->st_value + loff; 206 } else if (symp->st_shndx == SHN_COMMON || 207 symp->st_shndx == SHN_UNDEF) { 208 sr = _dl_find_symbol(symn, 209 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 210 symp, object); 211 if (sr.sym) 212 *gotp = sr.sym->st_value + sr.obj->obj_base; 213 } else if ((ELF_ST_TYPE(symp->st_info) == STT_FUNC && 214 symp->st_value != *gotp) || 215 ELF_ST_VISIBILITY(symp->st_other) == STV_PROTECTED) { 216 *gotp += loff; 217 } else { /* Resolve all others immediately */ 218 sr = _dl_find_symbol(symn, 219 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 220 symp, object); 221 if (sr.sym) 222 *gotp = sr.sym->st_value + sr.obj->obj_base; 223 else 224 *gotp = symp->st_value + loff; 225 } 226 gotp++; 227 symp++; 228 } 229 object->status |= STAT_GOT_DONE; 230 231 return 0; 232 } 233 234 Elf_Addr 235 _dl_bind(elf_object_t *object, int symidx) 236 { 237 Elf_Addr *gotp = object->dyn.pltgot; 238 struct sym_res sr; 239 const Elf_Sym *sym; 240 const char *symn; 241 int64_t cookie = pcookie; 242 struct { 243 struct __kbind param; 244 Elf_Addr newval; 245 } buf; 246 int n; 247 248 sym = object->dyn.symtab; 249 sym += symidx; 250 symn = object->dyn.strtab + sym->st_name; 251 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] - 252 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 253 254 sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 255 sym, object); 256 if (sr.sym == NULL) 257 _dl_die("lazy binding failed!"); 258 259 buf.newval = sr.obj->obj_base + sr.sym->st_value; 260 261 if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn)) 262 return buf.newval; 263 264 buf.param.kb_addr = &gotp[n + symidx]; 265 buf.param.kb_size = sizeof(Elf_Addr); 266 267 /* directly code the syscall, so that it's actually inline here */ 268 { 269 register long syscall_num __asm("v0") = SYS_kbind; 270 register void *arg1 __asm("a0") = &buf; 271 register long arg2 __asm("a1") = sizeof(buf); 272 register long arg3 __asm("a2") = cookie; 273 274 __asm volatile("syscall" : "+r" (syscall_num) 275 : "r" (arg1), "r" (arg2), "r" (arg3) 276 : "v1", "a3", "memory"); 277 } 278 279 return buf.newval; 280 } 281