/* * parse and load elf binaries * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #ifdef __XEN__ #include #endif #include "libelf-private.h" /* ------------------------------------------------------------------------ */ /* Number of section header needed in order to fit the SYMTAB and STRTAB. */ #define ELF_BSDSYM_SECTIONS 3 struct elf_sym_header { uint32_t size; struct { elf_ehdr header; elf_shdr section[ELF_BSDSYM_SECTIONS]; } elf_header; } __attribute__((packed)); elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t size) { ELF_HANDLE_DECL(elf_shdr) shdr; unsigned i, count, section, link; uint64_t offset; if ( !elf_is_elfbinary(image_input, size) ) { elf_err(elf, "ELF: not an ELF binary\n"); return -1; } elf_memset_unchecked(elf, 0, sizeof(*elf)); elf->image_base = image_input; elf->size = size; elf->ehdr = ELF_MAKE_HANDLE(elf_ehdr, (elf_ptrval)image_input); elf->class = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_CLASS]); elf->data = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_DATA]); /* Sanity check phdr. */ offset = elf_uval(elf, elf->ehdr, e_phoff) + elf_uval(elf, elf->ehdr, e_phentsize) * elf_phdr_count(elf); if ( offset > elf->size ) { elf_err(elf, "ELF: phdr overflow (off %" PRIx64 " > size %lx)\n", offset, (unsigned long)elf->size); return -1; } /* Sanity check shdr. */ offset = elf_uval(elf, elf->ehdr, e_shoff) + elf_uval(elf, elf->ehdr, e_shentsize) * elf_shdr_count(elf); if ( offset > elf->size ) { elf_err(elf, "ELF: shdr overflow (off %" PRIx64 " > size %lx)\n", offset, (unsigned long)elf->size); return -1; } /* Find section string table. */ section = elf_uval(elf, elf->ehdr, e_shstrndx); shdr = elf_shdr_by_index(elf, section); if ( ELF_HANDLE_VALID(shdr) ) elf->sec_strtab = elf_section_start(elf, shdr); /* Find symbol table and symbol string table. */ count = elf_shdr_count(elf); for ( i = 1; i < count; i++ ) { shdr = elf_shdr_by_index(elf, i); if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) /* input has an insane section header count field */ break; if ( elf_uval(elf, shdr, sh_type) != SHT_SYMTAB ) continue; link = elf_uval(elf, shdr, sh_link); if ( link == SHN_UNDEF || link >= count ) /* out-of-bounds link value. */ break; elf->sym_tab = shdr; shdr = elf_shdr_by_index(elf, link); if ( !ELF_HANDLE_VALID(shdr) ) { elf->sym_tab = ELF_INVALID_HANDLE(elf_shdr); continue; } elf->sym_strtab = elf_section_start(elf, shdr); break; } return 0; } #ifndef __XEN__ void elf_call_log_callback(struct elf_binary *elf, bool iserr, const char *fmt,...) { va_list al; if (!elf->log_callback) return; if (!(iserr || elf->verbose)) return; va_start(al,fmt); elf->log_callback(elf, elf->log_caller_data, iserr, fmt, al); va_end(al); } void elf_set_log(struct elf_binary *elf, elf_log_callback *log_callback, void *log_caller_data, bool verbose) { elf->log_callback = log_callback; elf->log_caller_data = log_caller_data; elf->verbose = verbose; } static elf_errorstatus elf_load_image(struct elf_binary *elf, elf_ptrval dst, elf_ptrval src, uint64_t filesz, uint64_t memsz) { elf_memcpy_safe(elf, dst, src, filesz); elf_memset_safe(elf, dst + filesz, 0, memsz - filesz); return 0; } #else void elf_set_verbose(struct elf_binary *elf) { elf->verbose = 1; } static elf_errorstatus elf_memcpy(struct vcpu *v, void *dst, void *src, uint64_t size) { unsigned int res; #ifdef CONFIG_X86 if ( is_hvm_vcpu(v) ) { enum hvm_translation_result rc; rc = hvm_copy_to_guest_phys((paddr_t)dst, src, size, v); return rc != HVMTRANS_okay ? -1 : 0; } #endif res = src ? raw_copy_to_guest(dst, src, size) : raw_clear_guest(dst, size); return res ? -1 : 0; } static elf_errorstatus elf_load_image(struct elf_binary *elf, elf_ptrval dst, elf_ptrval src, uint64_t filesz, uint64_t memsz) { elf_errorstatus rc; if ( filesz > ULONG_MAX || memsz > ULONG_MAX ) return -1; /* We trust the dom0 kernel image completely, so we don't care * about overruns etc. here. */ rc = elf_memcpy(elf->vcpu, ELF_UNSAFE_PTR(dst), ELF_UNSAFE_PTR(src), filesz); if ( rc != 0 ) return -1; rc = elf_memcpy(elf->vcpu, ELF_UNSAFE_PTR(dst + filesz), NULL, memsz - filesz); if ( rc != 0 ) return -1; return 0; } #endif /* Calculate the required additional kernel space for the elf image */ void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) { uint64_t sz; ELF_HANDLE_DECL(elf_shdr) shdr; if ( !ELF_HANDLE_VALID(elf->sym_tab) ) { elf_mark_broken(elf, "invalid ELF handle for symtab section"); return; } pstart = elf_round_up(elf, pstart); /* Space to store the size of the elf image */ sz = sizeof(uint32_t); /* Space for the ELF header and section headers */ sz += offsetof(struct elf_sym_header, elf_header.section) + ELF_BSDSYM_SECTIONS * (elf_64bit(elf) ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr)); sz = elf_round_up(elf, sz); /* * No need to search for the symtab section header again, it's handler * is stored in elf->sym_tab by the elf_init function. */ /* Space for the symbol and string tables. */ sz = elf_round_up(elf, sz + elf_uval(elf, elf->sym_tab, sh_size)); shdr = elf_shdr_by_index(elf, elf_uval(elf, elf->sym_tab, sh_link)); if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) /* input has an insane section header count field */ return; if ( elf_uval(elf, shdr, sh_type) != SHT_STRTAB ) /* Invalid symtab -> strtab link */ return; sz = elf_round_up(elf, sz + elf_uval(elf, shdr, sh_size)); elf->bsd_symtab_pstart = pstart; elf->bsd_symtab_pend = pstart + sz; } static void elf_load_bsdsyms(struct elf_binary *elf) { /* * Header that is placed at the end of the kernel and allows * the OS to find where the symtab and strtab have been loaded. * It mimics a valid ELF file header, although it only contains * a symtab and a strtab section. The layout in memory is the * following: * * +------------------------+ * | | * | KERNEL | * | | * +------------------------+ pend * | ALIGNMENT | * +------------------------+ bsd_symtab_pstart * | | * | size | bsd_symtab_pend - bsd_symtab_pstart * | | * +------------------------+ bsd_symtab_pstart + 4 * | | * | ELF header | * | | * +------------------------+ * | | * | ELF section header 0 | Undefined section header * | | * +------------------------+ * | | * | ELF section header 1 | SYMTAB section header * | | * +------------------------+ * | | * | ELF section header 2 | STRTAB section header * | | * +------------------------+ * | | * | SYMTAB | * | | * +------------------------+ * | | * | STRTAB | * | | * +------------------------+ bsd_symtab_pend * * NB: according to the ELF spec there's only ONE symtab per ELF * file, and accordingly we will only load the corresponding * strtab, so we only need three section headers in our fake ELF * header (first section header is always the undefined section). */ struct elf_sym_header header; ELF_HANDLE_DECL(elf_ehdr) header_handle; unsigned long shdr_size, ehdr_size, header_size; ELF_HANDLE_DECL(elf_shdr) section_handle; unsigned int link, rc, i; elf_ptrval header_base; elf_ptrval elf_header_base; elf_ptrval symtab_base; elf_ptrval strtab_base; if ( !elf->bsd_symtab_pstart ) return; #define elf_store_field_bitness(_elf, _hdr, _elm, _val) \ do { \ if ( elf_64bit(_elf) ) \ elf_store_field(_elf, _hdr, e64._elm, _val); \ else \ elf_store_field(_elf, _hdr, e32._elm, _val); \ } while ( 0 ) #define SYMTAB_INDEX 1 #define STRTAB_INDEX 2 /* Allow elf_memcpy_safe to write to header. */ elf_set_xdest(elf, &header, sizeof(header)); /* * Calculate the position of the various elements in GUEST MEMORY SPACE. * This addresses MUST only be used with elf_load_image. * * NB: strtab_base cannot be calculated at this point because we don't * know the size of the symtab yet, and the strtab will be placed after it. */ header_base = elf_get_ptr(elf, elf->bsd_symtab_pstart); elf_header_base = elf_get_ptr(elf, elf->bsd_symtab_pstart) + sizeof(uint32_t); symtab_base = elf_round_up(elf, header_base + sizeof(header)); /* * Set the size of the ELF header and the section headers, based on the * size of our local copy. */ ehdr_size = elf_64bit(elf) ? sizeof(header.elf_header.header.e64) : sizeof(header.elf_header.header.e32); shdr_size = elf_64bit(elf) ? sizeof(header.elf_header.section[0].e64) : sizeof(header.elf_header.section[0].e32); /* Fill the ELF header, copied from the original ELF header. */ header_handle = ELF_MAKE_HANDLE(elf_ehdr, ELF_REALPTR2PTRVAL(&header.elf_header.header)); elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(header_handle), ELF_HANDLE_PTRVAL(elf->ehdr), ehdr_size); /* * Set the ELF header size, section header entry size and version * (in case we are dealing with an input ELF header that has extensions). */ elf_store_field_bitness(elf, header_handle, e_ehsize, ehdr_size); elf_store_field_bitness(elf, header_handle, e_shentsize, shdr_size); elf_store_field_bitness(elf, header_handle, e_version, EV_CURRENT); /* Set the offset to the shdr array. */ elf_store_field_bitness(elf, header_handle, e_shoff, offsetof(typeof(header.elf_header), section)); /* Set the right number of section headers. */ elf_store_field_bitness(elf, header_handle, e_shnum, ELF_BSDSYM_SECTIONS); /* Clear a couple of fields we don't use. */ elf_store_field_bitness(elf, header_handle, e_phoff, 0); elf_store_field_bitness(elf, header_handle, e_phentsize, 0); elf_store_field_bitness(elf, header_handle, e_phnum, 0); elf_store_field_bitness(elf, header_handle, e_shstrndx, 0); /* * The symtab section header is going to reside in section[SYMTAB_INDEX], * while the corresponding strtab is going to be placed in * section[STRTAB_INDEX]. sh_offset is mangled so it points to the offset * where the sections are actually loaded (relative to the ELF header * location). */ section_handle = ELF_MAKE_HANDLE(elf_shdr, ELF_REALPTR2PTRVAL(&header.elf_header.section[SYMTAB_INDEX])); elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(section_handle), ELF_HANDLE_PTRVAL(elf->sym_tab), shdr_size); /* Copy the original sh_link field before mangling it. */ link = elf_uval(elf, section_handle, sh_link); /* Load symtab into guest memory. */ rc = elf_load_image(elf, symtab_base, elf_section_start(elf, section_handle), elf_uval(elf, section_handle, sh_size), elf_uval(elf, section_handle, sh_size)); if ( rc != 0 ) { elf_mark_broken(elf, "unable to load symtab into guest memory"); return; } /* Adjust the sh_offset and sh_link of the copied section header. */ elf_store_field_bitness(elf, section_handle, sh_offset, symtab_base - elf_header_base); elf_store_field_bitness(elf, section_handle, sh_link, STRTAB_INDEX); /* Calculate the guest address where strtab is loaded. */ strtab_base = elf_round_up(elf, symtab_base + elf_uval(elf, section_handle, sh_size)); /* Load strtab section header. */ section_handle = ELF_MAKE_HANDLE(elf_shdr, ELF_REALPTR2PTRVAL(&header.elf_header.section[STRTAB_INDEX])); elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(section_handle), ELF_HANDLE_PTRVAL(elf_shdr_by_index(elf, link)), shdr_size); if ( elf_uval(elf, section_handle, sh_type) != SHT_STRTAB ) { elf_mark_broken(elf, "strtab not found"); return; } /* Load strtab into guest memory. */ rc = elf_load_image(elf, strtab_base, elf_section_start(elf, section_handle), elf_uval(elf, section_handle, sh_size), elf_uval(elf, section_handle, sh_size)); if ( rc != 0 ) { elf_mark_broken(elf, "unable to load strtab into guest memory"); return; } elf_store_field_bitness(elf, section_handle, sh_offset, strtab_base - elf_header_base); /* Store the whole size (including headers and loaded sections). */ header.size = strtab_base + elf_uval(elf, section_handle, sh_size) - elf_header_base; /* Load the size plus ELF header. */ header_size = offsetof(typeof(header), elf_header.section); rc = elf_load_image(elf, header_base, ELF_REALPTR2PTRVAL(&header), header_size, header_size); if ( rc != 0 ) { elf_mark_broken(elf, "unable to load ELF headers into guest memory"); return; } /* * Load the section headers. * * NB: this _must_ be done one by one, and taking the bitness into account, * so that the guest can treat this as an array of type Elf{32/64}_Shdr. */ for ( i = 0; i < ELF_BSDSYM_SECTIONS; i++ ) { rc = elf_load_image(elf, header_base + header_size + shdr_size * i, ELF_REALPTR2PTRVAL(&header.elf_header.section[i]), shdr_size, shdr_size); if ( rc != 0 ) { elf_mark_broken(elf, "unable to load ELF section header into guest memory"); return; } } /* Remove permissions from elf_memcpy_safe. */ elf_set_xdest(elf, NULL, 0); #undef SYMTAB_INDEX #undef STRTAB_INDEX #undef elf_store_field_bitness } void elf_parse_binary(struct elf_binary *elf) { ELF_HANDLE_DECL(elf_phdr) phdr; uint64_t low = -1, high = 0, paddr, memsz; unsigned i, count; count = elf_phdr_count(elf); for ( i = 0; i < count; i++ ) { phdr = elf_phdr_by_index(elf, i); if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) ) /* input has an insane program header count field */ break; if ( !elf_phdr_is_loadable(elf, phdr) ) continue; paddr = elf_uval(elf, phdr, p_paddr); memsz = elf_uval(elf, phdr, p_memsz); elf_msg(elf, "ELF: phdr: paddr=%#" PRIx64 " memsz=%#" PRIx64 "\n", paddr, memsz); if ( low > paddr ) low = paddr; if ( high < paddr + memsz ) high = paddr + memsz; } elf->pstart = low; elf->pend = high; elf_msg(elf, "ELF: memory: %#" PRIx64 " -> %#" PRIx64 "\n", elf->pstart, elf->pend); } elf_errorstatus elf_load_binary(struct elf_binary *elf) { ELF_HANDLE_DECL(elf_phdr) phdr; uint64_t paddr, offset, filesz, memsz; unsigned i, count; elf_ptrval dest; /* * Let bizarre ELFs write the output image up to twice; this * calculation is just to ensure our copying loop is no worse than * O(domain_size). */ uint64_t remain_allow_copy = (uint64_t)elf->dest_size * 2; count = elf_phdr_count(elf); for ( i = 0; i < count; i++ ) { phdr = elf_phdr_by_index(elf, i); if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) ) /* input has an insane program header count field */ break; if ( !elf_phdr_is_loadable(elf, phdr) ) continue; paddr = elf_uval(elf, phdr, p_paddr); offset = elf_uval(elf, phdr, p_offset); filesz = elf_uval(elf, phdr, p_filesz); memsz = elf_uval(elf, phdr, p_memsz); dest = elf_get_ptr(elf, paddr); /* * We need to check that the input image doesn't have us copy * the whole image zillions of times, as that could lead to * O(n^2) time behaviour and possible DoS by a malicous ELF. */ if ( remain_allow_copy < memsz ) { elf_mark_broken(elf, "program segments total to more" " than the input image size"); break; } remain_allow_copy -= memsz; elf_msg(elf, "ELF: phdr %u at %#"ELF_PRPTRVAL" -> %#"ELF_PRPTRVAL"\n", i, dest, (elf_ptrval)(dest + filesz)); if ( elf_load_image(elf, dest, ELF_IMAGE_BASE(elf) + offset, filesz, memsz) != 0 ) return -1; } elf_load_bsdsyms(elf); return 0; } elf_ptrval elf_get_ptr(struct elf_binary *elf, unsigned long addr) { return ELF_REALPTR2PTRVAL(elf->dest_base) + addr - elf->pstart; } uint64_t elf_lookup_addr(struct elf_binary * elf, const char *symbol) { ELF_HANDLE_DECL(elf_sym) sym; uint64_t value; sym = elf_sym_by_name(elf, symbol); if ( !ELF_HANDLE_VALID(sym) ) { elf_err(elf, "%s: not found: %s\n", __func__, symbol); return -1; } value = elf_uval(elf, sym, st_value); elf_msg(elf, "%s: symbol \"%s\" at 0x%" PRIx64 "\n", __func__, symbol, value); return value; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */