/* * ELF loader. * Copyright (C) 2023, @stsp * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include "dos.h" #include "util.h" #include "stub_priv.h" #include "elf.h" #include "elfp.h" #define STUB_DEBUG 0 #if STUB_DEBUG #define stub_debug(...) printf(__VA_ARGS__) #else #define stub_debug(...) #endif struct elf_h { uint32_t va; uint32_t length; uint32_t entry; int phnum; Elf32_Phdr phdr[0]; }; static void *read_elf_headers(int ifile) { Elf32_Ehdr ehdr; struct elf_h *h; int i, rc; unsigned rd; long beg = 0, end = 0; rc = _dos_read(ifile, &ehdr, sizeof(ehdr), &rd); /* get the ELF header */ if (rc || rd != sizeof(ehdr)) { fprintf(stderr, "cant read ELF header\n"); return NULL; } if (memcmp(&ehdr.e_ident, ELFMAG, SELFMAG) || ehdr.e_ehsize != sizeof(ehdr) || ehdr.e_phentsize != sizeof(Elf32_Phdr)) { fprintf(stderr, "bad ELF header\n"); return NULL; } if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) { fprintf(stderr, "bad ELF class %i\n", ehdr.e_ident[EI_CLASS]); return NULL; } if (ehdr.e_phoff > sizeof(ehdr)) _dos_seek(ifile, ehdr.e_phoff - sizeof(ehdr), SEEK_CUR); h = malloc(sizeof(*h) + sizeof(Elf32_Phdr) * ehdr.e_phnum); assert(h); rc = _dos_read(ifile, h->phdr, sizeof(Elf32_Phdr) * ehdr.e_phnum, &rd); if (rc || rd != sizeof(Elf32_Phdr) * ehdr.e_phnum) { fprintf(stderr, "can't read phdr\n"); return NULL; } h->phnum = ehdr.e_phnum; for (i = 0; i < ehdr.e_phnum; i++) { Elf32_Phdr *phdr = &h->phdr[i]; if (phdr->p_type != PT_LOAD) continue; if (phdr->p_align != 4096) { fprintf(stderr, "unsupported ELF alignment %i\n", phdr->p_align); return NULL; } if (phdr->p_vaddr < beg || !beg) beg = phdr->p_vaddr; if (phdr->p_vaddr + phdr->p_memsz > end) end = phdr->p_vaddr + phdr->p_memsz; #if STUB_DEBUG stub_debug("PHDR pa 0x%lx va 0x%lx size 0x%lx foffs 0x%lx\n", phdr->p_paddr, phdr->p_vaddr, phdr->p_filesz, phdr->p_offset); #endif } h->entry = ehdr.e_entry; h->va = beg; h->length = end - beg; return h; } static uint32_t get_elf_length(void *handle) { struct elf_h *h = handle; return h->length; } static uint32_t get_elf_entry(void *handle) { struct elf_h *h = handle; return h->entry; } static uint32_t get_elf_va(void *handle) { struct elf_h *h = handle; return h->va; } static void read_elf_sections(void *handle, char *ptr, int ifile, uint32_t offset) { struct elf_h *h = handle; int i; for (i = 0; i < h->phnum; i++) { Elf32_Phdr *phdr = &h->phdr[i]; long bytes; if (phdr->p_type != PT_LOAD) continue; _dos_seek(ifile, offset + phdr->p_offset, SEEK_SET); bytes = _long_read(ifile, ptr, phdr->p_vaddr, phdr->p_filesz); stub_debug("read returned %li\n", bytes); if (bytes != phdr->p_filesz) { fprintf(stderr, "err reading %i bytes, got %li\n", phdr->p_filesz, bytes); // _exit(EXIT_FAILURE); goto fr; } if (phdr->p_memsz > phdr->p_filesz) { uint32_t len = phdr->p_memsz - phdr->p_filesz; len += len & 1; // word-align memset(ptr + phdr->p_vaddr + phdr->p_filesz, 0, len); } } fr:; } static void elf_close(void *handle) { free(handle); } struct ldops elf_ops = { read_elf_headers, get_elf_va, get_elf_length, get_elf_entry, read_elf_sections, elf_close, };