2011-12-18 1 views
3

Je portais newlib dans mon très petit noyau, et je suis perplexe: chaque fois que inclut une fonction qui fait référence à un appel système, mon programme va signaler une erreur lors de l'exécution. Si j'appelle une fonction qui ne fait pas référence à un appel système, comme rand(), rien ne va mal.Erreur de page avec les fonctions newlib

Note: Par inclusion, je veux dire aussi longtemps que la fonction, par ex. ou fopen(), est quelque part dans le programme, même si elle n'est pas appelée par main().

J'ai eu ce problème depuis un certain temps maintenant, et ne savent pas ce qui pourrait être la cause:

  • J'ai rebâti newlib plusieurs fois
  • modifié mon chargeur ELF pour charger le le code des en-têtes de section au lieu d'en-têtes de programme
  • ont tenté de construire newlib/libgloss séparément (qui a échoué)
  • reliaient les bibliothèques (libc, libnosys) par le script en utilisant ld GROUP, gcc et ld

Je ne suis pas tout à fait sûr quelle autre information je devrais inclure avec ceci, mais je serais heureux d'inclure ce que je peux. Editer: Pour vérifier, les défauts de page qui se produisent ne sont pas aux adresses des fonctions défaillantes; ils sont ailleurs dans le programme. Par exemple, lorsque j'appelle fopen(), situé à 0x08048170, je vais signaler une erreur à 0xA00A316C.

Edit 2: code approprié pour le chargement 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: Je l'ai remarqué que si j'appelle un appel système, comme write(), puis appeler printf() deux fois ou plus, je obtiendra une interruption d'opcode inconnue. Impair.

+0

Pouvez-vous exécuter l'équivalent moral de 'ldd/path/to/binary' sur un exécutable et un exécutable défaillant? – sarnold

+1

@sarnold J'ai couru 'objdump' et' readelf' (mon environnement n'a pas 'ldd'); il n'y a pas de section dynamique, de relocalisation ou de section de déroulement dans les deux exécutables. –

Répondre

0

Oups! Figured it out: quand je mapper l'adresse virtuelle, j'allouer une nouvelle page à chaque fois, comme ceci:

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

Maintenant, il fonctionne très bien. Pour ceux qui sont curieux de savoir pourquoi cela ne fonctionnait pas: lorsque je n'incluais pas printf(), la taille du programme était inférieure à 0x1000 octets, donc le mappage avec une seule page était correct. Lorsque j'inclus printf() ou fopen(), la taille du programme était beaucoup plus grande, c'est donc ce qui a causé le problème.

Questions connexes