1 /* $OpenBSD: rtld_machine.c,v 1.11 2008/04/09 21:45:26 kurt 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 34 #include <link.h> 35 #include "resolve.h" 36 #include "syscall.h" 37 #include "archdep.h" 38 39 int 40 _dl_md_reloc(elf_object_t *object, int rel, int relsz) 41 { 42 int i; 43 int numrel; 44 int fails = 0; 45 struct load_list *load_list; 46 Elf64_Addr loff; 47 Elf64_Addr ooff; 48 Elf64_Addr got_start, got_end; 49 Elf64_Rel *relocs; 50 const Elf64_Sym *sym, *this; 51 52 loff = object->obj_base; 53 numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel); 54 relocs = (Elf64_Rel *)(object->Dyn.info[rel]); 55 56 if (relocs == NULL) 57 return(0); 58 59 /* 60 * Change protection of all write protected segments in the 61 * object so we can do relocations in the .rodata section. 62 * After relocation restore protection. 63 */ 64 load_list = object->load_list; 65 while (load_list != NULL) { 66 if ((load_list->prot & PROT_WRITE) == 0) 67 _dl_mprotect(load_list->start, load_list->size, 68 load_list->prot|PROT_WRITE); 69 load_list = load_list->next; 70 } 71 72 /* XXX We need the got limits to know if reloc is in got. */ 73 /* XXX Relocs against the got should not include the STUB address! */ 74 this = NULL; 75 got_start = 0; 76 got_end = 0; 77 ooff = _dl_find_symbol("__got_start", &this, 78 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 79 if (this != NULL) 80 got_start = ooff + this->st_value; 81 this = NULL; 82 ooff = _dl_find_symbol("__got_end", &this, 83 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 84 if (this != NULL) 85 got_end = ooff + this->st_value; 86 87 DL_DEB(("relocating %d\n", numrel)); 88 for (i = 0; i < numrel; i++, relocs++) { 89 Elf64_Addr r_addr = relocs->r_offset + loff; 90 const char *symn; 91 int type; 92 93 if (ELF64_R_SYM(relocs->r_info) == 0xffffff) 94 continue; 95 96 ooff = 0; 97 sym = object->dyn.symtab; 98 sym += ELF64_R_SYM(relocs->r_info); 99 symn = object->dyn.strtab + sym->st_name; 100 type = ELF64_R_TYPE(relocs->r_info); 101 102 this = NULL; 103 if (ELF64_R_SYM(relocs->r_info) && 104 !(ELF64_ST_BIND(sym->st_info) == STB_LOCAL && 105 ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) { 106 ooff = _dl_find_symbol(symn, &this, 107 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, 108 sym, object, NULL); 109 110 if (this == NULL) { 111 if (ELF_ST_BIND(sym->st_info) != STB_WEAK) 112 fails++; 113 continue; 114 } 115 } 116 117 switch (ELF64_R_TYPE(relocs->r_info)) { 118 /* XXX Handle non aligned relocs. .eh_frame 119 * XXX in libstdc++ seems to have them... */ 120 u_int64_t robj; 121 122 case R_MIPS_REL32_64: 123 if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL && 124 (ELF64_ST_TYPE(sym->st_info) == STT_SECTION || 125 ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { 126 if ((long)r_addr & 7) { 127 _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); 128 robj += loff + sym->st_value; 129 _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); 130 } else { 131 *(u_int64_t *)r_addr += loff + sym->st_value; 132 } 133 } else if (this && ((long)r_addr & 7)) { 134 _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); 135 robj += this->st_value + ooff; 136 _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); 137 } else if (this) { 138 *(u_int64_t *)r_addr += this->st_value + ooff; 139 } 140 break; 141 142 case R_MIPS_NONE: 143 break; 144 145 default: 146 _dl_printf("%s: unsupported relocation '%d'\n", 147 _dl_progname, ELF64_R_TYPE(relocs->r_info)); 148 _dl_exit(1); 149 } 150 } 151 DL_DEB(("done %d fails\n", fails)); 152 load_list = object->load_list; 153 while (load_list != NULL) { 154 if ((load_list->prot & PROT_WRITE) == 0) 155 _dl_mprotect(load_list->start, load_list->size, 156 load_list->prot); 157 load_list = load_list->next; 158 } 159 return(fails); 160 } 161 162 /* 163 * Relocate the Global Offset Table (GOT). Currently we don't 164 * do lazy evaluation here because the GNU linker doesn't 165 * follow the ABI spec which says that if an external symbol 166 * is referenced by other relocations than CALL16 and 26 it 167 * should not be given a stub and have a zero value in the 168 * symbol table. By not doing so, we can't use pointers to 169 * external functions and use them in comparisons... 170 */ 171 int 172 _dl_md_reloc_got(elf_object_t *object, int lazy) 173 { 174 int i, n; 175 Elf64_Addr loff; 176 Elf64_Addr ooff; 177 Elf64_Addr *gotp; 178 const Elf64_Sym *symp; 179 const Elf64_Sym *this; 180 const char *strt; 181 182 if (object->status & STAT_GOT_DONE) 183 return (0); 184 185 lazy = 0; /* XXX Fix ld before enabling lazy */ 186 loff = object->obj_base; 187 strt = object->dyn.strtab; 188 gotp = object->dyn.pltgot; 189 n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM]; 190 191 DL_DEB(("loff: '%p'\n", loff)); 192 /* 193 * Set up pointers for run time (lazy) resolving. 194 */ 195 gotp[0] = (long)_dl_rt_resolve; 196 if (gotp[1] & 0x0000000080000000) { 197 gotp[1] = (long)object | 0x0000000080000000; 198 } 199 200 /* First do all local references. */ 201 for (i = ((gotp[1] & 0x0000000080000000) ? 2 : 1); i < n; i++) { 202 gotp[i] += loff; 203 } 204 205 gotp += n; 206 207 symp = object->dyn.symtab; 208 symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 209 n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - 210 object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; 211 212 this = NULL; 213 object->plt_size = 0; 214 object->got_size = 0; 215 ooff = _dl_find_symbol("__got_start", &this, 216 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 217 if (this != NULL) 218 object->got_start = ooff + this->st_value; 219 220 this = NULL; 221 ooff = _dl_find_symbol("__got_end", &this, 222 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 223 if (this != NULL) 224 object->got_size = ooff + this->st_value - object->got_start; 225 226 /* 227 * Then do all global references according to the ABI. 228 * Quickstart is not yet implemented. 229 */ 230 while (n--) { 231 if (symp->st_shndx == SHN_UNDEF && 232 ELF64_ST_TYPE(symp->st_info) == STT_FUNC) { 233 if (symp->st_value == 0 || !lazy) { 234 this = 0; 235 ooff = _dl_find_symbol(strt + symp->st_name, 236 &this, 237 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 238 symp, object, NULL); 239 if (this) 240 *gotp = this->st_value + ooff; 241 } else 242 *gotp = symp->st_value + ooff; 243 } else if (symp->st_shndx == SHN_COMMON || 244 symp->st_shndx == SHN_UNDEF) { 245 this = 0; 246 ooff = _dl_find_symbol(strt + symp->st_name, &this, 247 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 248 symp, object, NULL); 249 if (this) 250 *gotp = this->st_value + ooff; 251 } else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC && 252 symp->st_value != *gotp) { 253 *gotp += loff; 254 } else { /* Resolve all others immediately */ 255 this = 0; 256 ooff = _dl_find_symbol(strt + symp->st_name, &this, 257 SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, 258 symp, object, NULL); 259 if (this) 260 *gotp = this->st_value + ooff; 261 else 262 *gotp = symp->st_value + loff; 263 } 264 gotp++; 265 symp++; 266 } 267 object->status |= STAT_GOT_DONE; 268 269 DL_DEB(("got: %x, %x\n", object->got_start, object->got_size)); 270 if (object->got_size != 0) 271 _dl_mprotect((void*)object->got_start, object->got_size, 272 PROT_READ); 273 274 return (0); 275 } 276