diff mbox series

[v2,1/3] elf: Implement elf_get_file_size to determine size of an ELF image

Message ID 20200401145755.891080-2-stefanb@linux.vnet.ibm.com
State Superseded
Headers show
Series vTPM: Measure the bootloader | expand

Commit Message

Stefan Berger April 1, 2020, 2:57 p.m. UTC
From: Stefan Berger <stefanb@linux.ibm.com>

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>
---
 include/helpers.h  |  2 ++
 include/libelf.h   | 14 ++++++++++
 lib/libelf/elf.c   | 26 ++++++++++++++++++
 lib/libelf/elf32.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/libelf/elf64.c | 56 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 166 insertions(+)

Comments

Alexey Kardashevskiy May 8, 2020, 1:07 a.m. UTC | #1
On 02/04/2020 01:57, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> 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>
> ---
>  include/helpers.h  |  2 ++
>  include/libelf.h   | 14 ++++++++++
>  lib/libelf/elf.c   | 26 ++++++++++++++++++
>  lib/libelf/elf32.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
>  lib/libelf/elf64.c | 56 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 166 insertions(+)
> 
> 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..7ea85f0 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,58 @@ 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 != 52)

What is 52? sizeof(struct ehdr32)? Why "!=" rather ">=" ?


> +		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..824b56f 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,58 @@ 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 != 64)

The same questions here.


> +		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;
> +}
>
Stefan Berger May 8, 2020, 9:01 p.m. UTC | #2
On 5/7/20 9:07 PM, Alexey Kardashevskiy wrote:
>
> On 02/04/2020 01:57, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> 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>
>> ---
>>   include/helpers.h  |  2 ++
>>   include/libelf.h   | 14 ++++++++++
>>   lib/libelf/elf.c   | 26 ++++++++++++++++++
>>   lib/libelf/elf32.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
>>   lib/libelf/elf64.c | 56 ++++++++++++++++++++++++++++++++++++++
>>   5 files changed, 166 insertions(+)
>>
>> 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..7ea85f0 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,58 @@ 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 != 52)
> What is 52? sizeof(struct ehdr32)? Why "!=" rather ">=" ?


Right, it's sizeof(struct ehdr32). I will repost this series.
diff mbox series

Patch

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..7ea85f0 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,58 @@  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 != 52)
+		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..824b56f 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,58 @@  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 != 64)
+		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;
+}