31 votes

Fonctions .NET désassemblées

Lorsque je désassemble des fonctions .NET, je remarque qu'elles commencent toutes par un modèle similaire. Que fait ce code initial ?

Ce code apparaît avant le code réel de ce que la fonction est censée faire. S'agit-il d'une sorte de vérification du nombre de paramètres ?

func1

private static void Foo(int i)
{
   Console.WriteLine("hello");
}

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        eax 
00000004  mov         dword ptr [ebp-4],ecx 
00000007  cmp         dword ptr ds:[005C14A4h],0 
0000000e  je          00000015 
00000010  call        65E0367F 
//the console writleline code follows here and is not part of the question

func2

static private void Bar()
{
   for (int i = 0; i < 1000; i++)
   {
      Foo(i);
   }
}

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        eax 
00000004  cmp         dword ptr ds:[006914A4h],0 
0000000b  je          00000012 
0000000d  call        65CC36CF 
// the for loop code follows here

func3

private static void Foo()
{
   Console.WriteLine("hello");
}

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  cmp         dword ptr ds:[005614A4h],0 
0000000a  je          00000011 
0000000c  call        65E3367F 

[Edit] Est-ce que cette description est correcte ?

//fix stackframe
00000000  push        ebp 
00000001  mov         ebp,esp 
//store eax so it can be used locally
00000003  push        eax 
//ensure static ctor have been called
00000004  cmp         dword ptr ds:[006914A4h],0 
//it has been called, ignore it
0000000b  je          00000012
//it hasn't been called, call it now 
0000000d  call        65CC36CF 

ou ?

19voto

CodesInChaos Points 60274

Ce prologue comporte deux parties.

Mise en place du cadre d'empilage

Ceci stocke le registre EBP actuel sur la pile et assigne ensuite la valeur du stackpointer(ESP) à EBP.

push        ebp 
mov         ebp,esp

S'il y a des variables locales qui sont stockées sur la pile (c'est-à-dire qu'il n'y a pas assez de place dans les registres disponibles) alors les ESP seront déplacées de leur taille pour construire le cadre de la pile de la fonction courante.

Et à la fin de la fonction, vous verrez ces opérations inversées, de sorte que le cadre de la pile de la fonction précédente est restauré.

EBP doit toujours pointer vers le début du cadre de la pile de la fonction courante.
ESP jusqu'à la fin (qui a une adresse plus basse sur x86 parce que la pile se développe vers le bas).

Cela fait partie des conventions d'appel communes et est nécessaire pour le déroulement de la pile lorsqu'une exception est levée. Il n'est pas spécifique à .net et est utilisé par la plupart des conventions d'appel sous Windows/x86.

Après avoir mis en place le cadre de la pile, il est courant de stocker certains registres sur la pile. En effet, vous pouvez vouloir utiliser certains registres comme variables temporaires, mais la convention d'appel exige que votre fonction les conserve. Vous devez donc les sauvegarder sur la pile. Les registres qui doivent être conservés et ceux qui peuvent être modifiés dépendent de la convention d'appel que vous utilisez.

Lorsque vous vous référez à des variables locales sur la pile, vous pouvez utiliser [ebp-x]ebp pointe vers le début du cadre de la pile, et x est un décalage qui indique où la variable est stockée dans le cadre de la pile. Vous pouvez également utiliser [esp+y] avec un décalage par rapport à la fin du cadre de la pile.

Appel au constructeur/initialisateur statique

Comme danbystrom a remarqué que la deuxième partie est très probablement l'appel à un constructeur/initialisateur statique. Puisque le constructeur statique n'est pas appelé au démarrage du programme mais au premier accès, chaque accès pour lequel la gigue ne peut pas garantir que le constructeur statique a déjà été exécuté doit vérifier s'il a été appelé, et l'appelle si ce n'est pas le cas.

00000004  cmp         dword ptr ds:[006914A4h],0 
0000000b  je          00000012 
0000000d  call        65CC36CF

C'est quelque chose comme if (globalVar!=0) Call Function_65CC36CF . Il est probable que la variable globale indique si le constructeur statique a été exécuté et que l'appel est un appel au constructeur statique.


Pour autant que je sache, vos commentaires sur le démontage sont corrects.


Consultez cet article du blog de OldNewThing sur les stackframes : Comment sauver une trace de pile cassée : Récupérer la chaîne EBP

2voto

Dan Byström Points 5725

Comme vos méthodes sont toutes statiques, le code est utilisé pour vérifier si l'initialisateur statique de la classe a déjà été exécuté.

00000007  cmp    dword ptr ds:[005C14A4h],0  ; test if static initializer has executed
0000000e  je     00000015                    ; skip call to initializer if already done
00000010  call   65E0367F                    ; call static initializer
00000015  ....                               ; continue with the method's code

0voto

Greg D Points 24218

Si vous parlez du push et du mov, il s'agit juste de réparer la pile d'appels. Je ne suis pas sûr de ce que fait le reste de ces segments.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X