diff options
-rw-r--r-- | include/helpers.h | 2 | ||||
-rw-r--r-- | include/libelf.h | 14 | ||||
-rw-r--r-- | lib/libelf/elf.c | 26 | ||||
-rw-r--r-- | lib/libelf/elf32.c | 69 | ||||
-rw-r--r-- | lib/libelf/elf64.c | 57 |
5 files changed, 168 insertions, 0 deletions
diff --git a/include/helpers.h b/include/helpers.h index 47b2674..112184f 100644 --- a/include/helpers.h +++ b/include/helpers.h @@ -51,5 +51,7 @@ extern unsigned long SLOF_get_vtpm_unit(void); const typeof(((type *)0)->member)* struct_ptr = (ptr); \ (type *)((char *)struct_ptr - offset_of(type, member)); }) #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define ROUNDUP(x,v) ((((x) + ((v) - 1)) / (v)) * (v)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) #endif diff --git a/include/libelf.h b/include/libelf.h index 5fbf279..48ff4d7 100644 --- a/include/libelf.h +++ b/include/libelf.h @@ -96,4 +96,18 @@ void elf_relocate64(void *file_addr, signed long offset); int elf_forth_claim(void *addr, long size); +long elf_get_file_size(const void *buffer, const long buffer_size); +long elf_get_file_size32(const void *buffer, const long buffer_size); +long elf_get_file_size64(const void *buffer, const long buffer_size); + +#ifdef __BIG_ENDIAN__ +#define elf64_to_cpu(x, ehdr) ((ehdr)->ei_data == ELFDATA2MSB ? (x) : bswap_64(x)) +#define elf32_to_cpu(x, ehdr) ((ehdr)->ei_data == ELFDATA2MSB ? (x) : bswap_32(x)) +#define elf16_to_cpu(x, ehdr) ((ehdr)->ei_data == ELFDATA2MSB ? (x) : bswap_16(x)) +#else +#define elf64_to_cpu(x, ehdr) ((ehdr)->ei_data == ELFDATA2LSB ? (x) : bswap_64(x)) +#define elf32_to_cpu(x, ehdr) ((ehdr)->ei_data == ELFDATA2LSB ? (x) : bswap_32(x)) +#define elf16_to_cpu(x, ehdr) ((ehdr)->ei_data == ELFDATA2LSB ? (x) : bswap_16(x)) +#endif + #endif /* __LIBELF_H */ diff --git a/lib/libelf/elf.c b/lib/libelf/elf.c index 5204bc3..d368454 100644 --- a/lib/libelf/elf.c +++ b/lib/libelf/elf.c @@ -196,3 +196,29 @@ elf_get_base_addr(void *file_addr) return -1; } + +/** + * Get the file size of the ELF image that has been loaded into a + * buffer larger than the size of the file + * @return The size of the ELF image or < 0 for error + */ +long elf_get_file_size(const void *buffer, const long buffer_size) +{ + const struct ehdr *ehdr = (const struct ehdr *)buffer; + + if (buffer_size < sizeof(struct ehdr)) + return -1; + + /* check if it is an ELF image at all */ + if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46) + return -1; + + switch (ehdr->ei_class) { + case 1: + return elf_get_file_size32(buffer, buffer_size); + case 2: + return elf_get_file_size64(buffer, buffer_size); + } + + return -1; +} diff --git a/lib/libelf/elf32.c b/lib/libelf/elf32.c index fea5cf4..64ea386 100644 --- a/lib/libelf/elf32.c +++ b/lib/libelf/elf32.c @@ -17,6 +17,7 @@ #include <string.h> #include <libelf.h> #include <byteorder.h> +#include <helpers.h> struct ehdr32 { uint32_t ei_ident; @@ -50,6 +51,18 @@ struct phdr32 { uint32_t p_align; }; +struct shdr32 { + uint32_t sh_name; + uint32_t sh_type; + uint32_t sh_flags; + uint32_t sh_addr; + uint32_t sh_offset; + uint32_t sh_size; + uint32_t sh_link; + uint32_t sh_info; + uint32_t sh_addralign; + uint32_t sh_entsize; +}; static struct phdr32* get_phdr32(void *file_addr) @@ -191,3 +204,59 @@ elf_byteswap_header32(void *file_addr) phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); } } + +/* + * Determine the size of an ELF image that has been loaded into + * a buffer larger than its size. We search all program headers + * and sections for the one that shows the farthest extent of the + * file. + * @return Return -1 on error, size of file otherwise. + */ +long elf_get_file_size32(const void *buffer, const long buffer_size) +{ + const struct ehdr32 *ehdr = (const struct ehdr32 *) buffer; + const uint8_t *buffer_end = buffer + buffer_size; + const struct phdr32 *phdr; + const struct shdr32 *shdr; + long elf_size = -1; + uint16_t entsize; + unsigned i; + + if (buffer_size < sizeof(struct ehdr) || + ehdr->e_ehsize != sizeof(struct ehdr32)) + return -1; + + phdr = buffer + elf32_to_cpu(ehdr->e_phoff, ehdr); + entsize = elf16_to_cpu(ehdr->e_phentsize, ehdr); + for (i = 0; i < elf16_to_cpu(ehdr->e_phnum, ehdr); i++) { + if (((uint8_t *)phdr) + entsize > buffer_end) + return -1; + + elf_size = MAX(elf32_to_cpu(phdr->p_offset, ehdr) + + elf32_to_cpu(phdr->p_filesz, ehdr), + elf_size); + + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + entsize); + } + + shdr = buffer + elf32_to_cpu(ehdr->e_shoff, ehdr); + entsize = elf16_to_cpu(ehdr->e_shentsize, ehdr); + for (i = 0; i < elf16_to_cpu(ehdr->e_shnum, ehdr); i++) { + if (((uint8_t *)shdr) + entsize > buffer_end) + return -1; + + elf_size = MAX(elf32_to_cpu(shdr->sh_offset, ehdr) + + elf32_to_cpu(shdr->sh_size, ehdr), + elf_size); + + /* step to next header */ + shdr = (struct shdr32 *)(((uint8_t *)shdr) + entsize); + } + + elf_size = ROUNDUP(elf_size, 4); + if (elf_size > buffer_size) + return -1; + + return elf_size; +} diff --git a/lib/libelf/elf64.c b/lib/libelf/elf64.c index 775cdee..0f30267 100644 --- a/lib/libelf/elf64.c +++ b/lib/libelf/elf64.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <libelf.h> #include <byteorder.h> +#include <helpers.h> struct ehdr64 { @@ -472,3 +473,59 @@ uint32_t elf_get_eflags_64(void *file_addr) return ehdr->e_flags; } + +/* + * Determine the size of an ELF image that has been loaded into + * a buffer larger than its size. We search all program headers + * and sections for the one that shows the farthest extent of the + * file. + * @return Return -1 on error, size of file otherwise. + */ +long elf_get_file_size64(const void *buffer, const long buffer_size) +{ + const struct ehdr64 *ehdr = (const struct ehdr64 *) buffer; + const uint8_t *buffer_end = buffer + buffer_size; + const struct phdr64 *phdr; + const struct shdr64 *shdr; + long elf_size = -1; + uint16_t entsize; + unsigned i; + + if (buffer_size < sizeof(struct ehdr) || + ehdr->e_ehsize != sizeof(struct ehdr64)) + return -1; + + phdr = buffer + elf64_to_cpu(ehdr->e_phoff, ehdr); + entsize = elf16_to_cpu(ehdr->e_phentsize, ehdr); + for (i = 0; i < elf16_to_cpu(ehdr->e_phnum, ehdr); i++) { + if (((uint8_t *)phdr) + entsize > buffer_end) + return -1; + + elf_size = MAX(elf64_to_cpu(phdr->p_offset, ehdr) + + elf64_to_cpu(phdr->p_filesz, ehdr), + elf_size); + + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + entsize); + } + + shdr = buffer + elf64_to_cpu(ehdr->e_shoff, ehdr); + entsize = elf16_to_cpu(ehdr->e_shentsize, ehdr); + for (i = 0; i < elf16_to_cpu(ehdr->e_shnum, ehdr); i++) { + if (((uint8_t *)shdr) + entsize > buffer_end) + return -1; + + elf_size = MAX(elf64_to_cpu(shdr->sh_offset, ehdr) + + elf64_to_cpu(shdr->sh_size, ehdr), + elf_size); + + /* step to next header */ + shdr = (struct shdr64 *)(((uint8_t *)shdr) + entsize); + } + + elf_size = ROUNDUP(elf_size, 4); + if (elf_size > buffer_size) + return -1; + + return elf_size; +} |