2009-07-30 6 views
35

Je voudrais recueillir ici ce qui se passe lorsque vous exécutez un exécutable sous Windows, Linux et OSX. En particulier, je voudrais comprendre exactement l'ordre des opérations: je suppose que le format de fichier exécutable (PE, ELF ou Mach-O) est chargé par le noyau (mais j'ignore les différentes sections du ELF (Exécutable et Linkable Format) et leur signification), et puis vous avez l'éditeur de liens dynamique qui résout les références, puis la partie __init de l'exécutable est exécuté, puis le principal, puis le __fini, puis le programme est terminé, mais je suis sûr qu'il est très rude, et probablement faux.Que se passe-t-il lorsque vous exécutez un programme?

Modifier: la question est maintenant CW. Je me remplis pour Linux. Si quelqu'un veut faire la même chose pour Win et OSX ce serait génial.

+1

Est-ce que c'est juste moi, ou la portée de cette question est-elle trop large? – mezoid

+0

Je ne pense pas que ce soit trop large, mais devrait probablement être wiki de la communauté –

+0

Je voudrais mettre une prime là-dessus si je ne reçois pas assez de commentaires. Je ne serai pas capable de le faire si c'est CW. –

Répondre

0

Eh bien, en fonction de votre définition exacte, vous devez tenir compte des compilateurs JIT pour des langages tels que .Net et Java. Lorsque vous exécutez un "exe" .Net qui n'est pas techniquement "exécutable", le compilateur JIT intervient et le compile.

+3

Le runtime .Net est un exécutable ... Le fait qu'il exécute un environnement virtuel complet et optimise le bytecode n'est pas pertinent. –

1

Dès que l'image est chargée en mémoire, la magie prend le dessus.

+0

C'est si vous l'avez réglé sur "Magic". "Plus de magie" brise l'univers. – jkeys

30

Ceci est juste à un niveau très élevé et abstrait bien sûr! Comme le dit routeNpingme, les registres sont placés à l'intérieur du CPU et la magie se produit!

Mise à jour: Oui, je ne peux pas speell correctement aujourd'hui!

+0

"Le noyau charge le binaire en mémoire -> Le noyau saute à l'adresse de mémoire spécifique" "Le noyau exécute le code à partir de cet emplacement de mémoire partagée" J'en doute. Le noyau fait très attention au code à exécuter; normalement, il n'exécuterait pas de code d'espace utilisateur. Ce que vous avez dit peut facilement être exploité par des attaquants. La réponse de Stefano est beaucoup plus logique. – Infinite

21

Ok, répondre à ma propre question. Cela se fera progressivement, et seulement pour Linux (et peut-être Mach-O). N'hésitez pas à ajouter plus de choses à vos réponses personnelles, afin qu'ils soient upvoted (et vous pouvez obtenir des badges, car il est maintenant CW).

Je vais commencer à mi-chemin, et construire le reste que je découvre. Ce document a été réalisé avec un x86_64, gcc (GCC) 4.1.2.

Ouverture du fichier, l'initialisation

Dans cette section, nous décrivons ce qui se passe lorsque le programme est invoqué, du point de vue du noyau, jusqu'à ce que le programme est prêt à être exécuté.

  1. L'ELF est ouverte.
  2. le noyau recherche la section .text et la charge en mémoire. Le noyau est chargé en lecture seule.
  3. le noyau charge la section .data
  4. le noyau charge la section .bss et initialise tout le contenu à zéro.
  5. Le noyau transfère le contrôle à l'éditeur de liens dynamique (dont le nom se trouve dans le fichier ELF, dans la section .interp). L'éditeur de liens dynamique résout tous les appels de bibliothèque partagés.
  6. le contrôle est transféré à l'application

exécution du programme

  1. la fonction _start s'invoquée, comme l'en-tête ELF, il indique que le point d'entrée pour l'exécutable
  2. _start appels __libc_start_main dans glibc (par le PLT) passer les informations suivantes à ce

    1. l'annonce robe de la fonction principale réelle
    2. l'adresse argc
    3. l'adresse argv
    4. l'adresse de la _init routine
    5. l'adresse de la routine _fini
    6. un pointeur de fonction pour l'enregistrement de atexit()
    7. l'adresse la plus haute pile disponible
  3. _init est appelé

    1. appelle call_gmon_start pour initialiser le profilage gmon. pas vraiment lié à l'exécution.
    2. appels frame_dummy, qui enveloppe __register_frame_info (adresse de la section eh_frame, adresse de la section bss) (FIXME: qu'est-ce que cette fonction n'initialise vars globales de la section BSS apparemment)
    3. appels __do_global_ctors_aux, dont le rôle est d'appeler tous les mondiale constructeurs énumérés dans la section .ctors.
  4. principale est appelée
  5. extrémités principales
  6. _fini est appelée, qui à son tour les appels __do_global_dtors_aux pour exécuter tous les Destructeurs comme indiqué dans la section .dtors.
  7. le programme se termine.
+1

Je ne sais pas combien de détails vous voulez donner, mais j'ai du mal à suivre cela parce que je ne sais pas ce qu'est un ELF. (eh bien soit linux soit _very_ different sous le capot de ce que j'imaginais) –

+0

Je continuerai cette partie dès que j'ai le temps de continuer à lire les docs que j'ai trouvés. ELF est un format binaire pour les exécutables sous Linux. C'est comme PE dans win et Mach-O dans OSX –

3

Sous Windows, l'image est d'abord chargée dans la mémoire. Le noyau analyse quelles bibliothèques (lire "DLL") il va exiger et les charge aussi.

Il modifie ensuite l'image du programme pour insérer les adresses de mémoire de chacune des fonctions de la bibliothèque dont il a besoin. Ces adresses ont déjà un espace dans le binaire .EXE, mais elles sont simplement remplies de zéros.

La procédure DllMain() de chaque DLL est ensuite exécutée, une par une, de la DLL la plus demandée à la dernière, comme suit un ordre de dépendances. Une fois que toutes les bibliothèques ont été chargées et préparées, l'image est finalement lancée, et tout ce qui se passe dépend de la langue utilisée, du compilateur utilisé et de la routine du programme elle-même.

Questions connexes