1*0687c322Sjasper /* $OpenBSD: elf.c,v 1.2 2017/08/11 14:58:56 jasper Exp $ */ 2*0687c322Sjasper 3192095f7Smpi /* 4192095f7Smpi * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org> 5192095f7Smpi * 6192095f7Smpi * Permission to use, copy, modify, and distribute this software for any 7192095f7Smpi * purpose with or without fee is hereby granted, provided that the above 8192095f7Smpi * copyright notice and this permission notice appear in all copies. 9192095f7Smpi * 10192095f7Smpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11192095f7Smpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12192095f7Smpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13192095f7Smpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14192095f7Smpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15192095f7Smpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16192095f7Smpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17192095f7Smpi */ 18192095f7Smpi 19192095f7Smpi #include <sys/param.h> 20192095f7Smpi #include <sys/exec_elf.h> 21192095f7Smpi 22192095f7Smpi #include <machine/reloc.h> 23192095f7Smpi 24192095f7Smpi #include <assert.h> 25192095f7Smpi #include <err.h> 26192095f7Smpi #include <string.h> 27192095f7Smpi 28192095f7Smpi static int elf_reloc_size(unsigned long); 29192095f7Smpi static void elf_reloc_apply(const char *, const char *, size_t, ssize_t, 30192095f7Smpi char *, size_t); 31192095f7Smpi 32192095f7Smpi int 33192095f7Smpi iself(const char *p, size_t filesize) 34192095f7Smpi { 35192095f7Smpi Elf_Ehdr *eh = (Elf_Ehdr *)p; 36192095f7Smpi 37192095f7Smpi if (filesize < (off_t)sizeof(Elf_Ehdr)) { 38192095f7Smpi warnx("file too small to be ELF"); 39192095f7Smpi return 0; 40192095f7Smpi } 41192095f7Smpi 42192095f7Smpi if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh)) 43192095f7Smpi return 0; 44192095f7Smpi 45192095f7Smpi if (eh->e_ident[EI_CLASS] != ELFCLASS) { 46192095f7Smpi warnx("unexpected word size %u", eh->e_ident[EI_CLASS]); 47192095f7Smpi return 0; 48192095f7Smpi } 49192095f7Smpi if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) { 50192095f7Smpi warnx("unexpected version %u", eh->e_ident[EI_VERSION]); 51192095f7Smpi return 0; 52192095f7Smpi } 53192095f7Smpi if (eh->e_ident[EI_DATA] >= ELFDATANUM) { 54192095f7Smpi warnx("unexpected data format %u", eh->e_ident[EI_DATA]); 55192095f7Smpi return 0; 56192095f7Smpi } 57192095f7Smpi if (eh->e_shoff > filesize) { 58192095f7Smpi warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff); 59192095f7Smpi return 0; 60192095f7Smpi } 61192095f7Smpi if (eh->e_shentsize < sizeof(Elf_Shdr)) { 62192095f7Smpi warnx("bogus section header size %u", eh->e_shentsize); 63192095f7Smpi return 0; 64192095f7Smpi } 65192095f7Smpi if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) { 66192095f7Smpi warnx("bogus section header count %u", eh->e_shnum); 67192095f7Smpi return 0; 68192095f7Smpi } 69192095f7Smpi if (eh->e_shstrndx >= eh->e_shnum) { 70192095f7Smpi warnx("bogus string table index %u", eh->e_shstrndx); 71192095f7Smpi return 0; 72192095f7Smpi } 73192095f7Smpi 74192095f7Smpi return 1; 75192095f7Smpi } 76192095f7Smpi 77192095f7Smpi int 78192095f7Smpi elf_getshstab(const char *p, size_t filesize, const char **shstab, 79192095f7Smpi size_t *shstabsize) 80192095f7Smpi { 81192095f7Smpi Elf_Ehdr *eh = (Elf_Ehdr *)p; 82192095f7Smpi Elf_Shdr *sh; 83192095f7Smpi 84192095f7Smpi sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize); 85192095f7Smpi if (sh->sh_type != SHT_STRTAB) { 86192095f7Smpi warnx("unexpected string table type"); 87192095f7Smpi return -1; 88192095f7Smpi } 89192095f7Smpi if (sh->sh_offset > filesize) { 90192095f7Smpi warnx("bogus string table offset"); 91192095f7Smpi return -1; 92192095f7Smpi } 93192095f7Smpi if (sh->sh_size > filesize - sh->sh_offset) { 94192095f7Smpi warnx("bogus string table size"); 95192095f7Smpi return -1; 96192095f7Smpi } 97192095f7Smpi if (shstab != NULL) 98192095f7Smpi *shstab = p + sh->sh_offset; 99192095f7Smpi if (shstabsize != NULL) 100192095f7Smpi *shstabsize = sh->sh_size; 101192095f7Smpi 102192095f7Smpi return 0; 103192095f7Smpi } 104192095f7Smpi 105192095f7Smpi ssize_t 106192095f7Smpi elf_getsymtab(const char *p, const char *shstab, size_t shstabsz, 107192095f7Smpi const Elf_Sym **symtab, size_t *nsymb) 108192095f7Smpi { 109192095f7Smpi Elf_Ehdr *eh = (Elf_Ehdr *)p; 110192095f7Smpi Elf_Shdr *sh; 111192095f7Smpi size_t snlen; 112192095f7Smpi ssize_t i; 113192095f7Smpi 114192095f7Smpi snlen = strlen(ELF_SYMTAB); 115192095f7Smpi 116192095f7Smpi for (i = 0; i < eh->e_shnum; i++) { 117192095f7Smpi sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize); 118192095f7Smpi 119192095f7Smpi if (sh->sh_type != SHT_SYMTAB) 120192095f7Smpi continue; 121192095f7Smpi 122192095f7Smpi if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 123192095f7Smpi continue; 124192095f7Smpi 125192095f7Smpi if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) { 126192095f7Smpi if (symtab != NULL) 127192095f7Smpi *symtab = (Elf_Sym *)(p + sh->sh_offset); 128192095f7Smpi if (nsymb != NULL) 129192095f7Smpi *nsymb = (sh->sh_size / sh->sh_entsize); 130192095f7Smpi 131192095f7Smpi return i; 132192095f7Smpi } 133192095f7Smpi } 134192095f7Smpi 135192095f7Smpi return -1; 136192095f7Smpi } 137192095f7Smpi 138192095f7Smpi ssize_t 139192095f7Smpi elf_getsection(char *p, const char *sname, const char *shstab, 140192095f7Smpi size_t shstabsz, const char **psdata, size_t *pssz) 141192095f7Smpi { 142192095f7Smpi Elf_Ehdr *eh = (Elf_Ehdr *)p; 143192095f7Smpi Elf_Shdr *sh; 144192095f7Smpi char *sdata = NULL; 145192095f7Smpi size_t snlen, ssz = 0; 146192095f7Smpi ssize_t sidx, i; 147192095f7Smpi 148192095f7Smpi snlen = strlen(sname); 149192095f7Smpi if (snlen == 0) 150192095f7Smpi return -1; 151192095f7Smpi 152192095f7Smpi /* Find the given section. */ 153192095f7Smpi for (i = 0; i < eh->e_shnum; i++) { 154192095f7Smpi sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize); 155192095f7Smpi 156192095f7Smpi if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz)) 157192095f7Smpi continue; 158192095f7Smpi 159192095f7Smpi if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) { 160192095f7Smpi sidx = i; 161192095f7Smpi sdata = p + sh->sh_offset; 162192095f7Smpi ssz = sh->sh_size; 163192095f7Smpi elf_reloc_apply(p, shstab, shstabsz, sidx, sdata, ssz); 164192095f7Smpi break; 165192095f7Smpi } 166192095f7Smpi } 167192095f7Smpi 168192095f7Smpi if (sdata == NULL) 169192095f7Smpi return -1; 170192095f7Smpi 171192095f7Smpi if (psdata != NULL) 172192095f7Smpi *psdata = sdata; 173192095f7Smpi if (pssz != NULL) 174192095f7Smpi *pssz = ssz; 175192095f7Smpi 176192095f7Smpi return sidx; 177192095f7Smpi } 178192095f7Smpi 179192095f7Smpi static int 180192095f7Smpi elf_reloc_size(unsigned long type) 181192095f7Smpi { 182192095f7Smpi switch (type) { 183192095f7Smpi #ifdef R_X86_64_64 184192095f7Smpi case R_X86_64_64: 185192095f7Smpi return sizeof(uint64_t); 186192095f7Smpi #endif 187192095f7Smpi #ifdef R_X86_64_32 188192095f7Smpi case R_X86_64_32: 189192095f7Smpi return sizeof(uint32_t); 190192095f7Smpi #endif 191192095f7Smpi #ifdef RELOC_32 192192095f7Smpi case RELOC_32: 193192095f7Smpi return sizeof(uint32_t); 194192095f7Smpi #endif 195192095f7Smpi default: 196192095f7Smpi break; 197192095f7Smpi } 198192095f7Smpi 199192095f7Smpi return -1; 200192095f7Smpi } 201192095f7Smpi 202192095f7Smpi #define ELF_WRITE_RELOC(buf, val, rsize) \ 203192095f7Smpi do { \ 204192095f7Smpi if (rsize == 4) { \ 205192095f7Smpi uint32_t v32 = val; \ 206192095f7Smpi memcpy(buf, &v32, sizeof(v32)); \ 207192095f7Smpi } else { \ 208192095f7Smpi uint64_t v64 = val; \ 209192095f7Smpi memcpy(buf, &v64, sizeof(v64)); \ 210192095f7Smpi } \ 211192095f7Smpi } while (0) 212192095f7Smpi 213192095f7Smpi static void 214192095f7Smpi elf_reloc_apply(const char *p, const char *shstab, size_t shstabsz, 215192095f7Smpi ssize_t sidx, char *sdata, size_t ssz) 216192095f7Smpi { 217192095f7Smpi Elf_Ehdr *eh = (Elf_Ehdr *)p; 218192095f7Smpi Elf_Shdr *sh; 219192095f7Smpi Elf_Rel *rel = NULL; 220192095f7Smpi Elf_RelA *rela = NULL; 221192095f7Smpi const Elf_Sym *symtab, *sym; 222192095f7Smpi ssize_t symtabidx; 223192095f7Smpi size_t nsymb, rsym, rtyp, roff; 224192095f7Smpi size_t i, j; 225192095f7Smpi uint64_t value; 226192095f7Smpi int rsize; 227192095f7Smpi 228192095f7Smpi /* Find symbol table location and number of symbols. */ 229192095f7Smpi symtabidx = elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb); 230192095f7Smpi if (symtabidx == -1) { 231192095f7Smpi warnx("symbol table not found"); 232192095f7Smpi return; 233192095f7Smpi } 234192095f7Smpi 235192095f7Smpi /* Apply possible relocation. */ 236192095f7Smpi for (i = 0; i < eh->e_shnum; i++) { 237192095f7Smpi sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize); 238192095f7Smpi 239192095f7Smpi if (sh->sh_size == 0) 240192095f7Smpi continue; 241192095f7Smpi 242192095f7Smpi if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx)) 243192095f7Smpi continue; 244192095f7Smpi 245192095f7Smpi switch (sh->sh_type) { 246192095f7Smpi case SHT_RELA: 247192095f7Smpi rela = (Elf_RelA *)(p + sh->sh_offset); 248192095f7Smpi for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) { 249192095f7Smpi rsym = ELF_R_SYM(rela[j].r_info); 250192095f7Smpi rtyp = ELF_R_TYPE(rela[j].r_info); 251192095f7Smpi roff = rela[j].r_offset; 252192095f7Smpi if (rsym >= nsymb) 253192095f7Smpi continue; 254192095f7Smpi sym = &symtab[rsym]; 255192095f7Smpi value = sym->st_value + rela[j].r_addend; 256192095f7Smpi 257192095f7Smpi rsize = elf_reloc_size(rtyp); 258192095f7Smpi if (rsize == -1 || roff + rsize >= ssz) 259192095f7Smpi continue; 260192095f7Smpi 261192095f7Smpi ELF_WRITE_RELOC(sdata + roff, value, rsize); 262192095f7Smpi } 263192095f7Smpi break; 264192095f7Smpi case SHT_REL: 265192095f7Smpi rel = (Elf_Rel *)(p + sh->sh_offset); 266192095f7Smpi for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) { 267192095f7Smpi rsym = ELF_R_SYM(rel[j].r_info); 268192095f7Smpi rtyp = ELF_R_TYPE(rel[j].r_info); 269192095f7Smpi roff = rel[j].r_offset; 270192095f7Smpi if (rsym >= nsymb) 271192095f7Smpi continue; 272192095f7Smpi sym = &symtab[rsym]; 273192095f7Smpi value = sym->st_value; 274192095f7Smpi 275192095f7Smpi rsize = elf_reloc_size(rtyp); 276192095f7Smpi if (rsize == -1 || roff + rsize >= ssz) 277192095f7Smpi continue; 278192095f7Smpi 279192095f7Smpi ELF_WRITE_RELOC(sdata + roff, value, rsize); 280192095f7Smpi } 281192095f7Smpi break; 282192095f7Smpi default: 283192095f7Smpi continue; 284192095f7Smpi } 285192095f7Smpi } 286192095f7Smpi } 287