1 /* $OpenBSD: elf.c,v 1.9 2017/11/14 09:14:50 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <machine/reloc.h> 22 23 #include <assert.h> 24 #include <elf.h> 25 #include <err.h> 26 #include <string.h> 27 28 static int elf_reloc_size(unsigned long); 29 static void elf_reloc_apply(const char *, size_t, const char *, size_t, 30 ssize_t, char *, size_t); 31 32 int 33 iself(const char *p, size_t filesize) 34 { 35 Elf_Ehdr *eh = (Elf_Ehdr *)p; 36 37 if (filesize < (off_t)sizeof(Elf_Ehdr)) { 38 warnx("file too small to be ELF"); 39 return 0; 40 } 41 42 if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh)) 43 return 0; 44 45 if (eh->e_ident[EI_CLASS] != ELFCLASS) { 46 warnx("unexpected word size %u", eh->e_ident[EI_CLASS]); 47 return 0; 48 } 49 if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) { 50 warnx("unexpected version %u", eh->e_ident[EI_VERSION]); 51 return 0; 52 } 53 if (eh->e_ident[EI_DATA] >= ELFDATANUM) { 54 warnx("unexpected data format %u", eh->e_ident[EI_DATA]); 55 return 0; 56 } 57 if (eh->e_shoff > filesize) { 58 warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff); 59 return 0; 60 } 61 if (eh->e_shentsize < sizeof(Elf_Shdr)) { 62 warnx("bogus section header size %u", eh->e_shentsize); 63 return 0; 64 } 65 if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) { 66 warnx("bogus section header count %u", eh->e_shnum); 67 return 0; 68 } 69 if (eh->e_shstrndx >= eh->e_shnum) { 70 warnx("bogus string table index %u", eh->e_shstrndx); 71 return 0; 72 } 73 74 return 1; 75 } 76 77 int 78 elf_getshstab(const char *p, size_t filesize, const char **shstab, 79 size_t *shstabsize) 80 { 81 Elf_Ehdr *eh = (Elf_Ehdr *)p; 82 Elf_Shdr *sh; 83 size_t shoff; 84 85 shoff = eh->e_shoff + eh->e_shstrndx * eh->e_shentsize; 86 if (shoff > (filesize - sizeof(*sh))) { 87 warnx("unexpected string table size"); 88 return -1; 89 } 90 91 sh = (Elf_Shdr *)(p + shoff); 92 if (sh->sh_type != SHT_STRTAB) { 93 warnx("unexpected string table type"); 94 return -1; 95 } 96 if (sh->sh_offset > filesize) { 97 warnx("bogus string table offset"); 98 return -1; 99 } 100 if (sh->sh_size > filesize - sh->sh_offset) { 101 warnx("bogus string table size"); 102 return -1; 103 } 104 if (shstab != NULL) 105 *shstab = p + sh->sh_offset; 106 if (shstabsize != NULL) 107 *shstabsize = sh->sh_size; 108 109 return 0; 110 } 111 112 ssize_t 113 elf_getsymtab(const char *p, size_t filesize, const char *shstab, 114 size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb, const char **strtab, 115 size_t *strtabsz) 116 { 117 Elf_Ehdr *eh = (Elf_Ehdr *)p; 118 Elf_Shdr *sh, *symsh; 119 size_t snlen, shoff; 120 ssize_t i; 121 122 snlen = strlen(ELF_SYMTAB); 123 symsh = NULL; 124 125 for (i = 0; i < eh->e_shnum; i++) { 126 shoff = eh->e_shoff + i * eh->e_shentsize; 127 if (shoff > (filesize - sizeof(*sh))) 128 continue; 129 130 sh = (Elf_Shdr *)(p + shoff); 131 if (sh->sh_type != SHT_SYMTAB) 132 continue; 133 134 if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 135 continue; 136 137 if (sh->sh_offset > filesize) 138 continue; 139 140 if (sh->sh_size > (filesize - sh->sh_offset)) 141 continue; 142 143 if (sh->sh_entsize == 0) 144 continue; 145 146 if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) { 147 if (symtab != NULL) 148 *symtab = (Elf_Sym *)(p + sh->sh_offset); 149 if (nsymb != NULL) 150 *nsymb = (sh->sh_size / sh->sh_entsize); 151 symsh = sh; 152 153 break; 154 } 155 } 156 157 if (symsh == NULL || (symsh->sh_link >= eh->e_shnum)) 158 return -1; 159 160 shoff = eh->e_shoff + symsh->sh_link * eh->e_shentsize; 161 if (shoff > (filesize - sizeof(*sh))) 162 return -1; 163 164 sh = (Elf_Shdr *)(p + shoff); 165 if ((sh->sh_offset + sh->sh_size) > filesize) 166 return -1; 167 168 if (strtab != NULL) 169 *strtab = p + sh->sh_offset; 170 if (strtabsz != NULL) 171 *strtabsz = sh->sh_size; 172 173 return i; 174 } 175 176 ssize_t 177 elf_getsection(char *p, size_t filesize, const char *sname, const char *shstab, 178 size_t shstabsz, const char **psdata, size_t *pssz) 179 { 180 Elf_Ehdr *eh = (Elf_Ehdr *)p; 181 Elf_Shdr *sh; 182 char *sdata = NULL; 183 size_t snlen, shoff, ssz = 0; 184 ssize_t sidx, i; 185 186 snlen = strlen(sname); 187 if (snlen == 0) 188 return -1; 189 190 /* Find the given section. */ 191 for (i = 0; i < eh->e_shnum; i++) { 192 shoff = eh->e_shoff + i * eh->e_shentsize; 193 if (shoff > (filesize - sizeof(*sh))) 194 continue; 195 196 sh = (Elf_Shdr *)(p + shoff); 197 if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 198 continue; 199 200 if (sh->sh_offset > filesize) 201 continue; 202 203 if (sh->sh_size > (filesize - sh->sh_offset)) 204 continue; 205 206 if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) { 207 sidx = i; 208 sdata = p + sh->sh_offset; 209 ssz = sh->sh_size; 210 elf_reloc_apply(p, filesize, shstab, shstabsz, sidx, 211 sdata, ssz); 212 break; 213 } 214 } 215 216 if (sdata == NULL) 217 return -1; 218 219 if (psdata != NULL) 220 *psdata = sdata; 221 if (pssz != NULL) 222 *pssz = ssz; 223 224 return sidx; 225 } 226 227 static int 228 elf_reloc_size(unsigned long type) 229 { 230 switch (type) { 231 #ifdef R_X86_64_64 232 case R_X86_64_64: 233 return sizeof(uint64_t); 234 #endif 235 #ifdef R_X86_64_32 236 case R_X86_64_32: 237 return sizeof(uint32_t); 238 #endif 239 #ifdef RELOC_32 240 case RELOC_32: 241 return sizeof(uint32_t); 242 #endif 243 default: 244 break; 245 } 246 247 return -1; 248 } 249 250 #define ELF_WRITE_RELOC(buf, val, rsize) \ 251 do { \ 252 if (rsize == 4) { \ 253 uint32_t v32 = val; \ 254 memcpy(buf, &v32, sizeof(v32)); \ 255 } else { \ 256 uint64_t v64 = val; \ 257 memcpy(buf, &v64, sizeof(v64)); \ 258 } \ 259 } while (0) 260 261 static void 262 elf_reloc_apply(const char *p, size_t filesize, const char *shstab, 263 size_t shstabsz, ssize_t sidx, char *sdata, size_t ssz) 264 { 265 Elf_Ehdr *eh = (Elf_Ehdr *)p; 266 Elf_Shdr *sh; 267 Elf_Rel *rel = NULL; 268 Elf_RelA *rela = NULL; 269 const Elf_Sym *symtab, *sym; 270 ssize_t symtabidx; 271 size_t nsymb, rsym, rtyp, roff; 272 size_t shoff, i, j; 273 uint64_t value; 274 int rsize; 275 276 /* Find symbol table location and number of symbols. */ 277 symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, 278 &nsymb, NULL, NULL); 279 if (symtabidx == -1) { 280 warnx("symbol table not found"); 281 return; 282 } 283 284 /* Apply possible relocation. */ 285 for (i = 0; i < eh->e_shnum; i++) { 286 shoff = eh->e_shoff + i * eh->e_shentsize; 287 if (shoff > (filesize - sizeof(*sh))) 288 continue; 289 290 sh = (Elf_Shdr *)(p + shoff); 291 if (sh->sh_size == 0) 292 continue; 293 294 if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx)) 295 continue; 296 297 if (sh->sh_offset > filesize) 298 continue; 299 300 if (sh->sh_size > (filesize - sh->sh_offset)) 301 continue; 302 303 switch (sh->sh_type) { 304 case SHT_RELA: 305 rela = (Elf_RelA *)(p + sh->sh_offset); 306 for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) { 307 rsym = ELF_R_SYM(rela[j].r_info); 308 rtyp = ELF_R_TYPE(rela[j].r_info); 309 roff = rela[j].r_offset; 310 if (rsym >= nsymb) 311 continue; 312 if (roff >= filesize) 313 continue; 314 sym = &symtab[rsym]; 315 value = sym->st_value + rela[j].r_addend; 316 317 rsize = elf_reloc_size(rtyp); 318 if (rsize == -1 || roff + rsize >= ssz) 319 continue; 320 321 ELF_WRITE_RELOC(sdata + roff, value, rsize); 322 } 323 break; 324 case SHT_REL: 325 rel = (Elf_Rel *)(p + sh->sh_offset); 326 for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) { 327 rsym = ELF_R_SYM(rel[j].r_info); 328 rtyp = ELF_R_TYPE(rel[j].r_info); 329 roff = rel[j].r_offset; 330 if (rsym >= nsymb) 331 continue; 332 if (roff >= filesize) 333 continue; 334 sym = &symtab[rsym]; 335 value = sym->st_value; 336 337 rsize = elf_reloc_size(rtyp); 338 if (rsize == -1 || roff + rsize >= ssz) 339 continue; 340 341 ELF_WRITE_RELOC(sdata + roff, value, rsize); 342 } 343 break; 344 default: 345 continue; 346 } 347 } 348 } 349