Skip to content

Commit

Permalink
elf: endian conversions
Browse files Browse the repository at this point in the history
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
  • Loading branch information
npiggin authored and oohal committed Dec 16, 2019
1 parent 789624f commit c47b2ff
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 123 deletions.
36 changes: 25 additions & 11 deletions core/flash.c
Expand Up @@ -523,24 +523,38 @@ const char *flash_map_resource_name(enum resource_id id)

static size_t sizeof_elf_from_hdr(void *buf)
{
struct elf_hdr *elf = (struct elf_hdr*) buf;
struct elf_hdr *elf = (struct elf_hdr *)buf;
size_t sz = 0;

BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf_hdr));
BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf64_hdr));
BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf32_hdr));
BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf64be_hdr));
BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf32be_hdr));

if (elf->ei_ident == ELF_IDENT) {
if (elf->ei_class == ELF_CLASS_64) {
struct elf64_hdr *elf64 = (struct elf64_hdr*) buf;
sz = le64_to_cpu(elf64->e_shoff) +
((uint32_t)le16_to_cpu(elf64->e_shentsize) *
(uint32_t)le16_to_cpu(elf64->e_shnum));
if (elf->ei_data == ELF_DATA_LSB) {
struct elf64le_hdr *kh = (struct elf64le_hdr *)buf;
sz = le64_to_cpu(kh->e_shoff) +
((uint32_t)le16_to_cpu(kh->e_shentsize) *
(uint32_t)le16_to_cpu(kh->e_shnum));
} else {
struct elf64be_hdr *kh = (struct elf64be_hdr *)buf;
sz = be64_to_cpu(kh->e_shoff) +
((uint32_t)be16_to_cpu(kh->e_shentsize) *
(uint32_t)be16_to_cpu(kh->e_shnum));
}
} else if (elf->ei_class == ELF_CLASS_32) {
struct elf32_hdr *elf32 = (struct elf32_hdr*) buf;
sz = le32_to_cpu(elf32->e_shoff) +
(le16_to_cpu(elf32->e_shentsize) *
le16_to_cpu(elf32->e_shnum));
if (elf->ei_data == ELF_DATA_LSB) {
struct elf32le_hdr *kh = (struct elf32le_hdr *)buf;
sz = le32_to_cpu(kh->e_shoff) +
(le16_to_cpu(kh->e_shentsize) *
le16_to_cpu(kh->e_shnum));
} else {
struct elf32be_hdr *kh = (struct elf32be_hdr *)buf;
sz = be32_to_cpu(kh->e_shoff) +
(be16_to_cpu(kh->e_shentsize) *
be16_to_cpu(kh->e_shnum));
}
}
}

Expand Down
92 changes: 51 additions & 41 deletions core/init.c
Expand Up @@ -89,9 +89,9 @@ static void checksum_romem(void);

