Điều gì có thể khiến các đối số P / Invoke không theo thứ tự khi được truyền?


79

Đây là sự cố xảy ra cụ thể trên ARM, không phải trên x86 hoặc x64. Tôi đã gặp sự cố này do người dùng báo cáo và có thể tái tạo nó bằng UWP trên Raspberry Pi 2 thông qua Windows IoT. Tôi đã từng gặp 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 đang chỉ định Cdecl trong khai báo P / Invoke và tôi đã thử thêm __cdecl ở phía gốc một cách rõ ràng với kết quả tương tự. Đây là một số thông tin:

P / Khai báo mời ( tham khảo ):

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

Các cấu trúc C # ( tham chiếu ):

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
{
}

Hàm trong tiêu đề C ( tham chiếu )

FLSliceResult FLEncoder_Finish(FLEncoder, FLError*);

FLSliceResult có thể gây ra một số vấn đề vì nó được trả về theo giá trị và có một số nội dung C ++ trên đó ở phía gốc?

Các cấu trúc ở phía gốc có thông tin thực tế, nhưng đối với API C, FLEncoder được định nghĩa là một con trỏ mờ . Khi gọi phương thức trên trên x86 và x64, mọi thứ hoạt động trơn tru, nhưng trên ARM, tôi quan sát thấy những điều sau. Địa chỉ của đối số đầu tiên là địa chỉ của đối số SECOND và đối số thứ hai là rỗng (ví dụ: khi tôi ghi các địa chỉ ở phía C #, tôi nhận được, ví dụ: 0x054f59b8 và 0x0583f3bc, nhưng sau đó ở phía gốc là các đối số là 0x0583f3bc và 0x00000000). Điều gì có thể gây ra loại vấn đề không theo thứ tự này? Có ai có bất kỳ ý kiến, vì tôi đang bối rối ...

Đây là mã tôi chạy để tái tạo:

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 ứng dụng C ++ với các tính năng 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ể gây ra sự cố với bản dựng mới nhất dành cho nhà phát triểnnhưng tiếc là tôi vẫn chưa tìm ra cách đáng tin cậy để có thể cung cấp các ký hiệu gỡ lỗi gốc thông qua Nuget để nó có thể được thực hiện (chỉ xây dựng mọi thứ từ nguồn dường như làm được điều đó ...) nên việc gỡ lỗi hơi khó xử vì cả bản gốc và các thành phần được quản lý cần được xây dựng. Tôi sẵn sàng nhận các đề xuất về cách làm cho việc này dễ dàng hơn nếu ai đó muốn thử. Nhưng nếu bất kỳ ai đã trải qua điều này trước đây hoặc có bất kỳ ý tưởng nào 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 sao chép (một trường hợp dễ xây dựng không cung cấp bước nguồn hoặc một trường hợp khó xây dựng có) thì hãy để lại nhận xét nhưng tôi không muốn thực hiện quá trình tạo nếu không ai sẽ sử dụng nó (tôi không chắc việc chạy Windows phổ biến trên ARM thực tế như thế nào)

CHỈNH SỬA 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ì tham số đầu tiên sẽ được chấp nhận.

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

CHỈNH SỬA 3 Thêm [StructLayout(LayoutKind.Sequential, Size = 12)]vào định nghĩa trong C # cũng làm cho điều này hoạt động, nhưng TẠI SAO? sizeof (FLSliceResult) trong C / C ++ cho kiến ​​trúc này trả về 8 như bình thường. Đặt kích thước tương tự trong C # gây ra lỗi, nhưng đặt nó thành 12 sẽ làm cho nó hoạt động.

CHỈNH SỬA 4 Tôi đã tối giản test case để tôi cũng có thể viết test case C ++. Trong C # UWP nó không thành công, nhưng trong C ++ UWP nó thành công.

CHỈNH SỬA 5 Đây là hướng dẫn tách rời cho cả C ++ và C # để so sánh (mặc dù C # tôi không chắc nên lấy bao nhiêu vì vậy tôi đã mắc lỗi về việc lấy quá nhiều)

CHỈNH SỬA 6 Phân tích sâu hơn cho thấy rằng trong quá trình chạy "tốt" khi tôi 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 đến thanh ghi r0, với hai args khác đến thông qua r1, r2. Tuy nhiên, trong trường hợp xấu, điều này được chuyển qua để hai args đến qua r0, r1 và giá trị trả về ở một nơi khác (con trỏ ngăn xếp?)

CHỈNH SỬA 7 Tôi đã tham khảo Tiêu chuẩn cuộc gọi thủ tục cho Kiến trúc ARM . Tôi đã tìm thấy câu trích dẫn này: "Loại hỗn hợp lớn hơn 4 byte hoặc 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 một địa chỉ được truyền dưới dạng đố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 bất kỳ lúc nào trong quá trình gọi hàm. " Điều này ngụ ý rằng truyền vào r0 là hành vi đúng vì đối số phụ ngụ ý đối số đầu tiên (vì quy ước gọi C không có cách nào để chỉ định số đối số). Tôi tự hỏi nếu CLR đang nhầm lẫn điều này với một quy tắc khác về cơ bản Kiểu dữ liệu 64-bit: "Kiểu Dữ liệu Cơ bản có kích thước từ kép (ví dụ: vectơ dài dài, kép và vectơ chứa 64-bit) được trả về trong r0 và r1."

CHỈNH SỬA 8 Được rồi, có rất nhiều bằng chứng chỉ ra CLR đang làm sai ở đây, vì vậy tôi đã gửi một báo cáo lỗi . Tôi hy vọng ai đó nhận thấy nó giữa tất cả các bot tự động đăng vấn đề trên repo đó: -S.


1
Nhận xét không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Andy

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

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 công cụ JIT" (tôi giả hầu hết mọi người đến đây để upvote bởi vì họ quan tâm đến việc giải quyết các lỗi)
borrrden

âm thanh như vấn đề lớn và nhỏ Ấn Độ ... stackoverflow.com/questions/217980/...
Proxytype

Câu hỏi này có thể được đóng lại vì nó có vẻ là một lỗi?
huysentruitw 13/118

Câu trả lời:


1

Vấn đề tôi nộp lên GH đã được đặt ở đó một thời gian. 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ó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.