2017-06-19 4 views
79

Dies ist ein Problem, das speziell auf dem ARM passiert, nicht auf x86 oder x64. Ich hatte dieses Problem von einem Benutzer gemeldet und konnte es mit UWP auf Raspberry Pi 2 über Windows IoT reproduzieren. Ich habe diese Art von Problem zuvor mit nicht übereinstimmenden Aufrufkonventionen gesehen, aber ich gebe Cdecl in der P/Invoke-Deklaration an und habe versucht, explizit __cdecl auf der nativen Seite mit den gleichen Ergebnissen hinzuzufügen. Hier einige Informationen:Was könnte dazu führen, dass P/Invoke-Argumente bei der Übergabe nicht in Ordnung sind?

P/Invoke-Deklaration (reference):

[DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] 
public static extern FLSliceResult FLEncoder_Finish(FLEncoder* encoder, FLError* outError); 

Die C# structs (reference):

internal unsafe partial struct FLSliceResult 
{ 
    public void* buf; 
    private UIntPtr _size; 

    public ulong size 
    { 
     get { 
      return _size.ToUInt64(); 
     } 
     set { 
      _size = (UIntPtr)value; 
     } 
    } 
} 

internal enum FLError 
{ 
    NoError = 0, 
    MemoryError, 
    OutOfRange, 
    InvalidData, 
    EncodeError, 
    JSONError, 
    UnknownValue, 
    InternalError, 
    NotFound, 
    SharedKeysStateError, 
} 

internal unsafe struct FLEncoder 
{ 
} 

Die Funktion in der C-Header (reference)

FLSliceResult FLEncoder_Finish(FLEncoder, FLError*); 

FLSliceResult kann einige Probleme, weil ich verursachen t wird von Wert zurückgegeben und hat C++ Zeug auf der nativen Seite?

