1 /* $OpenBSD: rtld_machine.c,v 1.34 2021/11/27 15:13:09 visa 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/mman.h> 33 #include <sys/syscall.h> 34 #include <sys/unistd.h> 35 36 #include <link.h> 37 38 #include "resolve.h" 39 #include "syscall.h" 40 #include "archdep.h" 41 42 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; 43 44 static inline void 45 _dl_reloc_ent(Elf_Addr r_addr, Elf_Addr value) 46 { 47 if ((r_addr & 7) == 0) 48 *(u_int64_t *)r_addr += value; 49 else { 50 /* 51 * XXX Handle non aligned relocs. .eh_frame 52 * XXX in libstdc++ seems to have them... 53 */ 54 u_int64_t robj; 55 56 _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); 57 robj += value; 58 _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); 59 } 60 } 61 62 int 63 _dl_md_reloc(elf_object_t *object, int rel, int relsz) 64 { 65 int i; 66 int numrel; 67 int fails = 0; 68 Elf_Addr loff; 69 Elf_Rel *relocs; 70 const Elf_Sym *sym, *this; 71 Elf_Addr prev_value = 0; 72 const Elf_Sym *prev_sym = NULL; 73 74 loff = object->obj_base; 75 numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel); 76 relocs = (Elf_Rel *)(object->Dyn.info[rel]); 77 78 if (relocs == NULL) 79 return 0; 80 81 DL_DEB(("relocating %d\n", numrel)); 82 for (i = 0; i < numrel; i++, relocs++) { 83 Elf_Addr r_addr = relocs->r_offset + loff; 84 const char *symn; 85 86 if (ELF_R_SYM(relocs->r_info) == 0xffffff) 87 continue; 88 89 sym = object->dyn.symtab; 90 sym += ELF_R_SYM(relocs->r_info); 91 symn = object->dyn.strtab + sym->st_name; 92 93 this = NULL; 94 if (ELF_R_SYM(relocs->r_info)) { 95 if (sym == prev_sym) 96 this = sym; /* XXX non-NULL */ 97 else if (!(ELF_ST_BIND(sym->st_info) == STB_LOCAL && 98 ELF_ST_TYPE (sym->st_info) == STT_NOTYPE)) { 99 struct sym_res sr; 100 101 sr = _dl_find_symbol(symn, 102 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, 103 sym, object); 104 105 if (sr.sym == NULL) { 106 if (ELF_ST_BIND(sym->st_info) != 107 STB_WEAK) 108 fails++; 109 continue; 110 } 111 prev_sym = sym; 112 prev_value = sr.obj->obj_base + 113 sr.sym->st_value; 114 this = sym; /* XXX non-NULL */ 115 } 116 } 117 118 switch (ELF_R_TYPE(relocs->r_info)) { 119 case R_MIPS_REL32_64: 120 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && 121 (ELF_ST_TYPE(sym->st_info) == STT_SECTION || 122 ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) 123 _dl_reloc_ent(r_addr, loff + sym->st_value); 124 else if (this) 125 _dl_reloc_ent(r_addr, prev_value); 126 break; 127 128 case R_MIPS_NONE: 129 break; 130 131 default: 132 _dl_die("unsupported relocation '%llu'", 133 ELF_R_TYPE(relocs->r_info)); 134 } 135 } 136 137 DL_DEB(("done %d fails\n", fails)); 138 return fails; 139 } 140 141 extern void _dl_bind_start(void); 142 143 /* 144 * Relocate the Global Offset Table (GOT). Currently we don't 145 * do lazy evaluation here because the GNU linker doesn't 146 * follow the ABI spec which says that if an external symbol 147 * is referenced by other relocations than CALL16 and 26 it 148 * should not be given a stub and have a zero value in the 149 * symbol table. By not doing so, we can't use pointers to 150 * external functions and use them in comparisons... 151 */ 152 int 153 _dl_md_reloc_got(elf_object_t *object, int lazy) 154 { 155 int i, n; 156 Elf_Addr loff; 157 Elf_Addr *gotp; 158 const Elf_Sym *symp; 159 const char *strt; 160 161 if (object->status & STAT_GOT_DONE) 162 return 0; 163 164 loff = object->obj_base; 165 strt = object->dyn.strtab; 166 gotp = object->dyn.pltgot; 167 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM]; 168 169 DL_DEB(("loff: 0x%lx\n", (unsigned long)loff)); 170 /* 171 * Set up pointers for run time (lazy) resolving. 172 */ 173 gotp[0] = (long)_dl_bind_start; 174 gotp[1] = (long)object; 175 176 /* First do all local references. */ 177 for (i = 2; i < n; i++) { 178 gotp[i] += loff; 179 } 180 181 gotp += n; 182 183 symp = object->dyn.symtab; 184 symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 185 n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - 186 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 187 188 /* 189 * Then do all global references according to the ABI. 190 * Quickstart is not yet implemented. 191 */ 192 while (n--) { 193 const char *symn = strt + symp->st_name; 194 struct sym_res sr; 195 196 if (symp->st_shndx == SHN_UNDEF && 197 ELF_ST_TYPE(symp->st_info) == STT_FUNC) { 198 if (symp->st_value == 0 || !lazy) { 199 sr = _dl_find_symbol(symn, 200 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 201 symp, object); 202 if (sr.sym) 203 *gotp = sr.sym->st_value + 204 sr.obj->obj_base; 205 } else 206 *gotp = symp->st_value + loff; 207 } else if (symp->st_shndx == SHN_COMMON || 208 symp->st_shndx == SHN_UNDEF) { 209 sr = _dl_find_symbol(symn, 210 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 211 symp, object); 212 if (sr.sym) 213 *gotp = sr.sym->st_value + sr.obj->obj_base; 214 } else if ((ELF_ST_TYPE(symp->st_info) == STT_FUNC && 215 symp->st_value != *gotp) || 216 ELF_ST_VISIBILITY(symp->st_other) == STV_PROTECTED) { 217 *gotp += loff; 218 } else { /* Resolve all others immediately */ 219 sr = _dl_find_symbol(symn, 220 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 221 symp, object); 222 if (sr.sym) 223 *gotp = sr.sym->st_value + sr.obj->obj_base; 224 else 225 *gotp = symp->st_value + loff; 226 } 227 gotp++; 228 symp++; 229 } 230 object->status |= STAT_GOT_DONE; 231 232 return 0; 233 } 234 235 Elf_Addr 236 _dl_bind(elf_object_t *object, int symidx) 237 { 238 Elf_Addr *gotp = object->dyn.pltgot; 239 struct sym_res sr; 240 const Elf_Sym *sym; 241 const char *symn; 242 int64_t cookie = pcookie; 243 struct { 244 struct __kbind param; 245 Elf_Addr newval; 246 } buf; 247 int n; 248 249 sym = object->dyn.symtab; 250 sym += symidx; 251 symn = object->dyn.strtab + sym->st_name; 252 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] - 253 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 254 255 sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 256 sym, object); 257 if (sr.sym == NULL) 258 _dl_die("lazy binding failed!"); 259 260 buf.newval = sr.obj->obj_base + sr.sym->st_value; 261 262 if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn)) 263 return buf.newval; 264 265 buf.param.kb_addr = &gotp[n + symidx]; 266 buf.param.kb_size = sizeof(Elf_Addr); 267 268 /* directly code the syscall, so that it's actually inline here */ 269 { 270 register long syscall_num __asm("v0") = SYS_kbind; 271 register void *arg1 __asm("a0") = &buf; 272 register long arg2 __asm("a1") = sizeof(buf); 273 register long arg3 __asm("a2") = cookie; 274 275 __asm volatile("syscall" : "+r" (syscall_num) 276 : "r" (arg1), "r" (arg2), "r" (arg3) 277 : "v1", "a3", "memory"); 278 } 279 280 return buf.newval; 281 } 282