// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags // - if clone_flags & CLONE_VM, then "share" ; else "duplicate" static int copy_mm(uint32_t clone_flags, struct proc_struct *proc) { struct mm_struct *mm, *oldmm = current->mm; /* current is a kernel thread */ if (oldmm == NULL) { return 0; } if (clone_flags & CLONE_VM) { mm = oldmm; goto good_mm; } int ret = -E_NO_MEM; if ((mm = mm_create()) == NULL) { goto bad_mm; } if (setup_pgdir(mm) != 0) { goto bad_pgdir_cleanup_mm; } lock_mm(oldmm); { ret = dup_mmap(mm, oldmm); } unlock_mm(oldmm); if (ret != 0) { goto bad_dup_cleanup_mmap; } good_mm: if (mm != oldmm) { mm->brk_start = oldmm->brk_start; mm->brk = oldmm->brk; bool intr_flag; local_intr_save(intr_flag); { list_add(&(proc_mm_list), &(mm->proc_mm_link)); } local_intr_restore(intr_flag); } mm_count_inc(mm); proc->mm = mm; set_pgdir (proc, mm->pgdir); return 0; bad_dup_cleanup_mmap: exit_mmap(mm); put_pgdir(mm); bad_pgdir_cleanup_mm: mm_destroy(mm); bad_mm: return ret; }
static int load_icode(int fd, int argc, char **kargv, int envc, char **kenvp) { assert(argc >= 0 && argc <= EXEC_MAX_ARG_NUM); assert(envc >= 0 && envc <= EXEC_MAX_ENV_NUM); if (current->mm != NULL) { panic("load_icode: current->mm must be empty.\n"); } int ret = -E_NO_MEM; struct mm_struct *mm; if ((mm = mm_create()) == NULL) { goto bad_mm; } if (setup_pgdir(mm) != 0) { goto bad_pgdir_cleanup_mm; } mm->brk_start = 0; struct Page *page; struct elfhdr __elf, *elf = &__elf; if ((ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0)) != 0) { goto bad_elf_cleanup_pgdir; } if (elf->e_magic != ELF_MAGIC) { ret = -E_INVAL_ELF; goto bad_elf_cleanup_pgdir; } struct proghdr __ph, *ph = &__ph; uint32_t vm_flags, phnum; pte_perm_t perm = 0; for (phnum = 0; phnum < elf->e_phnum; phnum ++) { off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum; if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) { goto bad_cleanup_mmap; } if (ph->p_type != ELF_PT_LOAD) { continue ; } if (ph->p_filesz > ph->p_memsz) { ret = -E_INVAL_ELF; goto bad_cleanup_mmap; } vm_flags = 0; ptep_set_u_read(&perm); if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE; if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ; if (vm_flags & VM_WRITE) ptep_set_u_write(&perm); if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) { goto bad_cleanup_mmap; } if (mm->brk_start < ph->p_va + ph->p_memsz) { mm->brk_start = ph->p_va + ph->p_memsz; } off_t offset = ph->p_offset; size_t off, size; uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE); end = ph->p_va + ph->p_filesz; while (start < end) { if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { ret = -E_NO_MEM; goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } if ((ret = load_icode_read(fd, page2kva(page) + off, size, offset)) != 0) { goto bad_cleanup_mmap; } start += size, offset += size; } end = ph->p_va + ph->p_memsz; if (start < la) { /* ph->p_memsz == ph->p_filesz */ if (start == end) { continue ; } off = start + PGSIZE - la, size = PGSIZE - off; if (end < la) { size -= la - end; } memset(page2kva(page) + off, 0, size); start += size; assert((end < la && start == end) || (end >= la && start == la)); } while (start < end) { if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { ret = -E_NO_MEM; goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } memset(page2kva(page) + off, 0, size); start += size; } } sysfile_close(fd); mm->brk_start = mm->brk = ROUNDUP(mm->brk_start, PGSIZE); /* setup user stack */ vm_flags = VM_READ | VM_WRITE | VM_STACK; if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) { goto bad_cleanup_mmap; } bool intr_flag; local_intr_save(intr_flag); { list_add(&(proc_mm_list), &(mm->proc_mm_link)); } local_intr_restore(intr_flag); mm_count_inc(mm); current->mm = mm; set_pgdir(current, mm->pgdir); mm->lapic = pls_read(lapic_id); mp_set_mm_pagetable(mm); if (init_new_context (current, elf, argc, kargv, envc, kenvp) < 0) goto bad_cleanup_mmap; ret = 0; out: return ret; bad_cleanup_mmap: exit_mmap(mm); bad_elf_cleanup_pgdir: put_pgdir(mm); bad_pgdir_cleanup_mm: mm_destroy(mm); bad_mm: goto out; }
static int load_icode(int fd, int argc, char **kargv, int envc, char **kenvp) { assert(argc >= 0 && argc <= EXEC_MAX_ARG_NUM); assert(envc >= 0 && envc <= EXEC_MAX_ENV_NUM); if (current->mm != NULL) { panic("load_icode: current->mm must be empty.\n"); } int ret = -E_NO_MEM; //#ifdef UCONFIG_BIONIC_LIBC uint32_t real_entry; //#endif //UCONFIG_BIONIC_LIBC struct mm_struct *mm; if ((mm = mm_create()) == NULL) { goto bad_mm; } if (setup_pgdir(mm) != 0) { goto bad_pgdir_cleanup_mm; } mm->brk_start = 0; struct Page *page; struct elfhdr __elf, *elf = &__elf; if ((ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0)) != 0) { goto bad_elf_cleanup_pgdir; } if (elf->e_magic != ELF_MAGIC) { ret = -E_INVAL_ELF; goto bad_elf_cleanup_pgdir; } //#ifdef UCONFIG_BIONIC_LIBC real_entry = elf->e_entry; uint32_t load_address, load_address_flag = 0; //#endif //UCONFIG_BIONIC_LIBC struct proghdr __ph, *ph = &__ph; uint32_t vm_flags, phnum; pte_perm_t perm = 0; //#ifdef UCONFIG_BIONIC_LIBC uint32_t is_dynamic = 0, interp_idx; uint32_t bias = 0; //#endif //UCONFIG_BIONIC_LIBC for (phnum = 0; phnum < elf->e_phnum; phnum++) { off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum; if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) { goto bad_cleanup_mmap; } if (ph->p_type == ELF_PT_INTERP) { is_dynamic = 1; interp_idx = phnum; continue; } if (ph->p_type != ELF_PT_LOAD) { continue; } if (ph->p_filesz > ph->p_memsz) { ret = -E_INVAL_ELF; goto bad_cleanup_mmap; } if (ph->p_va == 0 && !bias) { bias = 0x00008000; } if ((ret = map_ph(fd, ph, mm, &bias, 0)) != 0) { kprintf("load address: 0x%08x size: %d\n", ph->p_va, ph->p_memsz); goto bad_cleanup_mmap; } if (load_address_flag == 0) load_address = ph->p_va + bias; ++load_address_flag; /*********************************************/ /* vm_flags = 0; ptep_set_u_read(&perm); if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE; if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ; if (vm_flags & VM_WRITE) ptep_set_u_write(&perm); if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) { goto bad_cleanup_mmap; } if (mm->brk_start < ph->p_va + ph->p_memsz) { mm->brk_start = ph->p_va + ph->p_memsz; } off_t offset = ph->p_offset; size_t off, size; uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE); end = ph->p_va + ph->p_filesz; while (start < end) { if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { ret = -E_NO_MEM; goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } if ((ret = load_icode_read(fd, page2kva(page) + off, size, offset)) != 0) { goto bad_cleanup_mmap; } start += size, offset += size; } end = ph->p_va + ph->p_memsz; if (start < la) { // ph->p_memsz == ph->p_filesz if (start == end) { continue ; } off = start + PGSIZE - la, size = PGSIZE - off; if (end < la) { size -= la - end; } memset(page2kva(page) + off, 0, size); start += size; assert((end < la && start == end) || (end >= la && start == la)); } while (start < end) { if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { ret = -E_NO_MEM; goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } memset(page2kva(page) + off, 0, size); start += size; } */ /**************************************/ } mm->brk_start = mm->brk = ROUNDUP(mm->brk_start, PGSIZE); /* setup user stack */ vm_flags = VM_READ | VM_WRITE | VM_STACK; if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) { goto bad_cleanup_mmap; } if (is_dynamic) { elf->e_entry += bias; bias = 0; off_t phoff = elf->e_phoff + sizeof(struct proghdr) * interp_idx; if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) { goto bad_cleanup_mmap; } char *interp_path = (char *)kmalloc(ph->p_filesz); load_icode_read(fd, interp_path, ph->p_filesz, ph->p_offset); int interp_fd = sysfile_open(interp_path, O_RDONLY); assert(interp_fd >= 0); struct elfhdr interp___elf, *interp_elf = &interp___elf; assert((ret = load_icode_read(interp_fd, interp_elf, sizeof(struct elfhdr), 0)) == 0); assert(interp_elf->e_magic == ELF_MAGIC); struct proghdr interp___ph, *interp_ph = &interp___ph; uint32_t interp_phnum; uint32_t va_min = 0xffffffff, va_max = 0; for (interp_phnum = 0; interp_phnum < interp_elf->e_phnum; ++interp_phnum) { off_t interp_phoff = interp_elf->e_phoff + sizeof(struct proghdr) * interp_phnum; assert((ret = load_icode_read(interp_fd, interp_ph, sizeof(struct proghdr), interp_phoff)) == 0); if (interp_ph->p_type != ELF_PT_LOAD) { continue; } if (va_min > interp_ph->p_va) va_min = interp_ph->p_va; if (va_max < interp_ph->p_va + interp_ph->p_memsz) va_max = interp_ph->p_va + interp_ph->p_memsz; } bias = get_unmapped_area(mm, va_max - va_min + 1 + PGSIZE); bias = ROUNDUP(bias, PGSIZE); for (interp_phnum = 0; interp_phnum < interp_elf->e_phnum; ++interp_phnum) { off_t interp_phoff = interp_elf->e_phoff + sizeof(struct proghdr) * interp_phnum; assert((ret = load_icode_read(interp_fd, interp_ph, sizeof(struct proghdr), interp_phoff)) == 0); if (interp_ph->p_type != ELF_PT_LOAD) { continue; } assert((ret = map_ph(interp_fd, interp_ph, mm, &bias, 1)) == 0); } real_entry = interp_elf->e_entry + bias; sysfile_close(interp_fd); kfree(interp_path); } sysfile_close(fd); bool intr_flag; local_intr_save(intr_flag); { list_add(&(proc_mm_list), &(mm->proc_mm_link)); } local_intr_restore(intr_flag); mm_count_inc(mm); current->mm = mm; set_pgdir(current, mm->pgdir); mm->cpuid = myid(); mp_set_mm_pagetable(mm); if (!is_dynamic) { real_entry += bias; } #ifdef UCONFIG_BIONIC_LIBC if (init_new_context_dynamic(current, elf, argc, kargv, envc, kenvp, is_dynamic, real_entry, load_address, bias) < 0) goto bad_cleanup_mmap; #else if (init_new_context(current, elf, argc, kargv, envc, kenvp) < 0) goto bad_cleanup_mmap; #endif //UCONFIG_BIONIC_LIBC ret = 0; out: return ret; bad_cleanup_mmap: exit_mmap(mm); bad_elf_cleanup_pgdir: put_pgdir(mm); bad_pgdir_cleanup_mm: mm_destroy(mm); bad_mm: goto out; }