2009-04-23 15 views
4

Dans un constructeur C#, qui se termine par un appel à this(...), l'appel réel se traduit à ceci:Besoin d'aide pour déchiffrer une ligne de code assembleur, à partir du code .NET JITted

0000003d call  dword ptr ds:[199B88E8h] 

Quelle est la DS enregistrer le contenu ici? Je sais que c'est le segment de données, mais cet appel est-il à travers une table VMT ou similaire? J'en doute cependant, car this(...) ne serait pas un appel à une méthode virtuelle, juste un autre constructeur. Je demande parce que la valeur à cet endroit semble être mauvaise d'une certaine façon, si je tape F11, trace dans (Visual Studio 2008), sur cette instruction d'appel, le programme se bloque avec une violation d'accès. Le code est au fond d'une bibliothèque de contrôle tierce, où, bien que j'ai le code source, je n'ai pas les assemblies compilées avec assez d'informations de débogage que je peux tracer à travers le code C#, seulement par le désassembleur, et puis je dois faire correspondre cela au code réel.

Le code C# en question est la suivante:

public AxisRangeData(AxisRange range) : this(range, range.Axis) { 
} 

réflecteur me montre ce code IL:

.maxstack 8 
L_0000: ldarg.0 
L_0001: ldarg.1 
L_0002: ldarg.1 
L_0003: callvirt instance class DevExpress.XtraCharts.AxisBase DevExpress.XtraCharts.AxisRange::get_Axis() 
L_0008: call instance void DevExpress.XtraCharts.Native.AxisRangeData::.ctor(class DevExpress.XtraCharts.ChartElement, class DevExpress.XtraCharts.AxisBase) 
L_000d: ret 

Il est que le dernier Appel, à l'autre constructeur de la même classe, qui échoue . Le débogueur ne surface jamais dans l'autre méthode, il se bloque juste.

Le démontage de la méthode après JITting est la suivante:

00000000 push  ebp 
00000001 mov   ebp,esp 
00000003 sub   esp,14h 
00000006 mov   dword ptr [ebp-4],ecx 
00000009 mov   dword ptr [ebp-8],edx 
0000000c cmp   dword ptr ds:[18890E24h],0 
00000013 je   0000001A 
00000015 call  61843511 
0000001a mov   eax,dword ptr [ebp-4] 
0000001d mov   dword ptr [ebp-0Ch],eax 
00000020 mov   eax,dword ptr [ebp-8] 
00000023 mov   dword ptr [ebp-10h],eax 
00000026 mov   ecx,dword ptr [ebp-8] 
00000029 cmp   dword ptr [ecx],ecx 
0000002b call  dword ptr ds:[1889D0DCh] // range.Axis 
00000031 mov   dword ptr [ebp-14h],eax 
00000034 push  dword ptr [ebp-14h] 
00000037 mov   edx,dword ptr [ebp-10h] 
0000003a mov   ecx,dword ptr [ebp-0Ch] 
0000003d call  dword ptr ds:[199B88E8h] // this(range, range.Axis)? 
00000043 nop    
00000044 mov   esp,ebp 
00000046 pop   ebp 
00000047 ret    

Fondamentalement, ce que je pose est la suivante:

  • Ce que le but de la ds:[ADDR] indirection ici? VMT-table est seulement pour le virtuel n'est-ce pas? et ceci est le constructeur
  • Le constructeur pourrait-il encore être JITted, ce qui pourrait signifier que l'appel ferait appel à un shim JIT? J'ai peur que je sois dans l'eau profonde ici, donc tout pourrait et pourrait aider.

Modifier: Eh bien, le problème ne faisait qu'empirer, ou mieux, ou autre chose.

Nous développons la fonctionnalité .NET dans un projet C# dans une solution Visual Studio 2008, ainsi que le débogage et le développement via Visual Studio. Toutefois, à la fin, ce code sera chargé dans un environnement d'exécution .NET hébergé par une application Delphi Win32. Pour faciliter l'expérimentation de telles fonctionnalités, nous pouvons également configurer le projet/solution/débogueur Visual Studio pour copier les DLL produites dans le répertoire de l'application Delphi, puis exécuter l'application Delphi via le débogueur Visual Studio.

Il s'avère que le problème disparaît si j'exécute le programme en dehors du débogueur, mais lors du débogage, il apparaît à chaque fois.

Pas sûr que cela aide, mais puisque le code n'est pas prévu pour la sortie de production pour un autre 6 mois ou plus, alors il enlève une partie de la pression pour la version de test que nous avons bientôt.

Je vais plonger dans les parties de la mémoire plus tard, mais probablement pas avant le week-end, et poster un suivi.

+0

Pouvez-vous trouver l'adresse réellement pointée par ds: [199B88E8h] et la désassembler? – Groo

+0

C'est une adresse stockée à cet endroit, et cette adresse pointe vers la mémoire non mappée, d'où la violation d'accès. –

Répondre

3

Le segment de données est l'endroit où les compilateurs placent généralement des variables globales et où réside la table d'adresses d'importation.

00000029 cmp   dword ptr [ecx],ecx 
0000002b call  dword ptr ds:[1889D0DCh] 

La première ligne est en fait un nul, qui déclenche finalement une NullReferenceException si le pointeur se trouve dans le registre ECX est invalide.

L'instruction MSIL callvirt doit effectuer la vérification null avant d'appeler la méthode actuelle. Cela étant dit, nous pouvons supposer que ces deux lignes de code d'assemblage ont la représentation de code MSIL suivant:

class DevExpress.XtraCharts.AxisBase DevExpress.XtraCharts.AxisRange::get_Axis() 

Et le code assembleur commenté:

00000026 mov   ecx,dword ptr [ebp-8]  // store the pointer to the 'range' in ECX 
00000029 cmp   dword ptr [ecx],ecx  // null-check 
0000002b call  dword ptr ds:[1889D0DCh] // range.get_Axis() 
00000031 mov   dword ptr [ebp-14h],eax // store the result in a local variable 
00000034 push  dword ptr [ebp-14h]  // push the result onto a stack 
00000037 mov   edx,dword ptr [ebp-10h] // this variable was previously loaded with the 'range' pointer 
0000003a mov   ecx,dword ptr [ebp-0Ch] // here seems to be stored the actual 'this' pointer 
0000003d call  dword ptr ds:[199B88E8h] // call the this(...) ctor 

On ne sait pas bien pourquoi il est écraser, avez-vous essayé de rechercher le contenu de l'emplacement de mémoire (DS:[199B88E8h])?

+0

Oui, et je posterai les valeurs que je vois dans les différentes instructions d'appel demain. Autant dire que ça a l'air bizarre, comparé à tous les autres (je me suis arrêté sur chaque appel-instruction jusqu'à ce point). Le défectueux est le seul avec un premier chiffre différent de zéro. –