Die structs auf der nativen Seite haben eigentliche Informationen, sondern auch für das C-API wird FLEncoder as an opaque pointer definiert. Beim Aufruf der obigen Methode auf x86 und x64 Dinge reibungslos funktionieren, aber auf dem ARM, beobachte ich Folgendes. Die Adresse des ersten Arguments ist die Adresse des zweiten Arguments, und das zweite Argument ist null (zB wenn ich die Adressen auf der C# Seite einzuloggen ich zum Beispiel 0x054f59b8 und 0x0583f3bc, aber dann auf der nativen Seite die Argumente sind 0x0583f3bc und 0x00000000). Was könnte diese Art von Out-of-Order-Problem verursachen? Hat jemand irgendwelche Ideen haben, denn ich bin ratlos ...

Hier ist der Code Ich laufe zu reproduzieren:

unsafe { 
    var enc = Native.FLEncoder_New(); 
    Native.FLEncoder_BeginDict(enc, 1); 
    Native.FLEncoder_WriteKey(enc, "answer"); 
    Native.FLEncoder_WriteInt(enc, 42); 
    Native.FLEncoder_EndDict(enc); 
    FLError err; 
    NativeRaw.FLEncoder_Finish(enc, &err); 
    Native.FLEncoder_Free(enc); 
} 

Ausführen einen C++ App mit folgenden funktioniert:

auto enc = FLEncoder_New(); 
FLEncoder_BeginDict(enc, 1); 
FLEncoder_WriteKey(enc, FLSTR("answer")); 
FLEncoder_WriteInt(enc, 42); 
FLEncoder_EndDict(enc); 
FLError err; 
auto result = FLEncoder_Finish(enc, &err); 
FLEncoder_Free(enc); 

diese Logik kann den Absturz mit den neuesten developer build auslösen, aber leider habe ich noch nicht herausgefunden, wie zuverlässig die Lage sein, bieten nativen Debug-Symbole über Nuget, so dass es durch verstärkt werden kann (nur alles Sourcen scheint das zu tun ...) Debugging ist ein bisschen unbeholfen d weil sowohl native als auch verwaltete Komponenten erstellt werden müssen. Ich bin offen für Vorschläge, wie man das einfacher machen kann, wenn jemand es versuchen möchte. Aber wenn jemand das schon einmal erlebt hat oder irgendwelche Ideen darüber hat, warum dies passiert, bitte fügen Sie eine Antwort hinzu, danke! Natürlich, wenn jemand einen Reproduktionsfall haben will (entweder einen einfach zu erstellen, der keine Source-Stepping bietet oder einen schwer zu bauen, der tut), dann hinterlasse einen Kommentar, aber ich möchte nicht durch den Prozess gehen wenn niemand es benutzen wird (ich bin nicht sicher, wie populär das Laufen von Windows-Sachen auf aktuellem ARM ist)

EDIT Interessantes Update: Wenn ich die Unterschrift in C# "fälsche" und den 2. Parameter entferne, dann der erste kommt durch OK.

EDIT 2 Zweites interessantes Update: Wenn ich die C# FLSliceResult Definition der Größe UIntPtr-ulong dann die Argumente kommen in richtig ... ändern, welche seit size_t auf ARM macht keinen Sinn, sollte unsigned int sein.

EDIT 3 Hinzufügen der [StructLayout(LayoutKind.Sequential, Size = 12)] zu der Definition in C# macht auch das funktioniert, aber WARUM?sizeof (FLSliceResult) in C/C++ für diese Architektur gibt 8 wie gewünscht zurück. Die Einstellung der gleichen Größe in C# verursacht einen Absturz, aber wenn Sie 12 einstellen, funktioniert es.

EDIT 4 Ich habe den Testfall minimiert, so dass ich auch einen C++ Testfall schreiben konnte. In C# UWP schlägt es fehl, aber in C++ UWP ist es erfolgreich.

EDIT 5Here sind die zerlegten Anweisungen für beide C++ und C# für den Vergleich (wenn auch C# bin ich nicht sicher, wie viel zu nehmen, so irrte ich auf der Seite, die zu viel)

EDIT 6 Weitere Analyse zeigt, dass während des "guten" Laufs, wenn ich lüge und sage, dass die Struktur 12 Bytes auf C# ist, der Rückgabewert an Register r0 übergeben wird, wobei die anderen zwei Argumente über r1, r2 eingehen. Doch in der schlechten Lauf, das verschoben wird über so dass die beiden args kommen in über r0 sind, R1 und der Rückgabewert irgendwo anders ist (Stack-Pointer?)

EDIT 7 ich die Procedure Call Standard for the ARM Architecture konsultiert. Ich fand dieses Zitat: "Ein zusammengesetzter Typ, der größer als 4 Bytes ist oder dessen Größe nicht statisch vom Aufrufer und callee bestimmt werden kann, wird im Speicher an einer Adresse gespeichert, die beim Aufruf der Funktion als zusätzliches Argument übergeben wurde (§5.5, Regel A.4). Der für das Ergebnis zu verwendende Speicher kann während des Funktionsaufrufs jederzeit geändert werden. " Dies impliziert, dass das Übergeben in r0 das korrekte Verhalten ist, da das zusätzliche Argument das erste enthält (da die C-Aufrufkonvention keine Möglichkeit hat, die Anzahl der Argumente anzugeben). Ich frage mich, ob die CLR dies mit einer anderen Regel über 64-Bit-Grundtypen verwirrt: "Ein doppelter Wort-sortierter grundlegender Datentyp (z. B. lange lange, doppelte und 64-Bit containerisierte Vektoren) ist in R0 zurückgegeben und r1. "

EDIT 8 Ok, es gibt eine Menge Beweise dafür, dass die CLR hier das Falsche macht, also habe ich eine bug report eingereicht. Ich hoffe, jemand bemerkt es zwischen all den automatisierten Bots, die Probleme in diesem Repo veröffentlichen: -S.

+1

Kommentare sind nicht für längere Diskussion; Diese Konversation wurde [in den Chat verschoben] (http://chat.stackoverflow.com/rooms/157727/discussion-on-question-by-borrrden-what-cow-cause-p-invoke-arguments-to-be- aus). – Andy

+0

60 Upvotes und keine Bounty wurde angeboten ... das ist seltsam –

+6

@MauricioGraciaGutierrez Ich könnte diese Frage mit "Dies ist ein Bug in der JIT-Engine" beantworten (ich nehme an, die meisten Leute kommen hierher, um zu upvoten, weil sie daran interessiert sind) die Auflösung des Bugs) – borrrden

Antwort

1

Das Problem, das ich bei GH eingereicht habe, sitzt seit geraumer Zeit dort. Ich glaube, dass dieses Verhalten einfach ein Fehler ist und keine Zeit mehr damit verbracht wird, sich damit zu beschäftigen.

Verwandte Themen