2017-08-17 1 views
1

Vous avez peut-être entendu parler de StoneKnifeForth, un projet de kragen: https://github.com/kragen/stoneknifeforth. C'est un programme Python qui agit comme un petit interpréteur Forth et un programme Forth qui agit comme un compilateur Forth. Par conséquent, vous pouvez construire un binaire de compilateur Forth en utilisant ces deux à l'unisson. Après avoir porté StoneKnifeForth en C++ (https://github.com/tekknolagi/stoneknifecpp), j'ai remarqué que tous les binaires produits par StoneKnifeForth (l'un ou l'autre) segfault sur Linux 64 bits. Autrement dit, si vous clone stoneknifecpp et exécutez:SIGSEGV lors de l'exécution de binaires 32 bits sur un système 64 bits Linux

make 
./l01compiler # produced by the Forth program 

Vous obtiendrez les éléments suivants:

willow% ./l01compiler     
[1] 31614 segmentation fault ./l01compiler 

Ce n'est pas un message d'erreur très intéressant, évidemment, donc je pensais que je strace il :

willow% strace ./l01compiler 
execve("./l01compiler", ["./l01compiler"], [/* 110 vars */]) = -1 EPERM (Operation not permitted) 
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- 
+++ killed by SIGSEGV +++ 
[1] 31615 segmentation fault (core dumped) strace ./l01compiler 

Et obtenu ... un peu plus d'informations. Il ressemble à l'en-tête ELF est erroné en quelque sorte, sauf pour les deux détails intéressants suivants:

  • Il fonctionne très bien sous 32 bits qemu
  • Il fonctionne très bien si je sudo ./l01compiler

Je Je suis un peu perplexe quant à savoir pourquoi, même après la recherche sur Internet des différences possibles entre les formats d'en-tête ELF entre les noyaux Linux 32 bits et 64 bits, etc. Si quelqu'un a des informations, je serais ravi.

Je joins l'en-tête ci-dessous:

willow% readelf -h l01compiler 
ELF Header: 
    Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
    Class:        ELF32 
    Data:        2's complement, little endian 
    Version:       1 (current) 
    OS/ABI:       UNIX - System V 
    ABI Version:      0 
    Type:        EXEC (Executable file) 
    Machine:       Intel 80386 
    Version:       0x1 
    Entry point address:    0x1e39 
    Start of program headers:   52 (bytes into file) 
    Start of section headers:   0 (bytes into file) 
    Flags:        0x0 
    Size of this header:    52 (bytes) 
    Size of program headers:   32 (bytes) 
    Number of program headers:   1 
    Size of section headers:   40 (bytes) 
    Number of section headers:   0 
    Section header string table index: 0 
+0

Veuillez montrer la sortie de 'readelf -l'. Il est probable que le problème concerne les segments 'PT_LOAD', pas l'en-tête ELF. –

+0

@EmployedRussian Mon ami Tom a aidé à trouver le problème plus tôt aujourd'hui. Je vais essayer de poster une réponse automatique bientôt. – tekknolagi

+0

@EmployedRussian J'ai posté une réponse qui explique les choses. – tekknolagi

Répondre

1

Un grand merci à Tom Hebb (tchebb) pour confirmer mes soupçons et de trouver cela. Comme on peut le voir dans this commit, le problème était que l'adresse d'origine était simplement trop faible. Ce n'est pas lié à 32 bits ou 64 bits, mais plutôt au noyau plus tôt au noyau plus récent.

Les nouveaux noyaux ont augmenté le paramètre sysctl vm.mmap_min_addr, ce qui signifie que l'ancienne origine empêcherait le démarrage du programme. Cela explique pourquoi sudo travaillé. Comme Tom l'a expliqué, "à moins d'appeler qemu avec le support KVM, qemu est un émulateur et non un hyperviseur, donc il simule l'espace adresse et le sous-système de mémoire virtuelle dans le logiciel et n'impose aucune restriction d'adresse."

+0

Votre réponse explique pourquoi le programme a fonctionné correctement sous QEMU, mais pas pourquoi il a fonctionné sous 'sudo'. En particulier, le commentaire que vous avez référencé dit "nous ne pouvons pas aller plus bas que 0x10000", alors que votre programme est lié à "0x1000" (ou à peu près). –

+0

@EmployedRussian vous avez raison. J'ai agi de la sorte à la main. Je ne suis pas tout à fait sûr, mais je continue à chercher. https://wiki.debian.org/mmap_min_addr, par exemple, dit que root résout le problème pour dosemu/qemu ... – tekknolagi