mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
firmware: add simple ELF loader for static binaries
This is a small & simple ELF loader which just copies program segments from disk to memory. It only supports static binaries right now. Change-Id: I8944feb5b9dafcc5c56e12383aed25e2718ad7ea
This commit is contained in:
parent
21ba79d431
commit
a610998ea4
5 changed files with 466 additions and 0 deletions
|
|
@ -58,6 +58,10 @@ target/hosted/rolo.c
|
|||
lc-rock.c
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ELF)
|
||||
elf_loader.c
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
|
||||
common/multiboot.c
|
||||
#ifndef BOOTLOADER
|
||||
|
|
|
|||
175
firmware/elf_loader.c
Normal file
175
firmware/elf_loader.c
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2026 Aidan MacDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "elf_loader.h"
|
||||
#include "elf-target.h"
|
||||
#include "file.h"
|
||||
|
||||
#if defined(ROCKBOX_LITTLE_ENDIAN)
|
||||
# define NATIVE_ELF_DATA ELFDATA2LSB
|
||||
#elif defined(ROCKBOX_BIG_ENDIAN)
|
||||
# define NATIVE_ELF_DATA ELFDATA2MSB
|
||||
#else
|
||||
# error "Unknown endianness!"
|
||||
#endif
|
||||
|
||||
_Static_assert(TARGET_ELF_DATA == NATIVE_ELF_DATA,
|
||||
"ELF binaries with foreign endianness not supported");
|
||||
|
||||
static int elf_validate_header(const struct elf_header *header)
|
||||
{
|
||||
if (header->ei_magic[0] != ELFMAG0 ||
|
||||
header->ei_magic[1] != ELFMAG1 ||
|
||||
header->ei_magic[2] != ELFMAG2 ||
|
||||
header->ei_magic[3] != ELFMAG3)
|
||||
return ELF_ERR_BADMAGIC;
|
||||
|
||||
if (header->ei_class != TARGET_ELF_CLASS ||
|
||||
header->ei_data != TARGET_ELF_DATA)
|
||||
return ELF_ERR_UNSUPP_ARCH;
|
||||
|
||||
if (header->ei_version != EV_CURRENT ||
|
||||
header->e_version != EV_CURRENT)
|
||||
return ELF_ERR_UNSUPP_VERSION;
|
||||
|
||||
if (header->ei_osabi != TARGET_ELF_OSABI ||
|
||||
header->ei_abiversion != TARGET_ELF_ABIVERSION)
|
||||
return ELF_ERR_UNSUPP_OS;
|
||||
|
||||
if (header->e_type != ET_EXEC)
|
||||
return ELF_ERR_UNSUPP_TYPE;
|
||||
|
||||
if (header->e_machine != TARGET_ELF_MACHINE)
|
||||
return ELF_ERR_UNSUPP_ARCH;
|
||||
|
||||
if (header->e_phoff == 0)
|
||||
return ELF_ERR_EMPTY;
|
||||
|
||||
if (header->e_phentsize < sizeof(struct elf_phdr))
|
||||
return ELF_ERR_UNSUPP_PHSIZE;
|
||||
|
||||
if (header->e_phnum == PN_XNUM)
|
||||
return ELF_ERR_TOO_MANY_PHDRS;
|
||||
|
||||
return ELF_OK;
|
||||
}
|
||||
|
||||
static int elf_validate_phdr(const struct elf_phdr *phdr,
|
||||
const struct elf_load_context *ctx)
|
||||
{
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
return ELF_ERR_UNSUPP_PHDR;
|
||||
|
||||
if (phdr->p_filesz > phdr->p_memsz)
|
||||
return ELF_ERR_INVALID_PHDR;
|
||||
|
||||
/* Ignore headers which occupy zero memory */
|
||||
if (phdr->p_memsz == 0)
|
||||
return 0;
|
||||
|
||||
for (size_t i = 0; i < ctx->num_mmap; ++i)
|
||||
{
|
||||
const struct elf_memory_map *entry = &ctx->mmap[i];
|
||||
if (phdr->p_vaddr >= entry->addr &&
|
||||
phdr->p_vaddr < entry->addr + entry->size &&
|
||||
phdr->p_memsz <= entry->size)
|
||||
{
|
||||
if ((phdr->p_flags & entry->flags) != phdr->p_flags)
|
||||
return ELF_ERR_MEM_INCOMPAT_FLAGS;
|
||||
|
||||
return ELF_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ELF_ERR_MEM_UNMAPPED;
|
||||
}
|
||||
|
||||
int elf_loadfd(int fd,
|
||||
const struct elf_load_context *ctx,
|
||||
void **entrypoint)
|
||||
{
|
||||
struct elf_header ehdr;
|
||||
struct elf_phdr phdr;
|
||||
ssize_t nread;
|
||||
int err;
|
||||
|
||||
nread = read(fd, &ehdr, sizeof(ehdr));
|
||||
if (nread != (ssize_t)sizeof(ehdr))
|
||||
return ELF_ERR_IO;
|
||||
|
||||
err = elf_validate_header(&ehdr);
|
||||
if (err != ELF_OK)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Iterate over program headers, copying segments to memory
|
||||
*/
|
||||
for (size_t ph_index = 0; ph_index < ehdr.e_phnum; ++ph_index)
|
||||
{
|
||||
off_t ph_off = ehdr.e_phoff + (ph_index * ehdr.e_phentsize);
|
||||
if (lseek(fd, ph_off, SEEK_SET) == (off_t)-1)
|
||||
return ELF_ERR_IO;
|
||||
|
||||
nread = read(fd, &phdr, sizeof(phdr));
|
||||
if (nread != (ssize_t)sizeof(phdr))
|
||||
return ELF_ERR_IO;
|
||||
|
||||
err = elf_validate_phdr(&phdr, ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Load file data to memory if needed */
|
||||
if (phdr.p_filesz > 0)
|
||||
{
|
||||
if (lseek(fd, phdr.p_offset, SEEK_SET) == (off_t)-1)
|
||||
return ELF_ERR_IO;
|
||||
|
||||
nread = read(fd, (void *)phdr.p_vaddr, phdr.p_filesz);
|
||||
if (nread < 0 || (uint32_t)nread != phdr.p_filesz)
|
||||
return ELF_ERR_IO;
|
||||
}
|
||||
|
||||
/* Zero out any memory not backed by file data */
|
||||
if (phdr.p_memsz > phdr.p_filesz)
|
||||
{
|
||||
void *addr = (void *)phdr.p_vaddr + phdr.p_filesz;
|
||||
size_t size = phdr.p_memsz - phdr.p_filesz;
|
||||
|
||||
memset(addr, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
*entrypoint = (void *)ehdr.e_entry;
|
||||
return ELF_OK;
|
||||
}
|
||||
|
||||
int elf_loadpath(const char *filename,
|
||||
const struct elf_load_context *ctx,
|
||||
void **entrypoint)
|
||||
{
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return ELF_ERR_FILE_NOT_FOUND;
|
||||
|
||||
int err = elf_loadfd(fd, ctx, entrypoint);
|
||||
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
50
firmware/include/elf-target.h
Normal file
50
firmware/include/elf-target.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2026 Aidan MacDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef __ELF_TARGET_H__
|
||||
#define __ELF_TARGET_H__
|
||||
|
||||
#include "elf.h"
|
||||
#include "config.h"
|
||||
|
||||
#define TARGET_ELF_CLASS ELFCLASS32
|
||||
#define TARGET_ELF_OSABI ELFOSABI_SYSV
|
||||
#define TARGET_ELF_ABIVERSION 0
|
||||
#define elf_phdr elf32_phdr
|
||||
|
||||
#if defined(CPU_ARM)
|
||||
# define TARGET_ELF_MACHINE EM_ARM
|
||||
#elif defined(CPU_MIPS)
|
||||
# define TARGET_ELF_MACHINE EM_MIPS
|
||||
#elif defined(CPU_COLDFIRE)
|
||||
# define TARGET_ELF_MACHINE EM_68K
|
||||
#else
|
||||
# error "Unknown CPU architecture!"
|
||||
#endif
|
||||
|
||||
#if defined(ROCKBOX_LITTLE_ENDIAN)
|
||||
# define TARGET_ELF_DATA ELFDATA2LSB
|
||||
#elif defined(ROCKBOX_BIG_ENDIAN)
|
||||
# define TARGET_ELF_DATA ELFDATA2MSB
|
||||
#else
|
||||
# error "Unknown endianness!"
|
||||
#endif
|
||||
|
||||
#endif /* __ELF_TARGET_H__ */
|
||||
143
firmware/include/elf.h
Normal file
143
firmware/include/elf.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2026 Aidan MacDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef __ELF_H__
|
||||
#define __ELF_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Field offsets in the e_ident array */
|
||||
#define EI_MAG0 0
|
||||
#define EI_MAG1 1
|
||||
#define EI_MAG2 2
|
||||
#define EI_MAG3 3
|
||||
#define EI_CLASS 4
|
||||
#define EI_DATA 5
|
||||
#define EI_VERSION 6
|
||||
#define EI_OSABI 7
|
||||
#define EI_ABIVERSION 8
|
||||
#define EI_PAD 9
|
||||
#define EI_NIDENT 16
|
||||
|
||||
/* Values for ei_magic */
|
||||
#define ELFMAG0 0x7f
|
||||
#define ELFMAG1 'E'
|
||||
#define ELFMAG2 'L'
|
||||
#define ELFMAG3 'F'
|
||||
|
||||
/* Values for ei_class */
|
||||
#define ELFCLASSNONE 0
|
||||
#define ELFCLASS32 1
|
||||
#define ELFCLASS64 2
|
||||
|
||||
/* Values for ei_data */
|
||||
#define ELFDATANONE 0
|
||||
#define ELFDATA2LSB 1
|
||||
#define ELFDATA2MSB 2
|
||||
|
||||
/* Values for ei_version and e_version */
|
||||
#define EV_NONE 0
|
||||
#define EV_CURRENT 1
|
||||
|
||||
/* Values for ei_osabi */
|
||||
#define ELFOSABI_NONE ELFOSABI_SYSV
|
||||
#define ELFOSABI_SYSV 0
|
||||
|
||||
/* Values for e_type */
|
||||
#define ET_NONE 0
|
||||
#define ET_REL 1
|
||||
#define ET_EXEC 2
|
||||
#define ET_DYN 3
|
||||
#define ET_CORE 4
|
||||
|
||||
/* Values for e_machine */
|
||||
#define EM_NONE 0
|
||||
#define EM_68K 4
|
||||
#define EM_MIPS 8
|
||||
#define EM_ARM 40
|
||||
|
||||
/*
|
||||
* Special value for e_phnum when the real number of
|
||||
* program headers is too large.
|
||||
*/
|
||||
#define PN_XNUM 0xFFFFu
|
||||
|
||||
/* ELF basic types */
|
||||
typedef uint32_t elf_addr_t;
|
||||
typedef uint32_t elf_off_t;
|
||||
|
||||
/* ELF file header */
|
||||
struct elf_header
|
||||
{
|
||||
union {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
struct {
|
||||
uint8_t ei_magic[4];
|
||||
uint8_t ei_class;
|
||||
uint8_t ei_data;
|
||||
uint8_t ei_version;
|
||||
uint8_t ei_osabi;
|
||||
uint8_t ei_abiversion;
|
||||
};
|
||||
};
|
||||
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
elf_addr_t e_entry;
|
||||
elf_off_t e_phoff;
|
||||
elf_off_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
/* Values for p_type */
|
||||
#define PT_NULL 0
|
||||
#define PT_LOAD 1
|
||||
#define PT_DYNAMIC 2
|
||||
#define PT_INTERP 3
|
||||
#define PT_NOTE 4
|
||||
#define PT_SHLIB 5
|
||||
#define PT_PHDR 6
|
||||
|
||||
/* Values for p_flags */
|
||||
#define PF_X (1 << 0) /* Segment is executable */
|
||||
#define PF_W (1 << 1) /* Segment is writable */
|
||||
#define PF_R (1 << 2) /* Segment is readable */
|
||||
|
||||
/* ELF 32-bit program header */
|
||||
struct elf32_phdr
|
||||
{
|
||||
uint32_t p_type;
|
||||
elf_off_t p_offset;
|
||||
elf_addr_t p_vaddr;
|
||||
elf_addr_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
|
||||
#endif /* __ELF_H__ */
|
||||
94
firmware/include/elf_loader.h
Normal file
94
firmware/include/elf_loader.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2026 Aidan MacDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef __ELF_LOADER_H__
|
||||
#define __ELF_LOADER_H__
|
||||
|
||||
/*
|
||||
* Primitive ELF loader -- allows loading an ELF binary from
|
||||
* a file descriptor into RAM. Only static binaries linked at
|
||||
* a fixed address are supported.
|
||||
*
|
||||
* The caller supplies a memory map which limits where the
|
||||
* ELF file can be loaded; if a program header requests any
|
||||
* memory outside of this mapping, loading will fail. There
|
||||
* is no support for relocation, PIC code, or any form of
|
||||
* dynamic linking.
|
||||
*
|
||||
* The loader can return the entry point of the ELF binary.
|
||||
* It does not support arbitrary symbol lookups.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum elf_error
|
||||
{
|
||||
ELF_OK,
|
||||
ELF_ERR_FILE_NOT_FOUND,
|
||||
ELF_ERR_IO,
|
||||
ELF_ERR_EMPTY,
|
||||
ELF_ERR_BADMAGIC,
|
||||
ELF_ERR_UNSUPP_ARCH,
|
||||
ELF_ERR_UNSUPP_OS,
|
||||
ELF_ERR_UNSUPP_VERSION,
|
||||
ELF_ERR_UNSUPP_TYPE,
|
||||
ELF_ERR_UNSUPP_FORMAT,
|
||||
ELF_ERR_UNSUPP_PHSIZE,
|
||||
ELF_ERR_TOO_MANY_PHDRS,
|
||||
ELF_ERR_UNSUPP_PHDR,
|
||||
ELF_ERR_INVALID_PHDR,
|
||||
ELF_ERR_MEM_INCOMPAT_FLAGS,
|
||||
ELF_ERR_MEM_UNMAPPED,
|
||||
};
|
||||
|
||||
/*
|
||||
* Describes an entry in the virtual memory map specified
|
||||
* when loading an ELF file.
|
||||
*
|
||||
* ELF segments must fit completely within a single entry
|
||||
* in the memory map. Segments crossing multiple mappings
|
||||
* are rejected even if the mappings are contiguous.
|
||||
*
|
||||
* A segment may also be rejected if it has flags (R/W/X)
|
||||
* not present in the corresponding memory map entry.
|
||||
*/
|
||||
struct elf_memory_map
|
||||
{
|
||||
uintptr_t addr;
|
||||
size_t size;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct elf_load_context
|
||||
{
|
||||
const struct elf_memory_map *mmap;
|
||||
size_t num_mmap;
|
||||
};
|
||||
|
||||
int elf_loadfd(int fd,
|
||||
const struct elf_load_context *ctx,
|
||||
void **entrypoint);
|
||||
|
||||
int elf_loadpath(const char *filename,
|
||||
const struct elf_load_context *ctx,
|
||||
void **entrypoint);
|
||||
|
||||
#endif /* __ELF_LOADER_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue