3

我一直在将 newlib 移植到我非常小的内核中,但我很困惑:每当我包含一个引用系统调用的函数时,我的程序都会在执行时出现页面错误。如果我调用一个不引用系统调用的函数,比如rand(),什么都不会出错。

注意:包含,我的意思是只要函数,例如printf()or fopen(),在程序内部的某个地方,即使它不是通过 调用的main()

这个问题我已经有一段时间了,不知道是什么原因造成的:

  • 我已经多次重建 newlib
  • 修改了我的 ELF 加载器以从节头而不是程序头加载代码
  • 尝试单独构建 newlib/libgloss(失败)
  • GROUP使用、gcc 和 ld通过 ld 脚本链接库(libc、libnosys)

我不太确定我应该包含哪些其他信息,但我很乐意包含我可以包含的信息。

编辑:为了验证,发生的页面错误不在失败函数的地址;他们在程序的其他地方。例如,当我调用fopen()位于 0x08048170 的 时,我将在 0xA00A316C 处出现页面错误。

编辑2:加载ELF的相关代码:

int krun(u8int *name) {
    int fd = kopen(name); 
    Elf32_Ehdr *ehdr = kmalloc(sizeof(Elf32_Ehdr*));
    read(fd, ehdr, sizeof(Elf32_Ehdr));

    if (ehdr->e_ident[0] != 0x7F || ehdr->e_ident[1] != 'E' || ehdr->e_ident[2] != 'L' || ehdr->e_ident[3] != 'F') {
        kfree(ehdr);
        return -1; 
    }

    int pheaders    = ehdr->e_phnum;
    int phoff       = ehdr->e_phoff;
    int phsize      = ehdr->e_phentsize;

    int sheaders    = ehdr->e_shnum;
    int shoff       = ehdr->e_shoff;
    int shsize      = ehdr->e_shentsize; 

    for (int i = 0; i < pheaders; i++) {
        lseek(fd, phoff + phsize * i, SEEK_SET);

        Elf32_Phdr *phdr = kmalloc(sizeof(Elf32_Phdr*));
        read(fd, phdr, sizeof(Elf32_Phdr)); 

        u32int page = PMMAllocPage();

        int flags = 0; 
        if (phdr->p_flags & PF_R) flags |= PAGE_PRESENT;
        if (phdr->p_flags & PF_W) flags |= PAGE_WRITE; 

        int pages = (phdr->p_memsz / 0x1000) + 1;
        while (pages >= 0) {
            u32int mapaddr = (phdr->p_vaddr + (pages * 0x1000)) & 0xFFFFF000; 
            map(mapaddr, page, flags | PAGE_USER); 
            pages--; 
        }

        lseek(fd, phdr->p_offset, SEEK_SET);
        read(fd, (void *)phdr->p_vaddr, phdr->p_filesz);   

        kfree(phdr);
    }

    // Removed: code block that zeroes .bss: it's already zeroed whenever I check it anyways
    // Removed: code block that creates thread and adds it to scheduler 

    kfree(ehdr);                
    return 0; 
}

编辑3:我注意到如果我调用系统调用,例如write(),然后调用printf()两次或更多次,我会得到一个未知的操作码中断。奇怪的。

4

1 回答 1

0

哎呀!想通了:当我映射虚拟地址时,我应该每次都分配一个新页面,如下所示:

map(mapaddr, PMMAllocPage(), flags | PAGE_USER); 

现在它工作正常。

对于那些好奇为什么它不起作用的人:当我不包含 时printf(),程序的大小在 0x1000 字节以下,所以只映射一页是可以的。当我包含printf()orfopen()时,程序的大小要大得多,这就是导致问题的原因。

于 2011-12-20T20:08:23.837 回答