1 /* $OpenBSD: elf.c,v 1.2 2017/08/11 14:58:56 jasper 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/param.h> 20 #include <sys/exec_elf.h> 21 22 #include <machine/reloc.h> 23 24 #include <assert.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 *, const char *, size_t, ssize_t, 30 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, const char *shstab, size_t shstabsz, 107 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 (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) { 126 if (symtab != NULL) 127 *symtab = (Elf_Sym *)(p + sh->sh_offset); 128 if (nsymb != NULL) 129 *nsymb = (sh->sh_size / sh->sh_entsize); 130 131 return i; 132 } 133 } 134 135 return -1; 136 } 137 138 ssize_t 139 elf_getsection(char *p, const char *sname, const char *shstab, 140 size_t shstabsz, const char **psdata, size_t *pssz) 141 { 142 Elf_Ehdr *eh = (Elf_Ehdr *)p; 143 Elf_Shdr *sh; 144 char *sdata = NULL; 145 size_t snlen, ssz = 0; 146 ssize_t sidx, i; 147 148 snlen = strlen(sname); 149 if (snlen == 0) 150 return -1; 151 152 /* Find the given section. */ 153 for (i = 0; i < eh->e_shnum; i++) { 154 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize); 155 156 if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 157 continue; 158 159 if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) { 160 sidx = i; 161 sdata = p + sh->sh_offset; 162 ssz = sh->sh_size; 163 elf_reloc_apply(p, shstab, shstabsz, sidx, sdata, ssz); 164 break; 165 } 166 } 167 168 if (sdata == NULL) 169 return -1; 170 171 if (psdata != NULL) 172 *psdata = sdata; 173 if (pssz != NULL) 174 *pssz = ssz; 175 176 return sidx; 177 } 178 179 static int 180 elf_reloc_size(unsigned long type) 181 { 182 switch (type) { 183 #ifdef R_X86_64_64 184 case R_X86_64_64: 185 return sizeof(uint64_t); 186 #endif 187 #ifdef R_X86_64_32 188 case R_X86_64_32: 189 return sizeof(uint32_t); 190 #endif 191 #ifdef RELOC_32 192 case RELOC_32: 193 return sizeof(uint32_t); 194 #endif 195 default: 196 break; 197 } 198 199 return -1; 200 } 201 202 #define ELF_WRITE_RELOC(buf, val, rsize) \ 203 do { \ 204 if (rsize == 4) { \ 205 uint32_t v32 = val; \ 206 memcpy(buf, &v32, sizeof(v32)); \ 207 } else { \ 208 uint64_t v64 = val; \ 209 memcpy(buf, &v64, sizeof(v64)); \ 210 } \ 211 } while (0) 212 213 static void 214 elf_reloc_apply(const char *p, const char *shstab, size_t shstabsz, 215 ssize_t sidx, char *sdata, size_t ssz) 216 { 217 Elf_Ehdr *eh = (Elf_Ehdr *)p; 218 Elf_Shdr *sh; 219 Elf_Rel *rel = NULL; 220 Elf_RelA *rela = NULL; 221 const Elf_Sym *symtab, *sym; 222 ssize_t symtabidx; 223 size_t nsymb, rsym, rtyp, roff; 224 size_t i, j; 225 uint64_t value; 226 int rsize; 227 228 /* Find symbol table location and number of symbols. */ 229 symtabidx = elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb); 230 if (symtabidx == -1) { 231 warnx("symbol table not found"); 232 return; 233 } 234 235 /* Apply possible relocation. */ 236 for (i = 0; i < eh->e_shnum; i++) { 237 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize); 238 239 if (sh->sh_size == 0) 240 continue; 241 242 if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx)) 243 continue; 244 245 switch (sh->sh_type) { 246 case SHT_RELA: 247 rela = (Elf_RelA *)(p + sh->sh_offset); 248 for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) { 249 rsym = ELF_R_SYM(rela[j].r_info); 250 rtyp = ELF_R_TYPE(rela[j].r_info); 251 roff = rela[j].r_offset; 252 if (rsym >= nsymb) 253 continue; 254 sym = &symtab[rsym]; 255 value = sym->st_value + rela[j].r_addend; 256 257 rsize = elf_reloc_size(rtyp); 258 if (rsize == -1 || roff + rsize >= ssz) 259 continue; 260 261 ELF_WRITE_RELOC(sdata + roff, value, rsize); 262 } 263 break; 264 case SHT_REL: 265 rel = (Elf_Rel *)(p + sh->sh_offset); 266 for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) { 267 rsym = ELF_R_SYM(rel[j].r_info); 268 rtyp = ELF_R_TYPE(rel[j].r_info); 269 roff = rel[j].r_offset; 270 if (rsym >= nsymb) 271 continue; 272 sym = &symtab[rsym]; 273 value = sym->st_value; 274 275 rsize = elf_reloc_size(rtyp); 276 if (rsize == -1 || roff + rsize >= ssz) 277 continue; 278 279 ELF_WRITE_RELOC(sdata + roff, value, rsize); 280 } 281 break; 282 default: 283 continue; 284 } 285 } 286 } 287