Files
net/exec/elf.c
2026-01-06 15:51:03 -08:00

348 lines
11 KiB
C

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <exec/mem.h>
#include <exec/debcon.h>
#include <exec/library.h>
#include <exec/elf.h>
/* From https://chromium.googlesource.com/native_client/src/native_client/+/branch_heads/2311/src/include */
#define EI_NIDENT 16 /* fwd, see rest of EI_* below */
#define ET_NONE 0 /* no file type */
#define ET_REL 1 /* relocatable file */
#define ET_EXEC 2 /* executable file */
#define ET_DYN 3 /* shared object file */
#define ET_CORE 4 /* core file */
/* TODO(karl) figure out effect of adding ET_LOOS through ET_HIOS */
#define ET_LOOS 0xfe00 /* Environment-specific */
#define ET_HIOS 0xfeff /* Environment-specific */
#define ET_LOPROC 0xff00 /* processor-specific */
#define ET_HIPROC 0xffff /* processor-specific */
#define EM_NONE 0 /* no machine */
#define EM_M32 1 /* at&t we 32100 */
#define EM_SPARC 2 /* sparc */
#define EM_386 3 /* intel architecture */
#define EM_68K 4 /* motorola 68000 */
#define EM_88K 5 /* motorola 88000 */
#define EM_860 7 /* intel 80860 */
#define EM_MIPS 8 /* mips rs3000 */
#define EM_MIPS_RS4_BE 10 /* mips rs4000 big-endian */
#define EM_LORESERVED 11
#define EM_HIRESERVED 16
#define EM_ARM 40 /* arm */
#define EM_X86_64 62 /* x86-64 */
#define EV_NONE 0 /* invalid version */
#define EV_CURRENT 1 /* current version */
#define EI_MAG0 0 /* file identification */
#define EI_MAG1 1 /* file identification */
#define EI_MAG2 2 /* file identification */
#define EI_MAG3 3 /* file identification */
#define EI_CLASS 4 /* file class */
#define EI_DATA 5 /* data encoding */
#define EI_VERSION 6 /* file version */
/*
* EI_PAD deviates from the pdf specification, where its value is 7, since
* EI_OSABI and EI_ABIVERSION have been introduced. EI_OSABI and
* EI_OSABIVERSION are from linux elf.h for code usage compatibility.
* Also, for Elf 64, the value for EI_PAD is also 9.
*/
#define EI_PAD 9 /* start of padding bytes */
#define EI_OSABI 7
#define EI_ABIVERSION 8
/*
* ELFMAG and SELFMAG are names/values from linux elf.h, for code usage
* compatibility.
*/
#define ELFMAG "\177ELF"
#define SELFMAG 4
/* EI_CLASS values */
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
/* EI_DATA values */
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
#define PT_NULL 0 /* Unused entry */
#define PT_LOAD 1 /* Loadable segment */
#define PT_DYNAMIC 2 /* Dynamic linking tables */
#define PT_INTERP 3 /* Program interpreter path name */
#define PT_NOTE 4 /* Note section */
#define PT_SHLIB 5 /* Reserved */
#define PT_PHDR 6 /* Program header table */
#define PT_LOOS 0x60000000 /* Environment-specific low */
#define PT_HIOS 0x6fffffff /* Environment-specific high */
#define PT_LOPROC 0x70000000 /* Processor-specific low */
#define PT_HIPROC 0x7fffffff /* Processor-specific high */
/*
* These are from linux elf.h, for code usage
* compatibility.
*/
#define PT_TLS 7
#define PT_GNU_STACK 0x6474e551
#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
#define PF_X 1
#define PF_W 2
#define PF_R 4
/*
* PF_MASKOS is from linux elf.h, for code usage compatibility
*/
#define PF_MASKOS 0x0ff00000 /* os specific */
#define SHF_WRITE 0x1 /* Has writable data */
#define SHF_ALLOC 0x2 /* Allocated in memory image of program */
#define SHF_EXECINSTR 0x4 /* Contains executable instructions */
#define SHF_MASKOS 0x0f000000 /* Environment-specific use */
#define SHF_MASKPROC 0xf0000000 /* Processor-specific use */
#define DT_NULL 0
#define DT_REL 17
#define DT_RELSZ 18
#define ELF_NOTE_GNU "GNU"
/* n_type value for build ID notes generated by "ld --build-id". */
#define NT_GNU_BUILD_ID 3
#define R_386_GLOB_DAT 6
#define R_386_RELATIVE 8
#define R_ARM_GLOB_DAT 21
#define R_ARM_RELATIVE 23
/* Define 32-bit specific types */
typedef uint32_t Elf32_Addr; /* alignment 4 */
typedef uint16_t Elf32_Half; /* alignment 2 */
typedef uint32_t Elf32_Off; /* alignment 4 */
typedef int32_t Elf32_Sword; /* alignment 4 */
typedef uint32_t Elf32_Word; /* alignment 4 */
/* unsigned char, size 1, alignment 1 */
#pragma pack(push,1)
/* Define the structure of the file header for 32 bits. */
typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
/* Define the structure of a program header table for 32-bits. */
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
/*
* Define 32-bit section headers.
* ncfileutil wants section headers, even though service runtime does
* not.
*/
typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
#define ELF32_R_TYPE(val) ((val) & 0xff)
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
typedef struct {
Elf32_Word n_namesz;
Elf32_Word n_descsz;
Elf32_Word n_type;
} Elf32_Nhdr;
#pragma pack(pop)
/* Simple ELF loader for 32-bit i386 images in memory.
* - loads PT_LOAD segments into freshly allocated memory
* - supports ET_DYN (shared object) by relocating the image to a new base
* - applies relocations found via the PT_DYNAMIC table (DT_REL, DT_RELSZ)
* - handles R_386_RELATIVE relocations
*
* This loader is intentionally small and only implements what is needed
* for in-memory device/shared-object loading in this project.
*/
#define ALIGN_DOWN(x, a) ((x) & ~((a)-1))
#define ALIGN_UP(x, a) (((x) + (a)-1) & ~((a)-1))
uintptr_t ElfLoad(struct Library *lib, void *elf_image) {
if (!elf_image) return 0;
uint8_t *base = (uint8_t *)elf_image;
Elf32_Ehdr *eh = (Elf32_Ehdr *)base;
/* Basic validation: magic, class, data */
if (memcmp(eh->e_ident, ELFMAG, SELFMAG) != 0) return 0;
if (eh->e_ident[EI_CLASS] != ELFCLASS32) return 0;
if (eh->e_ident[EI_DATA] != ELFDATA2LSB) return 0;
/* Only handle i386 for now */
if (eh->e_machine != EM_386) return 0;
Elf32_Phdr *ph = (Elf32_Phdr *)(base + eh->e_phoff);
/* Determine address range of loadable segments (virtual addresses).
* For ET_DYN we'll treat p_vaddr as relative offsets and pick a
* new base address. For ET_EXEC we'd ideally load at the linked
* addresses; here we support ET_EXEC only if its p_vaddrs are
* usable after allocation (best-effort).
*/
uint32_t min_vaddr = UINT32_MAX;
uint32_t max_vaddr = 0;
for (int i = 0; i < eh->e_phnum; i++) {
if (ph[i].p_type != PT_LOAD) continue;
if (ph[i].p_memsz == 0) continue;
if (ph[i].p_vaddr < min_vaddr) min_vaddr = ph[i].p_vaddr;
uint32_t end = ph[i].p_vaddr + ph[i].p_memsz;
if (end > max_vaddr) max_vaddr = end;
}
if (min_vaddr == UINT32_MAX) return 0; /* nothing to load */
uint32_t span = max_vaddr - min_vaddr;
/* align to page-size like boundary (4k) */
uint32_t alloc_size = ALIGN_UP(span, 0x1000);
void *load_base = AllocMem(alloc_size);
if (!load_base) return 0;
uint8_t *img_base = (uint8_t *)load_base;
/* zero entire allocation to simplify bss handling */
memset(img_base, 0, alloc_size);
/* Copy PT_LOAD segments into the allocated image */
for (int i = 0; i < eh->e_phnum; i++) {
Elf32_Phdr *p = &ph[i];
if (p->p_type != PT_LOAD) continue;
uint32_t dest_off = p->p_vaddr - min_vaddr;
uint8_t *dest = img_base + dest_off;
if (p->p_filesz) {
/* bounds check in source image */
if ((uint32_t)p->p_offset + p->p_filesz > (uint32_t)eh->e_shoff && eh->e_shoff != 0) {
/* best-effort: but don't fail here if file layout unknown */
}
memcpy(dest, base + p->p_offset, p->p_filesz);
}
if (p->p_memsz > p->p_filesz) {
memset(dest + p->p_filesz, 0, p->p_memsz - p->p_filesz);
}
}
/* Walk program headers to find PT_DYNAMIC (if any) and apply relocations */
Elf32_Dyn *dynamic = NULL;
uint32_t dynamic_size = 0;
for (int i = 0; i < eh->e_phnum; i++) {
Elf32_Phdr *p = &ph[i];
if (p->p_type == PT_DYNAMIC) {
/* dynamic table lives inside loaded memory at p_vaddr */
uint32_t off = p->p_vaddr - min_vaddr;
dynamic = (Elf32_Dyn *)(img_base + off);
dynamic_size = p->p_memsz;
break;
}
}
uintptr_t entry = 0;
if (eh->e_type == ET_DYN) {
entry = (uintptr_t)img_base + (eh->e_entry - min_vaddr);
} else if (eh->e_type == ET_EXEC) {
/* For ET_EXEC assume entry is absolute in header */
entry = (uintptr_t)img_base + (eh->e_entry - min_vaddr);
} else {
/* unsupported type */
return 0;
}
/* Apply relocations from dynamic segment (DT_REL / DT_RELSZ) */
if (dynamic) {
uint32_t rel_addr = 0;
uint32_t rel_sz = 0;
for (Elf32_Dyn *d = dynamic; d->d_tag != DT_NULL; d++) {
switch (d->d_tag) {
case DT_REL:
rel_addr = d->d_un.d_ptr;
break;
case DT_RELSZ:
rel_sz = d->d_un.d_val;
break;
default:
break;
}
}
if (rel_addr && rel_sz) {
/* rel_addr is a virtual address; convert into our load image */
uint32_t rel_off = rel_addr - min_vaddr;
Elf32_Rel *rels = (Elf32_Rel *)(img_base + rel_off);
uint32_t rel_count = rel_sz / sizeof(Elf32_Rel);
for (uint32_t i = 0; i < rel_count; i++) {
Elf32_Rel *r = &rels[i];
uint32_t type = ELF32_R_TYPE(r->r_info);
uint32_t where_off = r->r_offset - min_vaddr;
Elf32_Addr *where = (Elf32_Addr *)(img_base + where_off);
switch (type) {
case R_386_RELATIVE:
/* *where = base + addend; For REL entries addend is the
* original memory value at the location.
*/
*where = (Elf32_Addr)((uintptr_t)img_base + (*where));
break;
case R_386_GLOB_DAT:
/* Without a symbol resolver we cannot set external
* addresses. Leave as-is (best-effort) or attempt to
* treat it like RELATIVE if it appears to be relative.
*/
/* no-op */
break;
default:
/* unsupported relocation type */
break;
}
}
}
}
lib->entry = entry;
lib->base = (uintptr_t)img_base;
return entry;
}