static bool try_load_elf64_le(struct elf_hdr *header)
{
struct elf64_hdr *kh = (struct elf64_hdr *)header;
struct elf64le_hdr *kh = (struct elf64le_hdr *)header;
uint64_t load_base = (uint64_t)kh;
struct elf64_phdr *ph;
struct elf64le_phdr *ph;
unsigned int i;

printf("INIT: 64-bit LE kernel discovered\n");
Expand All @@ -103,7 +103,7 @@ static bool try_load_elf64_le(struct elf_hdr *header)
* to work for the Linux Kernel because it's a fairly dumb ELF
* but it will not work for any ELF binary.
*/
ph = (struct elf64_phdr *)(load_base + le64_to_cpu(kh->e_phoff));
ph = (struct elf64le_phdr *)(load_base + le64_to_cpu(kh->e_phoff));
for (i = 0; i < le16_to_cpu(kh->e_phnum); i++, ph++) {
if (le32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
continue;
Expand Down Expand Up @@ -137,23 +137,24 @@ static bool try_load_elf64_le(struct elf_hdr *header)

static bool try_load_elf64(struct elf_hdr *header)
{
struct elf64_hdr *kh = (struct elf64_hdr *)header;
struct elf64be_hdr *kh = (struct elf64be_hdr *)header;
struct elf64le_hdr *khle = (struct elf64le_hdr *)header;
uint64_t load_base = (uint64_t)kh;
struct elf64_phdr *ph;
struct elf64_shdr *sh;
struct elf64be_phdr *ph;
struct elf64be_shdr *sh;
unsigned int i;

/* Check it's a ppc64 LE ELF */
if (kh->ei_ident == ELF_IDENT &&
kh->ei_data == ELF_DATA_LSB &&
kh->e_machine == le16_to_cpu(ELF_MACH_PPC64)) {
if (khle->ei_ident == ELF_IDENT &&
khle->ei_data == ELF_DATA_LSB &&
le16_to_cpu(khle->e_machine) == ELF_MACH_PPC64) {
return try_load_elf64_le(header);
}

/* Check it's a ppc64 ELF */
if (kh->ei_ident != ELF_IDENT ||
kh->ei_data != ELF_DATA_MSB ||
kh->e_machine != ELF_MACH_PPC64) {
be16_to_cpu(kh->e_machine) != ELF_MACH_PPC64) {
prerror("INIT: Kernel doesn't look like an ppc64 ELF\n");
return false;
}
Expand All @@ -165,16 +166,18 @@ static bool try_load_elf64(struct elf_hdr *header)
* to work for the Linux Kernel because it's a fairly dumb ELF
* but it will not work for any ELF binary.
*/
ph = (struct elf64_phdr *)(load_base + kh->e_phoff);
for (i = 0; i < kh->e_phnum; i++, ph++) {
if (ph->p_type != ELF_PTYPE_LOAD)
ph = (struct elf64be_phdr *)(load_base + be64_to_cpu(kh->e_phoff));
for (i = 0; i < be16_to_cpu(kh->e_phnum); i++, ph++) {
if (be32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
continue;
if (ph->p_vaddr > kh->e_entry ||
(ph->p_vaddr + ph->p_memsz) < kh->e_entry)
if (be64_to_cpu(ph->p_vaddr) > be64_to_cpu(kh->e_entry) ||
(be64_to_cpu(ph->p_vaddr) + be64_to_cpu(ph->p_memsz)) <
be64_to_cpu(kh->e_entry))
continue;

/* Get our entry */
kernel_entry = kh->e_entry - ph->p_vaddr + ph->p_offset;
kernel_entry = be64_to_cpu(kh->e_entry) -
be64_to_cpu(ph->p_vaddr) + be64_to_cpu(ph->p_offset);
break;
}

Expand All @@ -189,23 +192,27 @@ static bool try_load_elf64(struct elf_hdr *header)
* into an executable section or not to figure this out. Default
* to assuming it obeys the ABI.
*/
sh = (struct elf64_shdr *)(load_base + kh->e_shoff);
for (i = 0; i < kh->e_shnum; i++, sh++) {
if (sh->sh_addr <= kh->e_entry &&
(sh->sh_addr + sh->sh_size) > kh->e_entry)
sh = (struct elf64be_shdr *)(load_base + be64_to_cpu(kh->e_shoff));
for (i = 0; i < be16_to_cpu(kh->e_shnum); i++, sh++) {
if (be64_to_cpu(sh->sh_addr) <= be64_to_cpu(kh->e_entry) &&
(be64_to_cpu(sh->sh_addr) + be64_to_cpu(sh->sh_size)) >
be64_to_cpu(kh->e_entry))
break;
}

if (i == kh->e_shnum || !(sh->sh_flags & ELF_SFLAGS_X)) {
if (i == be16_to_cpu(kh->e_shnum) ||
!(be64_to_cpu(sh->sh_flags) & ELF_SFLAGS_X)) {
kernel_entry = *(uint64_t *)(kernel_entry + load_base);
kernel_entry = kernel_entry - ph->p_vaddr + ph->p_offset;
kernel_entry = kernel_entry -
be64_to_cpu(ph->p_vaddr) + be64_to_cpu(ph->p_offset);
}

kernel_entry += load_base;
kernel_32bit = false;

kernel_size = kh->e_shoff +
((uint32_t)kh->e_shentsize * (uint32_t)kh->e_shnum);
kernel_size = be64_to_cpu(kh->e_shoff) +
((uint32_t)be16_to_cpu(kh->e_shentsize) *
(uint32_t)be16_to_cpu(kh->e_shnum));

printf("INIT: 64-bit kernel entry at 0x%llx, size 0x%lx\n",
kernel_entry, kernel_size);
Expand All @@ -215,9 +222,9 @@ static bool try_load_elf64(struct elf_hdr *header)

static bool try_load_elf32_le(struct elf_hdr *header)
{
struct elf32_hdr *kh = (struct elf32_hdr *)header;
struct elf32le_hdr *kh = (struct elf32le_hdr *)header;
uint64_t load_base = (uint64_t)kh;
struct elf32_phdr *ph;
struct elf32le_phdr *ph;
unsigned int i;

printf("INIT: 32-bit LE kernel discovered\n");
Expand All @@ -229,7 +236,7 @@ static bool try_load_elf32_le(struct elf_hdr *header)
* to work for the Linux Kernel because it's a fairly dumb ELF
* but it will not work for any ELF binary.
*/
ph = (struct elf32_phdr *)(load_base + le32_to_cpu(kh->e_phoff));
ph = (struct elf32le_phdr *)(load_base + le32_to_cpu(kh->e_phoff));
for (i = 0; i < le16_to_cpu(kh->e_phnum); i++, ph++) {
if (le32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
continue;
Expand Down Expand Up @@ -259,22 +266,23 @@ static bool try_load_elf32_le(struct elf_hdr *header)

static bool try_load_elf32(struct elf_hdr *header)
{
struct elf32_hdr *kh = (struct elf32_hdr *)header;
struct elf32be_hdr *kh = (struct elf32be_hdr *)header;
struct elf32le_hdr *khle = (struct elf32le_hdr *)header;
uint64_t load_base = (uint64_t)kh;
struct elf32_phdr *ph;
struct elf32be_phdr *ph;
unsigned int i;

/* Check it's a ppc32 LE ELF */
if (header->ei_ident == ELF_IDENT &&
header->ei_data == ELF_DATA_LSB &&
header->e_machine == le16_to_cpu(ELF_MACH_PPC32)) {
if (khle->ei_ident == ELF_IDENT &&
khle->ei_data == ELF_DATA_LSB &&
le16_to_cpu(khle->e_machine) == ELF_MACH_PPC32) {
return try_load_elf32_le(header);
}

/* Check it's a ppc32 ELF */
if (header->ei_ident != ELF_IDENT ||
header->ei_data != ELF_DATA_MSB ||
header->e_machine != ELF_MACH_PPC32) {
if (kh->ei_ident != ELF_IDENT ||
kh->ei_data != ELF_DATA_MSB ||
be16_to_cpu(kh->e_machine) != ELF_MACH_PPC32) {
prerror("INIT: Kernel doesn't look like an ppc32 ELF\n");
return false;
}
Expand All @@ -286,16 +294,18 @@ static bool try_load_elf32(struct elf_hdr *header)
* to work for the Linux Kernel because it's a fairly dumb ELF
* but it will not work for any ELF binary.
*/
ph = (struct elf32_phdr *)(load_base + kh->e_phoff);
for (i = 0; i < kh->e_phnum; i++, ph++) {
if (ph->p_type != ELF_PTYPE_LOAD)
ph = (struct elf32be_phdr *)(load_base + be32_to_cpu(kh->e_phoff));
for (i = 0; i < be16_to_cpu(kh->e_phnum); i++, ph++) {
if (be32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
continue;
if (ph->p_vaddr > kh->e_entry ||
(ph->p_vaddr + ph->p_memsz) < kh->e_entry)
if (be32_to_cpu(ph->p_vaddr) > be32_to_cpu(kh->e_entry) ||
(be32_to_cpu(ph->p_vaddr) + be32_to_cpu(ph->p_memsz)) <
be32_to_cpu(kh->e_entry))
continue;

/* Get our entry */
kernel_entry = kh->e_entry - ph->p_vaddr + ph->p_offset;
kernel_entry = be32_to_cpu(kh->e_entry) -
be32_to_cpu(ph->p_vaddr) + be32_to_cpu(ph->p_offset);
break;
}

Expand Down

0 comments on commit c47b2ff

Please sign in to comment.