/***************************************************************************** Code snippet to load ELF executables. The executable must be statically-linked. This code is public domain (no copyright). You can do whatever you want with it. EXPORTS: int load_elf_exec(task_t *task, unsigned char *image, unsigned *entry); *****************************************************************************/ /* flags used to describe task memory sections */ #define SF_ZERO 0x10 /* BSS */ #define SF_LOAD 0x08 /* load from file */ #define SF_READ 0x04 /* readable */ #define SF_WRITE 0x02 /* writable */ #define SF_EXEC 0x01 /* executable */ #define SF_BSS (SF_ZERO | SF_READ | SF_WRITE) /* user-defined task structure, used here as tramp data for add_section() */ typedef struct { char whatever; } task_t; /* user-defined function to add memory section to task should return 0 if success, <0 if error */ int add_section(task_t *task, unsigned flags, unsigned long adr, unsigned long size, unsigned long offset); #if defined(__GNUC__) #define __PACKED__ __attribute__((packed)) #else #define __PACKED__ /* nothing */ #endif /* or use STDINT.H, if you have it... */ typedef unsigned short uint16_t; typedef unsigned long uint32_t; typedef struct { unsigned char magic[4] __PACKED__; unsigned char bitness __PACKED__; unsigned char endian __PACKED__; unsigned char ver_1 __PACKED__; unsigned char res[9] __PACKED__; /* multi-byte fields from here on are big endian for big endian CPUs or little endian for little-endian CPUs. Thus, we can use a packed struct instead of horrid char array, byte-swapping macros, etc. */ uint16_t file_type __PACKED__; uint16_t machine __PACKED__; uint32_t ver_2 __PACKED__; uint32_t entry __PACKED__; uint32_t phtab_offset __PACKED__; uint32_t shtab_offset __PACKED__; uint32_t flags __PACKED__; uint16_t file_hdr_size __PACKED__; uint16_t phtab_ent_size __PACKED__; uint16_t num_phs __PACKED__; uint16_t shtab_ent_size __PACKED__; uint16_t num_shs __PACKED__; uint16_t shstrtab_index __PACKED__; } elf_file_t; /* 52 bytes */ typedef struct { uint32_t type __PACKED__; uint32_t offset __PACKED__; uint32_t virt_adr __PACKED__; uint32_t phys_adr __PACKED__; uint32_t disk_size __PACKED__; uint32_t mem_size __PACKED__; uint32_t flags __PACKED__; uint32_t align __PACKED__; } elf_seg_t; /* 32 bytes */ /***************************************************************************** Creates task from memory-mapped ELF executable file at address 'image'. Returns: +1 if file is not valid ELF 0 if success (*entry set to entry point) <0 if error (dynamically-linked file, or error from add_section()) If your OS has file I/O code, you could replace 'unsigned char *image' with a file handle, make 'elf' and 'seg' actual structs (instead of pointers), seek and read from the file, etc. *****************************************************************************/ int load_elf_exec(task_t *task, unsigned char *image, unsigned *entry) { elf_file_t *elf; elf_seg_t *seg; int i, j; /* validate ELF headers */ elf = (elf_file_t *)image; if(elf->magic[0] != 0x7F || elf->magic[1] != 'E' || elf->magic[2] != 'L' || elf->magic[3] != 'F') return 1; if(elf->bitness != 1 || /* 1=32-bit, 2=64-bit */ elf->endian != 1 || /* 1=little-endian, 2=big */ elf->ver_1 != 1 || /* 1=current ELF spec */ elf->file_type != 2 || /* 1=reloc, 2=exec, 3=DLL */ elf->machine != 3 || /* 3=i386 */ elf->ver_2 != 1) return 1; /* get entry point */ (*entry) = elf->entry; /* seek to program headers (segments) */ image += elf->phtab_offset; for(i = 0; i < elf->num_phs; i++) { seg = (elf_seg_t *)(image + elf->phtab_ent_size * i); /* choke on 2=DYNAMIC and the forbidden 5=SHLIB segments */ if(seg->type == 2 || seg->type == 5) return -1; /* handle 1=LOAD segment */ else if(seg->type == 1) { j = add_section(task, SF_LOAD | (seg->flags & 7), seg->virt_adr, seg->disk_size, seg->offset); if(j != 0) return j; /* if size-in-mem > size-on-disk, this segment contains the BSS */ if(seg->mem_size > seg->disk_size) { j = add_section(task, SF_ZERO | (seg->flags & 7), seg->virt_adr + seg->disk_size, seg->mem_size - seg->disk_size, seg->offset); if(j != 0) return j; } } /* ignore 0=NULL, 6=PHDR, 3=INTERP, and 4=NOTE segments else nothing; */ } return 0; }