static char* _elf_item(Elf *elf, Elf_Type type, size_t n, size_t off, int *flag) { Elf_Data src, dst; *flag = 0; elf_assert(n); elf_assert(valid_type(type)); if (off > elf->e_size) { seterr(ERROR_OUTSIDE); return NULL; } src.d_type = type; src.d_version = elf->e_version; src.d_size = n * _fsize(elf->e_class, src.d_version, type); elf_assert(src.d_size); if ((elf->e_size - off) < src.d_size) { seterr(truncerr(type)); return NULL; } dst.d_version = _elf_version; dst.d_size = n * _msize(elf->e_class, dst.d_version, type); elf_assert(dst.d_size); elf_assert(elf->e_data); if (elf->e_rawdata != elf->e_data && dst.d_size <= src.d_size) { dst.d_buf = elf->e_data + off; } else if (!(dst.d_buf = malloc(dst.d_size))) { seterr(memerr(type)); return NULL; } else { *flag = 1; } if (elf->e_rawdata) { src.d_buf = elf->e_rawdata + off; } else { src.d_buf = elf->e_data + off; } if (_elf_xlatetom(elf, &dst, &src)) { if (!*flag) { elf->e_cooked = 1; } return (char*)dst.d_buf; } if (*flag) { free(dst.d_buf); *flag = 0; } return NULL; }
static size_t scn_entsize(const Elf *elf, unsigned version, unsigned stype) { Elf_Type type; switch ((type = _elf_scn_type(stype))) { case ELF_T_BYTE: return 0; case ELF_T_VDEF: case ELF_T_VNEED: return 0; /* What else can I do? Thank you, Sun! */ default: return _fsize(elf->e_class, version, type); } }
static size_t _elf_fsize(unsigned cls, Elf_Type type, unsigned ver) { size_t n = 0; if (!valid_version(ver)) { seterr(ERROR_UNKNOWN_VERSION); } else if (!valid_type(type)) { seterr(ERROR_UNKNOWN_TYPE); } else if (!(n = _fsize(cls, ver, type))) { seterr(ERROR_UNKNOWN_TYPE); } return n; }
static char* _elf_item(void *buf, Elf *elf, Elf_Type type, size_t off) { Elf_Data src, dst; elf_assert(valid_type(type)); if (off < 0 || off > elf->e_size) { seterr(ERROR_OUTSIDE); return NULL; } src.d_type = type; src.d_version = elf->e_version; src.d_size = _fsize(elf->e_class, src.d_version, type); elf_assert(src.d_size); if ((elf->e_size - off) < src.d_size) { seterr(truncerr(type)); return NULL; } dst.d_version = _elf_version; dst.d_size = _msize(elf->e_class, dst.d_version, type); elf_assert(dst.d_size); if (!(dst.d_buf = buf) && !(dst.d_buf = malloc(dst.d_size))) { seterr(memerr(type)); return NULL; } elf_assert(elf->e_data); if (elf->e_rawdata) { src.d_buf = elf->e_rawdata + off; } else { src.d_buf = elf->e_data + off; } if (_elf_xlatetom(elf, &dst, &src)) { return (char*)dst.d_buf; } if (dst.d_buf != buf) { free(dst.d_buf); } return NULL; }
static int _elf_cook_phdr(Elf *elf) { size_t num, off, entsz; if (elf->e_class == ELFCLASS32) { num = ((Elf32_Ehdr*)elf->e_ehdr)->e_phnum; off = ((Elf32_Ehdr*)elf->e_ehdr)->e_phoff; entsz = ((Elf32_Ehdr*)elf->e_ehdr)->e_phentsize; } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { num = ((Elf64_Ehdr*)elf->e_ehdr)->e_phnum; off = ((Elf64_Ehdr*)elf->e_ehdr)->e_phoff; entsz = ((Elf64_Ehdr*)elf->e_ehdr)->e_phentsize; /* * Check for overflow on 32-bit systems */ if (overflow(off, ((Elf64_Ehdr*)elf->e_ehdr)->e_phoff, Elf64_Off)) { seterr(ERROR_OUTSIDE); return 0; } } #endif /* __LIBELF64 */ else { seterr(ERROR_UNIMPLEMENTED); return 0; } if (off) { Elf_Scn *scn; size_t size; unsigned i; char *p; if (num == PN_XNUM) { /* * Overflow in ehdr->e_phnum. * Get real value from first SHDR. */ if (!(scn = elf->e_scn_1)) { seterr(ERROR_NOSUCHSCN); return 0; } if (elf->e_class == ELFCLASS32) { num = scn->s_shdr32.sh_info; } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { num = scn->s_shdr64.sh_info; } #endif /* __LIBELF64 */ /* we already had this else { seterr(ERROR_UNIMPLEMENTED); return 0; } */ } size = _fsize(elf->e_class, elf->e_version, ELF_T_PHDR); elf_assert(size); #if ENABLE_EXTENDED_FORMAT if (entsz < size) { #else /* ENABLE_EXTENDED_FORMAT */ if (entsz != size) { #endif /* ENABLE_EXTENDED_FORMAT */ seterr(ERROR_EHDR_PHENTSIZE); return 0; } size = _msize(elf->e_class, _elf_version, ELF_T_PHDR); elf_assert(size); if (!(p = malloc(num * size))) { seterr(memerr(ELF_T_PHDR)); return 0; } for (i = 0; i < num; i++) { if (!_elf_item(p + i * size, elf, ELF_T_PHDR, off + i * entsz)) { free(p); return 0; } } elf->e_phdr = p; elf->e_phnum = num; } return 1; } static int _elf_cook_shdr(Elf *elf) { size_t num, off, entsz; if (elf->e_class == ELFCLASS32) { num = ((Elf32_Ehdr*)elf->e_ehdr)->e_shnum; off = ((Elf32_Ehdr*)elf->e_ehdr)->e_shoff; entsz = ((Elf32_Ehdr*)elf->e_ehdr)->e_shentsize; } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { num = ((Elf64_Ehdr*)elf->e_ehdr)->e_shnum; off = ((Elf64_Ehdr*)elf->e_ehdr)->e_shoff; entsz = ((Elf64_Ehdr*)elf->e_ehdr)->e_shentsize; /* * Check for overflow on 32-bit systems */ if (overflow(off, ((Elf64_Ehdr*)elf->e_ehdr)->e_shoff, Elf64_Off)) { seterr(ERROR_OUTSIDE); return 0; } } #endif /* __LIBELF64 */ else { seterr(ERROR_UNIMPLEMENTED); return 0; } if (off) { struct tmp { Elf_Scn scn; Scn_Data data; } *head; Elf_Data src, dst; Elf_Scn *scn; Scn_Data *sd; unsigned i; if (off < 0 || off > elf->e_size) { seterr(ERROR_OUTSIDE); return 0; } src.d_type = ELF_T_SHDR; src.d_version = elf->e_version; src.d_size = _fsize(elf->e_class, src.d_version, ELF_T_SHDR); elf_assert(src.d_size); #if ENABLE_EXTENDED_FORMAT if (entsz < src.d_size) { #else /* ENABLE_EXTENDED_FORMAT */ if (entsz != src.d_size) { #endif /* ENABLE_EXTENDED_FORMAT */ seterr(ERROR_EHDR_SHENTSIZE); return 0; } dst.d_version = EV_CURRENT; if (num == 0) { union { Elf32_Shdr sh32; #if __LIBELF64 Elf64_Shdr sh64; #endif /* __LIBELF64 */ } u; /* * Overflow in ehdr->e_shnum. * Get real value from first SHDR. */ if (elf->e_size - off < entsz) { seterr(ERROR_TRUNC_SHDR); return 0; } if (elf->e_rawdata) { src.d_buf = elf->e_rawdata + off; } else { src.d_buf = elf->e_data + off; } dst.d_buf = &u; dst.d_size = sizeof(u); if (!_elf_xlatetom(elf, &dst, &src)) { return 0; } elf_assert(dst.d_size == _msize(elf->e_class, EV_CURRENT, ELF_T_SHDR)); elf_assert(dst.d_type == ELF_T_SHDR); if (elf->e_class == ELFCLASS32) { num = u.sh32.sh_size; } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { num = u.sh64.sh_size; /* * Check for overflow on 32-bit systems */ if (overflow(num, u.sh64.sh_size, Elf64_Xword)) { seterr(ERROR_OUTSIDE); return 0; } } #endif /* __LIBELF64 */ } if ((elf->e_size - off) / entsz < num) { seterr(ERROR_TRUNC_SHDR); return 0; } if (!(head = (struct tmp*)malloc(num * sizeof(struct tmp)))) { seterr(ERROR_MEM_SCN); return 0; } for (scn = NULL, i = num; i-- > 0; ) { head[i].scn = _elf_scn_init; head[i].data = _elf_data_init; head[i].scn.s_link = scn; if (!scn) { elf->e_scn_n = &head[i].scn; } scn = &head[i].scn; sd = &head[i].data; if (elf->e_rawdata) { src.d_buf = elf->e_rawdata + off + i * entsz; } else { src.d_buf = elf->e_data + off + i * entsz; } dst.d_buf = &scn->s_uhdr; dst.d_size = sizeof(scn->s_uhdr); if (!_elf_xlatetom(elf, &dst, &src)) { elf->e_scn_n = NULL; free(head); return 0; } elf_assert(dst.d_size == _msize(elf->e_class, EV_CURRENT, ELF_T_SHDR)); elf_assert(dst.d_type == ELF_T_SHDR); scn->s_elf = elf; scn->s_index = i; scn->s_data_1 = sd; scn->s_data_n = sd; sd->sd_scn = scn; if (elf->e_class == ELFCLASS32) { Elf32_Shdr *shdr = &scn->s_shdr32; scn->s_type = shdr->sh_type; scn->s_size = shdr->sh_size; scn->s_offset = shdr->sh_offset; sd->sd_data.d_align = shdr->sh_addralign; sd->sd_data.d_type = _elf_scn_type(scn->s_type); } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { Elf64_Shdr *shdr = &scn->s_shdr64; scn->s_type = shdr->sh_type; scn->s_size = shdr->sh_size; scn->s_offset = shdr->sh_offset; sd->sd_data.d_align = shdr->sh_addralign; /* * Check for overflow on 32-bit systems */ if (overflow(scn->s_size, shdr->sh_size, Elf64_Xword) || overflow(scn->s_offset, shdr->sh_offset, Elf64_Off) || overflow(sd->sd_data.d_align, shdr->sh_addralign, Elf64_Xword)) { seterr(ERROR_OUTSIDE); return 0; } sd->sd_data.d_type = _elf_scn_type(scn->s_type); /* * QUIRKS MODE: * * Some 64-bit architectures use 64-bit entries in the * .hash section. This violates the ELF standard, and * should be fixed. It's mostly harmless as long as the * binary and the machine running your program have the * same byte order, but you're in trouble if they don't, * and if the entry size is wrong. * * As a workaround, I let libelf guess the right size * for the binary. This relies pretty much on the fact * that the binary provides correct data in the section * headers. If it doesn't, it's probably broken anyway. * Therefore, libelf uses a standard conforming value * when it's not absolutely sure. */ if (scn->s_type == SHT_HASH) { int override = 0; /* * sh_entsize must reflect the entry size */ if (shdr->sh_entsize == ELF64_FSZ_ADDR) { override++; } /* * sh_size must be a multiple of sh_entsize */ if (shdr->sh_size % ELF64_FSZ_ADDR == 0) { override++; } /* * There must be room for at least 2 entries */ if (shdr->sh_size >= 2 * ELF64_FSZ_ADDR) { override++; } /* * sh_addralign must be correctly set */ if (shdr->sh_addralign == ELF64_FSZ_ADDR) { override++; } /* * The section must be properly aligned */ if (shdr->sh_offset % ELF64_FSZ_ADDR == 0) { override++; } /* XXX: also look at the data? */ /* * Make a conservative decision... */ if (override >= 5) { sd->sd_data.d_type = ELF_T_ADDR; } } /* * END QUIRKS MODE. */ } #endif /* __LIBELF64 */ /* we already had this else { seterr(ERROR_UNIMPLEMENTED); return 0; } */ sd->sd_data.d_size = scn->s_size; sd->sd_data.d_version = _elf_version; } elf_assert(scn == &head[0].scn); elf->e_scn_1 = &head[0].scn; head[0].scn.s_freeme = 1; }
static off_t _elf64_layout(Elf *elf, unsigned *flag) { int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0; Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf->e_ehdr; size_t off = 0; unsigned version; unsigned encoding; size_t align_addr; size_t entsize; unsigned shnum; Elf_Scn *scn; *flag = elf->e_elf_flags | elf->e_phdr_flags; if ((version = ehdr->e_version) == EV_NONE) { version = EV_CURRENT; } if (!valid_version(version)) { seterr(ERROR_UNKNOWN_VERSION); return -1; } if ((encoding = ehdr->e_ident[EI_DATA]) == ELFDATANONE) { encoding = native_encoding; } if (!valid_encoding(encoding)) { seterr(ERROR_UNKNOWN_ENCODING); return -1; } entsize = _fsize(ELFCLASS64, version, ELF_T_EHDR); elf_assert(entsize); rewrite(ehdr->e_ehsize, entsize, elf->e_ehdr_flags); off = entsize; align_addr = _fsize(ELFCLASS64, version, ELF_T_ADDR); elf_assert(align_addr); if (elf->e_phnum) { entsize = _fsize(ELFCLASS64, version, ELF_T_PHDR); elf_assert(entsize); if (layout) { align(off, align_addr); rewrite(ehdr->e_phoff, off, elf->e_ehdr_flags); off += elf->e_phnum * entsize; } else { off = max(off, ehdr->e_phoff + elf->e_phnum * entsize); } } else { entsize = 0; if (layout) { rewrite(ehdr->e_phoff, 0, elf->e_ehdr_flags); } } rewrite(ehdr->e_phnum, elf->e_phnum, elf->e_ehdr_flags); rewrite(ehdr->e_phentsize, entsize, elf->e_ehdr_flags); for (scn = elf->e_scn_1, shnum = 0; scn; scn = scn->s_link, ++shnum) { Elf64_Shdr *shdr = &scn->s_shdr64; size_t scn_align = 1; off_t len; elf_assert(scn->s_index == shnum); *flag |= scn->s_scn_flags; if (scn->s_index == SHN_UNDEF) { rewrite(shdr->sh_entsize, 0, scn->s_shdr_flags); if (layout) { rewrite(shdr->sh_offset, 0, scn->s_shdr_flags); rewrite(shdr->sh_size, 0, scn->s_shdr_flags); rewrite(shdr->sh_addralign, 0, scn->s_shdr_flags); } *flag |= scn->s_shdr_flags; continue; } if (shdr->sh_type == SHT_NULL) { *flag |= scn->s_shdr_flags; continue; } len = scn_data_layout(scn, version, shdr->sh_type, &scn_align, flag); if (len == -1) { return -1; } /* * Never override the program's choice. */ if (shdr->sh_entsize == 0) { entsize = scn_entsize(elf, version, shdr->sh_type); if (entsize > 1) { rewrite(shdr->sh_entsize, entsize, scn->s_shdr_flags); } } if (layout) { align(off, scn_align); rewrite(shdr->sh_offset, off, scn->s_shdr_flags); rewrite(shdr->sh_size, (size_t)len, scn->s_shdr_flags); rewrite(shdr->sh_addralign, scn_align, scn->s_shdr_flags); if (shdr->sh_type != SHT_NOBITS) { off += (size_t)len; } } else if ((size_t)len > shdr->sh_size) { seterr(ERROR_SCN2SMALL); return -1; } else { Elf_Scn *scn2; size_t end1, end2; end1 = shdr->sh_offset; if (shdr->sh_type != SHT_NOBITS) { end1 += shdr->sh_size; } if (shdr->sh_offset < off) { /* * check for overlapping sections */ for (scn2 = elf->e_scn_1; scn2; scn2 = scn2->s_link) { if (scn2 == scn) { break; } end2 = scn2->s_shdr64.sh_offset; if (scn2->s_shdr64.sh_type != SHT_NOBITS) { end2 += scn2->s_shdr64.sh_size; } if (end1 > scn2->s_shdr64.sh_offset && end2 > shdr->sh_offset) { seterr(ERROR_SCN_OVERLAP); return -1; } } } if (off < end1) { off = end1; } } *flag |= scn->s_shdr_flags; } if (shnum) { entsize = _fsize(ELFCLASS64, version, ELF_T_SHDR); elf_assert(entsize); if (layout) { align(off, align_addr); rewrite(ehdr->e_shoff, off, elf->e_ehdr_flags); off += shnum * entsize; } else { off = max(off, ehdr->e_shoff + shnum * entsize); } } else { entsize = 0; if (layout) { rewrite(ehdr->e_shoff, 0, elf->e_ehdr_flags); } } if (shnum >= SHN_LORESERVE) { Elf_Scn *scn = elf->e_scn_1; Elf64_Shdr *shdr = &scn->s_shdr64; elf_assert(scn->s_index == 0); rewrite(shdr->sh_size, shnum, scn->s_shdr_flags); *flag |= scn->s_shdr_flags; shnum = 0; } rewrite(ehdr->e_shnum, shnum, elf->e_ehdr_flags); rewrite(ehdr->e_shentsize, entsize, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG0], ELFMAG0, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG1], ELFMAG1, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG2], ELFMAG2, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG3], ELFMAG3, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_CLASS], ELFCLASS64, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_DATA], encoding, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_VERSION], version, elf->e_ehdr_flags); rewrite(ehdr->e_version, version, elf->e_ehdr_flags); *flag |= elf->e_ehdr_flags; return off; }