summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Berger <stefanb@linux.ibm.com>2020-05-12 11:44:50 -0400
committerAlexey Kardashevskiy <aik@ozlabs.ru>2020-05-13 11:25:23 +1000
commit8df4a179ab5a8a2e4a185998aa0dd5d8e80b254e (patch)
tree74abb178f4199319b4e87547291b270b8014cbf3
parentd04fea6ab0431e81e43a9ce5b1ec33e0c2ea66eb (diff)
downloadqemu-SLOF-8df4a179ab5a8a2e4a185998aa0dd5d8e80b254e.tar.gz
elf: Implement elf_get_file_size to determine size of an ELF image
Implement elf_get_file_size to determine the size of an ELF image that has been loaded into a buffer much larger than the actual size of the original file. We determine the size by searching for the farthest offset declared by the ELF headers. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
-rw-r--r--include/helpers.h2
-rw-r--r--include/libelf.h14
-rw-r--r--lib/libelf/elf.c26
-rw-r--r--lib/libelf/elf32.c69
-rw-r--r--lib/libelf/elf64.c57
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;
+}