2017-06-19 16 views
79

Đây là sự cố xảy ra cụ thể trên ARM chứ không phải trên x86 hoặc x64. Tôi đã có vấn đề này được báo cáo bởi một người sử dụng và đã có thể tái sản xuất nó bằng cách sử dụng UWP trên Raspberry Pi 2 thông qua Windows IoT. Tôi đã nhìn thấy loại vấn đề này trước đây với các quy ước gọi không khớp, nhưng tôi chỉ định Cdecl trong khai báo P/Invoke và tôi đã thử thêm rõ ràng __cdecl ở phía gốc với cùng một kết quả. Dưới đây là một số thông tin:Điều gì có thể khiến P/Gọi các đối số bị lỗi khi được thông qua?

P/khai Gọi (reference):

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

C# struct (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 
{ 
} 

Chức năng trong tiêu đề C (reference)

FLSliceResult FLEncoder_Finish(FLEncoder, FLError*); 

FLSliceKết quả có thể gây ra một số sự cố vì tôi t được trả về bởi giá trị và có một số công cụ C++ trên nó ở phía gốc?

Cấu trúc ở bên gốc có thông tin thực tế, nhưng đối với API C, FLEncoder được xác định as an opaque pointer. Khi gọi phương thức trên trên x86 và x64 mọi thứ hoạt động suôn sẻ, nhưng trên ARM, tôi quan sát những điều sau đây. Địa chỉ của đối số đầu tiên là địa chỉ của đối số SECOND, và đối số thứ hai là null (ví dụ: khi tôi đăng nhập các địa chỉ trên mặt C#, ví dụ: 0x054f59b8 và 0x0583f3bc, nhưng sau đó ở bên gốc đối số là 0x0583f3bc và 0x00000000). Điều gì có thể gây ra loại vấn đề này? Có ai có bất kỳ ý tưởng, bởi vì tôi đang bối rối ...

Đây là mã tôi chạy để tái sản xuất:

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); 
} 

Chạy một ứng dụng ++ C như sau hoạt động tốt:

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); 

Logic này có thể kích hoạt sự cố với developer build mới nhất nhưng tiếc là tôi chưa tìm ra cách đáng tin cậy để có thể cung cấp các biểu tượng gỡ lỗi gốc thông qua Nuget sao cho nó có thể được thông qua (chỉ xây dựng mọi thứ từ nguồn dường như làm điều đó ...) để gỡ lỗi là một chút awkwar d vì cả hai thành phần gốc và được quản lý cần được xây dựng. Tôi đang mở để gợi ý về cách làm cho điều này dễ dàng hơn mặc dù nếu ai đó muốn thử. Nhưng nếu bất cứ ai đã trải nghiệm điều này trước hoặc có bất kỳ ý tưởng về lý do tại sao điều này xảy ra, vui lòng thêm câu trả lời, cảm ơn! Tất nhiên, nếu bất cứ ai muốn một trường hợp sinh sản (hoặc dễ dàng để xây dựng một trong đó không cung cấp nguồn bước hoặc một khó khăn để xây dựng một trong đó có) sau đó để lại một bình luận nhưng tôi không muốn đi qua quá trình làm một nếu không có ai sẽ sử dụng nó (tôi không chắc chắn cách thức hoạt động của Windows phổ biến trên ARM thực tế là)

EDIT Cập nhật thú vị: Nếu tôi "giả mạo" chữ ký trong C# và xóa tham số thứ 2 thì cái đầu tiên đi qua OK.

EDIT 2 Second cập nhật thú vị: Nếu tôi thay đổi FLSliceResult định nghĩa C# kích thước UIntPtr-ulong sau đó các đối số đến trong một cách chính xác ... mà không có ý nghĩa kể từ size_t trên ARM nên unsigned int.

CHỈNH SỬA 3 Thêm định nghĩa trong C# cũng thực hiện công việc này, nhưng TẠI SAO?sizeof (FLSliceResult) trong C/C++ cho kiến ​​trúc này trả về 8 như nó cần. Việc đặt cùng kích thước trong C# gây ra sự cố nhưng đặt nó thành 12 làm cho nó hoạt động.

EDIT 4 Tôi đã tối thiểu hóa trường hợp kiểm tra để tôi có thể viết một trường hợp thử nghiệm C++. Trong C# UWP nó không thành công, nhưng trong C++ UWP nó thành công.

EDIT 5Here là hướng dẫn tháo rời cho cả C++ và C# để so sánh (mặc dù C# Tôi không chắc chắn bao nhiêu để mất vì vậy tôi sai lầm ở phía bên lấy quá nhiều)

EDIT 6 Phân tích sâu hơn cho thấy rằng khi chạy "tốt" khi nói dối và nói rằng cấu trúc là 12 byte trên C#, giá trị trả về được chuyển tới thanh ghi r0, với hai arg khác đến qua r1, r2. Tuy nhiên, trong thời gian xấu, điều này được chuyển qua để hai args đang đến qua r0, r1 và giá trị trả về là một nơi khác (chồng con trỏ?)

EDIT 7 tôi tham khảo ý kiến ​​Procedure Call Standard for the ARM Architecture. Tôi tìm thấy trích dẫn này: "Loại hỗn hợp lớn hơn 4 byte hoặc kích thước không thể được xác định tĩnh bởi cả người gọi và callee, được lưu trữ trong bộ nhớ tại địa chỉ được chuyển làm đối số bổ sung khi hàm được gọi (§5.5, quy tắc A.4). Bộ nhớ được sử dụng cho kết quả có thể được sửa đổi tại bất kỳ thời điểm nào trong khi gọi hàm. " Điều này ngụ ý rằng việc chuyển sang r0 là hành vi đúng vì đối số thừa ngụ ý đối số đầu tiên (vì quy ước gọi C không có cách để chỉ định số đối số). Tôi tự hỏi liệu CLR có gây nhầm lẫn điều này với quy tắc khác về cơ bản loại dữ liệu 64 bit: "Loại dữ liệu cơ bản có kích thước hai từ (ví dụ: vectơ được chứa dài, đôi và 64 bit) là được trả lại bằng r0 và r1. "

CHỈNH SỬA 8 Ok có rất nhiều bằng chứng trỏ tới CLR làm điều sai ở đây, vì vậy tôi đã gửi bug report. Tôi hy vọng một ai đó nhận thấy nó giữa tất cả các chương trình tự động đăng các vấn đề về repo đó: -S.

+1

Nhận xét không dành cho thảo luận mở rộng; cuộc hội thoại này đã được [chuyển sang trò chuyện] (http://chat.stackoverflow.com/rooms/157727/discussion-on-question-by-borrrden-what-could-cause-p-invoke-arguments-to-be- ngoài). – Andy

+0

60 upvotes và không có tiền thưởng đã được cung cấp ... đó là lạ –

+6

@MauricioGraciaGutierrez Tôi cho rằng tôi có thể trả lời câu hỏi này với "đây là một lỗi trong động cơ JIT" (Tôi cho rằng hầu hết mọi người đến đây để upvote vì họ quan tâm đến độ phân giải của lỗi) – borrrden

Trả lời

1

Vấn đề tôi đã nộp trên GH đã ở đó một thời gian khá lâu. Tôi tin rằng hành vi này chỉ đơn giản là một lỗi và không cần thêm thời gian để xem xét nó.

Các vấn đề liên quan