Question

I've been porting newlib to my very small kernel, and I'm stumped: whenever I include a function that references a system call, my program will page fault on execution. If I call a function that does not reference a system call, like rand(), nothing will go wrong.

Note: By include, I mean as long as the function, e.g. printf() or fopen(), is somewhere inside the program, even if it isn't called through main().

I've had this problem for quite some time now, and have no idea what could be causing this:

  • I've rebuilt newlib numerous times
  • Modified my ELF loader to load the code from the section headers instead of program headers
  • Attempted to build newlib/libgloss separately (which failed)
  • Linked the libraries (libc, libnosys) through the ld script using GROUP, gcc and ld

I'm not quite sure what other information I should include with this, but I'd be happy to include what I can.

Edit: To verify, the page faults occurring are not at the addresses of the failing functions; they are elsewhere in the program. For example, when I call fopen(), located at 0x08048170, I will page fault at 0xA00A316C.

Edit 2: Relevant code for loading 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; 
}

Edit 3: I've noticed that if I call a system call, such as write(), and then call printf() two or more times, I will get an unknown opcode interrupt. Odd.

Was it helpful?

Solution

Whoops! Figured it out: when I map the virtual address, I should allocate a new page each time, like so:

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

Now it works fine.

For those curious as to why it didn't work: when I wasn't including printf(), the size of the program was under 0x1000 bytes, so mapping with only one page was okay. When I include printf() or fopen(), the size of the program was much bigger so that's what caused the issue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top