📄 binfmt_elf32.c
字号:
/* Copyright (C) 2005 David Decotigny 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 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <sos/kmalloc.h>#include <sos/assert.h>#include <sos/physmem.h>#include <drivers/bochs.h>#include <hwcore/paging.h>#include <drivers/zero.h>#include "binfmt_elf32.h"/** * The "C" structure of a user program image in the kernel. Structures * like this are created by the Makefile in the userland/ directory */struct userprog_entry{ const char *name; sos_vaddr_t bottom_vaddr; sos_vaddr_t top_vaddr;};/** * Symbol marking the start of the userprogs table, as setup by the * ld script in the userland/ directory */extern char _userprogs_table;/** * Structure of a mapped resource for an ELF32 program (ie a portion * of the kernel space) */struct elf32_mapped_program{ sos_vaddr_t vaddr; sos_size_t size; int ref_cnt; struct sos_umem_vmm_mapped_resource mr;};/** Called after the virtual region has been inserted inside its address space */static void elf32prog_ref(struct sos_umem_vmm_vr * vr){ struct elf32_mapped_program * elf32prog_resource; elf32prog_resource = (struct elf32_mapped_program*) sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data; elf32prog_resource->ref_cnt ++;}/** Called when the virtual region is removed from its address space */static void elf32prog_unref(struct sos_umem_vmm_vr * vr){ struct elf32_mapped_program * elf32prog_resource; elf32prog_resource = (struct elf32_mapped_program*) sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data; elf32prog_resource->ref_cnt --; SOS_ASSERT_FATAL(elf32prog_resource->ref_cnt >= 0); /* Free the resource if it becomes unused */ if (elf32prog_resource->ref_cnt == 0) sos_kfree((sos_vaddr_t)elf32prog_resource);}/** Called when a legitimate page fault is occuring in the VR */static sos_ret_t elf32prog_page_in(struct sos_umem_vmm_vr * vr, sos_uaddr_t uaddr, sos_bool_t write_access){ struct elf32_mapped_program * elf32prog_resource; sos_ret_t retval = SOS_OK; sos_paddr_t ppage_paddr; sos_uaddr_t upage_uaddr = SOS_PAGE_ALIGN_INF(uaddr); sos_uoffset_t offset_in_prog; sos_size_t size_to_copy; sos_ui32_t access_rights = sos_umem_vmm_get_prot_of_vr(vr); elf32prog_resource = (struct elf32_mapped_program*) sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data; /* Compute the offset in program of the page, and the size to copy in user space */ offset_in_prog = upage_uaddr - sos_umem_vmm_get_start_of_vr(vr) + sos_umem_vmm_get_offset_in_resource(vr); size_to_copy = SOS_PAGE_SIZE; if (offset_in_prog + size_to_copy > elf32prog_resource->size) size_to_copy = elf32prog_resource->size - offset_in_prog; /* If the source page is also aligned, simply remap the kernel area into user space */ if (SOS_IS_PAGE_ALIGNED(elf32prog_resource->vaddr + offset_in_prog)) { sos_vaddr_t kern_vaddr = elf32prog_resource->vaddr + offset_in_prog; ppage_paddr = sos_paging_get_paddr(kern_vaddr); /* Remap it in user space, in read-only mode (to force COW) */ retval = sos_paging_map(ppage_paddr, upage_uaddr, TRUE, access_rights & ~SOS_VM_MAP_PROT_WRITE); SOS_ASSERT_FATAL(SOS_OK == retval); } /* Otherwise we need to allocate a new page */ else { /* Allocate a new page that contains the code/data of the program */ ppage_paddr = sos_physmem_ref_physpage_new(FALSE); if (! ppage_paddr) return -SOS_ENOMEM; /* Map it in user space, in read/write mode for the kernel to copy the data in the page */ retval = sos_paging_map(ppage_paddr, upage_uaddr, TRUE, access_rights | SOS_VM_MAP_PROT_WRITE); SOS_ASSERT_FATAL(SOS_OK == retval); sos_physmem_unref_physpage(ppage_paddr); /* Copy the program in it */ memcpy((void*)upage_uaddr, (void*)elf32prog_resource->vaddr + offset_in_prog, size_to_copy); if (size_to_copy < SOS_PAGE_SIZE) memset((void*)(upage_uaddr + size_to_copy), 0x0, SOS_PAGE_SIZE - size_to_copy); /* Change it read-only if needed */ if (! (access_rights & SOS_VM_MAP_PROT_WRITE)) return sos_paging_set_prot(upage_uaddr, access_rights & ~SOS_VM_MAP_PROT_WRITE); } return retval;}static struct sos_umem_vmm_vr_ops elf32prog_ops = (struct sos_umem_vmm_vr_ops){ .ref = elf32prog_ref, .unref = elf32prog_unref, .page_in = elf32prog_page_in, .unmap = NULL /* ignored */};static sos_ret_t elf32prog_mmap(struct sos_umem_vmm_vr *vr){ return sos_umem_vmm_set_ops_of_vr(vr, &elf32prog_ops);}/* * Local functions *//** * Function to locate the given user program image in the kernel memory */static struct userprog_entry * lookup_userprog(const char *name);sos_uaddr_t sos_binfmt_elf32_map(struct sos_umem_vmm_as * dest_as, const char * progname){ int i; /** * Typedefs, constants and structure definitions as given by the ELF * standard specifications. */ typedef unsigned long Elf32_Addr; typedef unsigned long Elf32_Word; typedef unsigned short Elf32_Half; typedef unsigned long Elf32_Off; typedef signed long Elf32_Sword; /* Elf identification */ #define EI_NIDENT 16 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; } __attribute__((packed)) Elf32_Ehdr_t; /* e_ident value */#define ELFMAG0 0x7f#define ELFMAG1 'E'#define ELFMAG2 'L'#define ELFMAG3 'F'/* e_ident offsets */#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_PAD 7/* e_ident[EI_CLASS] */#define ELFCLASSNONE 0#define ELFCLASS32 1#define ELFCLASS64 2/* e_ident[EI_DATA] */#define ELFDATANONE 0#define ELFDATA2LSB 1#define ELFDATA2MSB 2/* e_type */#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 */#define ET_LOPROC 0xff00 /* Processor-specific */#define ET_HIPROC 0xffff /* Processor-specific *//* e_machine */#define EM_NONE 0 /* No machine */#define EM_M32 1 /* AT&T WE 32100 */#define EM_SPARC 2 /* SPARC */#define EM_386 3 /* Intel 80386 */#define EM_68K 4 /* Motorola 68000 */#define EM_88K 5 /* Motorola 88000 */#define EM_860 7 /* Intel 80860 */#define EM_MIPS 8 /* MIPS RS3000 *//* e_version */#define EV_NONE 0 /* invalid version */#define EV_CURRENT 1 /* current version */ 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; } __attribute__((packed)) Elf32_Phdr_t;/* Reserved segment types 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#define PT_LOPROC 0x70000000#define PT_HIPROC 0x7fffffff/* p_flags */#define PF_X 1#define PF_W 2#define PF_R 4 Elf32_Ehdr_t *elf_hdr; Elf32_Phdr_t *elf_phdrs; struct elf32_mapped_program * mapped_prog; struct userprog_entry * prog; sos_uaddr_t prog_top_user_address = 0; mapped_prog = (struct elf32_mapped_program*) sos_kmalloc(sizeof(struct elf32_mapped_program), 0); if (! mapped_prog) return -SOS_ENOMEM; prog = lookup_userprog(progname); if (! prog) { sos_kfree((sos_vaddr_t)mapped_prog); return 0; } /* Initialize mapped resource */ memset(mapped_prog, 0x0, sizeof(*mapped_prog)); mapped_prog->mr.custom_data = mapped_prog; mapped_prog->mr.mmap = elf32prog_mmap; mapped_prog->mr.allowed_access_rights = SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE | SOS_VM_MAP_PROT_EXEC; mapped_prog->vaddr = prog->bottom_vaddr; mapped_prog->size = prog->top_vaddr - prog->bottom_vaddr; elf_hdr = (Elf32_Ehdr_t*) prog->bottom_vaddr; /* Make sure the image is large enough to contain at least the ELF header */ if (prog->bottom_vaddr + sizeof(Elf32_Ehdr_t) > prog->top_vaddr) { sos_bochs_printf("ELF prog %s: incorrect header\n", prog->name); return 0; } /* Macro to check expected values for some fields in the ELF header */#define ELF_CHECK(hdr,field,expected_value) \ ({ if ((hdr)->field != (expected_value)) \ { \ sos_bochs_printf("ELF prog %s: for %s, expected %x, got %x\n", \ prog->name, \ #field, \ (unsigned)(expected_value), \ (unsigned)(hdr)->field); \ return 0; \ } \ }) ELF_CHECK(elf_hdr, e_ident[EI_MAG0], ELFMAG0); ELF_CHECK(elf_hdr, e_ident[EI_MAG1], ELFMAG1); ELF_CHECK(elf_hdr, e_ident[EI_MAG2], ELFMAG2); ELF_CHECK(elf_hdr, e_ident[EI_MAG3], ELFMAG3); ELF_CHECK(elf_hdr, e_ident[EI_CLASS], ELFCLASS32); ELF_CHECK(elf_hdr, e_ident[EI_DATA], ELFDATA2LSB); ELF_CHECK(elf_hdr, e_type, ET_EXEC); ELF_CHECK(elf_hdr, e_version, EV_CURRENT); /* Get the begining of the program header table */ elf_phdrs = (Elf32_Phdr_t*) (prog->bottom_vaddr + elf_hdr->e_phoff); /* Map the program segment in R/W mode. To make things clean, we should iterate over the sections, not the program header */ for (i = 0 ; i < elf_hdr->e_phnum ; i++) { sos_ui32_t prot_flags; sos_uaddr_t uaddr; /* Ignore the empty program headers that are not marked "LOAD" */ if (elf_phdrs[i].p_type != PT_LOAD) { if (elf_phdrs[i].p_memsz != 0) { sos_display_fatal_error("ELF: non-empty non-LOAD segments not supported yet"); } continue; } if (elf_phdrs[i].p_vaddr < SOS_PAGING_BASE_USER_ADDRESS) { sos_display_fatal_error("User program has an incorrect address"); } prot_flags = 0; if (elf_phdrs[i].p_flags & SOS_VM_MAP_PROT_READ) prot_flags |= SOS_VM_MAP_PROT_READ; if (elf_phdrs[i].p_flags & SOS_VM_MAP_PROT_WRITE) prot_flags |= SOS_VM_MAP_PROT_WRITE; if (elf_phdrs[i].p_flags & SOS_VM_MAP_PROT_EXEC) prot_flags |= SOS_VM_MAP_PROT_EXEC; uaddr = elf_phdrs[i].p_vaddr; SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(uaddr)); /* First of all: map the region of the phdr which is also covered by the file */ SOS_ASSERT_FATAL(SOS_OK == sos_umem_vmm_map(dest_as, &uaddr, SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz), prot_flags, /* PRIVATE */ SOS_VR_MAP_FIXED, & mapped_prog->mr, elf_phdrs[i].p_offset)); /* Then map the remaining by a zero resource */ uaddr += SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz); if (SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz) < SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz)) SOS_ASSERT_FATAL(SOS_OK == sos_dev_zero_map(dest_as, &uaddr, SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz) - SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz), prot_flags, /* PRIVATE */ SOS_VR_MAP_FIXED)); if (prog_top_user_address < uaddr + SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz)) prog_top_user_address = uaddr + SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz); } /* Now prepare the heap */ sos_umem_vmm_init_heap(dest_as, prog_top_user_address); return elf_hdr->e_entry;}/** * Lookup a user program located inside the kernel's image */static struct userprog_entry * lookup_userprog(const char *name){ struct userprog_entry *prog; if (! name) return NULL; /* Walk through the table of user program description structures to find the user program with the given name */ for (prog = (struct userprog_entry*) & _userprogs_table ; prog && (prog->name != NULL) ; prog++) { if (0 == strcmp(name, prog->name)) /* Found it ! */ return prog; } return NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -