1 /* $OpenBSD: elf.c,v 1.10 2022/08/14 14:54:13 millert 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 < 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", 59 (unsigned long long)eh->e_shoff); 60 return 0; 61 } 62 if (eh->e_shentsize < sizeof(Elf_Shdr)) { 63 warnx("bogus section header size %u", eh->e_shentsize); 64 return 0; 65 } 66 if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) { 67 warnx("bogus section header count %u", eh->e_shnum); 68 return 0; 69 } 70 if (eh->e_shstrndx >= eh->e_shnum) { 71 warnx("bogus string table index %u", eh->e_shstrndx); 72 return 0; 73 } 74 75 return 1; 76 } 77 78 int 79 elf_getshstab(const char *p, size_t filesize, const char **shstab, 80 size_t *shstabsize) 81 { 82 Elf_Ehdr *eh = (Elf_Ehdr *)p; 83 Elf_Shdr *sh; 84 size_t shoff; 85 86 shoff = eh->e_shoff + eh->e_shstrndx * eh->e_shentsize; 87 if (shoff > (filesize - sizeof(*sh))) { 88 warnx("unexpected string table size"); 89 return -1; 90 } 91 92 sh = (Elf_Shdr *)(p + shoff); 93 if (sh->sh_type != SHT_STRTAB) { 94 warnx("unexpected string table type"); 95 return -1; 96 } 97 if (sh->sh_offset > filesize) { 98 warnx("bogus string table offset"); 99 return -1; 100 } 101 if (sh->sh_size > filesize - sh->sh_offset) { 102 warnx("bogus string table size"); 103 return -1; 104 } 105 if (shstab != NULL) 106 *shstab = p + sh->sh_offset; 107 if (shstabsize != NULL) 108 *shstabsize = sh->sh_size; 109 110 return 0; 111 } 112 113 ssize_t 114 elf_getsymtab(const char *p, size_t filesize, const char *shstab, 115 size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb, const char **strtab, 116 size_t *strtabsz) 117 { 118 Elf_Ehdr *eh = (Elf_Ehdr *)p; 119 Elf_Shdr *sh, *symsh; 120 size_t snlen, shoff; 121 ssize_t i; 122 123 snlen = strlen(ELF_SYMTAB); 124 symsh = NULL; 125 126 for (i = 0; i < eh->e_shnum; i++) { 127 shoff = eh->e_shoff + i * eh->e_shentsize; 128 if (shoff > (filesize - sizeof(*sh))) 129 continue; 130 131 sh = (Elf_Shdr *)(p + shoff); 132 if (sh->sh_type != SHT_SYMTAB) 133 continue; 134 135 if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 136 continue; 137 138 if (sh->sh_offset > filesize) 139 continue; 140 141 if (sh->sh_size > (filesize - sh->sh_offset)) 142 continue; 143 144 if (sh->sh_entsize == 0) 145 continue; 146 147 if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) { 148 if (symtab != NULL) 149 *symtab = (Elf_Sym *)(p + sh->sh_offset); 150 if (nsymb != NULL) 151 *nsymb = (sh->sh_size / sh->sh_entsize); 152 symsh = sh; 153 154 break; 155 } 156 } 157 158 if (symsh == NULL || (symsh->sh_link >= eh->e_shnum)) 159 return -1; 160 161 shoff = eh->e_shoff + symsh->sh_link * eh->e_shentsize; 162 if (shoff > (filesize - sizeof(*sh))) 163 return -1; 164 165 sh = (Elf_Shdr *)(p + shoff); 166 if ((sh->sh_offset + sh->sh_size) > filesize) 167 return -1; 168 169 if (strtab != NULL) 170 *strtab = p + sh->sh_offset; 171 if (strtabsz != NULL) 172 *strtabsz = sh->sh_size; 173 174 return i; 175 } 176 177 ssize_t 178 elf_getsection(char *p, size_t filesize, const char *sname, const char *shstab, 179 size_t shstabsz, const char **psdata, size_t *pssz) 180 { 181 Elf_Ehdr *eh = (Elf_Ehdr *)p; 182 Elf_Shdr *sh; 183 char *sdata = NULL; 184 size_t snlen, shoff, ssz = 0; 185 ssize_t sidx, i; 186 187 snlen = strlen(sname); 188 if (snlen == 0) 189 return -1; 190 191 /* Find the given section. */ 192 for (i = 0; i < eh->e_shnum; i++) { 193 shoff = eh->e_shoff + i * eh->e_shentsize; 194 if (shoff > (filesize - sizeof(*sh))) 195 continue; 196 197 sh = (Elf_Shdr *)(p + shoff); 198 if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 199 continue; 200 201 if (sh->sh_offset > filesize) 202 continue; 203 204 if (sh->sh_size > (filesize - sh->sh_offset)) 205 continue; 206 207 if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) { 208 sidx = i; 209 sdata = p + sh->sh_offset; 210 ssz = sh->sh_size; 211 elf_reloc_apply(p, filesize, shstab, shstabsz, sidx, 212 sdata, ssz); 213 break; 214 } 215 } 216 217 if (sdata == NULL) 218 return -1; 219 220 if (psdata != NULL) 221 *psdata = sdata; 222 if (pssz != NULL) 223 *pssz = ssz; 224 225 return sidx; 226 } 227 228 static int 229 elf_reloc_size(unsigned long type) 230 { 231 switch (type) { 232 #ifdef R_X86_64_64 233 case R_X86_64_64: 234 return sizeof(uint64_t); 235 #endif 236 #ifdef R_X86_64_32 237 case R_X86_64_32: 238 return sizeof(uint32_t); 239 #endif 240 #ifdef RELOC_32 241 case RELOC_32: 242 return sizeof(uint32_t); 243 #endif 244 default: 245 break; 246 } 247 248 return -1; 249 } 250 251 #define ELF_WRITE_RELOC(buf, val, rsize) \ 252 do { \ 253 if (rsize == 4) { \ 254 uint32_t v32 = val; \ 255 memcpy(buf, &v32, sizeof(v32)); \ 256 } else { \ 257 uint64_t v64 = val; \ 258 memcpy(buf, &v64, sizeof(v64)); \ 259 } \ 260 } while (0) 261 262 static void 263 elf_reloc_apply(const char *p, size_t filesize, const char *shstab, 264 size_t shstabsz, ssize_t sidx, char *sdata, size_t ssz) 265 { 266 Elf_Ehdr *eh = (Elf_Ehdr *)p; 267 Elf_Shdr *sh; 268 Elf_Rel *rel = NULL; 269 Elf_RelA *rela = NULL; 270 const Elf_Sym *symtab, *sym; 271 ssize_t symtabidx; 272 size_t nsymb, rsym, rtyp, roff; 273 size_t shoff, i, j; 274 uint64_t value; 275 int rsize; 276 277 /* Find symbol table location and number of symbols. */ 278 symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, 279 &nsymb, NULL, NULL); 280 if (symtabidx == -1) { 281 warnx("symbol table not found"); 282 return; 283 } 284 285 /* Apply possible relocation. */ 286 for (i = 0; i < eh->e_shnum; i++) { 287 shoff = eh->e_shoff + i * eh->e_shentsize; 288 if (shoff > (filesize - sizeof(*sh))) 289 continue; 290 291 sh = (Elf_Shdr *)(p + shoff); 292 if (sh->sh_size == 0) 293 continue; 294 295 if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx)) 296 continue; 297 298 if (sh->sh_offset > filesize) 299 continue; 300 301 if (sh->sh_size > (filesize - sh->sh_offset)) 302 continue; 303 304 switch (sh->sh_type) { 305 case SHT_RELA: 306 rela = (Elf_RelA *)(p + sh->sh_offset); 307 for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) { 308 rsym = ELF_R_SYM(rela[j].r_info); 309 rtyp = ELF_R_TYPE(rela[j].r_info); 310 roff = rela[j].r_offset; 311 if (rsym >= nsymb) 312 continue; 313 if (roff >= filesize) 314 continue; 315 sym = &symtab[rsym]; 316 value = sym->st_value + rela[j].r_addend; 317 318 rsize = elf_reloc_size(rtyp); 319 if (rsize == -1 || roff + rsize >= ssz) 320 continue; 321 322 ELF_WRITE_RELOC(sdata + roff, value, rsize); 323 } 324 break; 325 case SHT_REL: 326 rel = (Elf_Rel *)(p + sh->sh_offset); 327 for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) { 328 rsym = ELF_R_SYM(rel[j].r_info); 329 rtyp = ELF_R_TYPE(rel[j].r_info); 330 roff = rel[j].r_offset; 331 if (rsym >= nsymb) 332 continue; 333 if (roff >= filesize) 334 continue; 335 sym = &symtab[rsym]; 336 value = sym->st_value; 337 338 rsize = elf_reloc_size(rtyp); 339 if (rsize == -1 || roff + rsize >= ssz) 340 continue; 341 342 ELF_WRITE_RELOC(sdata + roff, value, rsize); 343 } 344 break; 345 default: 346 continue; 347 } 348 } 349 } 350