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