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: などのシステム コールを呼び出してから2 回以上呼び出すと、不明なオペコード割り込みが発生することにwrite()気付きました。printf()奇数。

4

1 に答える 1

0

おっと!それを理解しました:仮想アドレスをマップするとき、次のように毎回新しいページを割り当てる必要があります:

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

今では正常に動作します。

なぜそれが機能しなかったのか知りたい人のために:私が含めなかったときprintf()、プログラムのサイズは0x1000バイト未満だったので、1ページだけでマッピングしても問題ありませんでした。printf()またはを含めるfopen()と、プログラムのサイズがはるかに大きくなったため、問題が発生しました。

于 2011-12-20T20:08:23.837 に答える