c++ - 可变参数函数 : Bad access with va_arg at iOS arm64

标签 c++ ios unity3d

我有这样一个函数:

typedef long long myint64;

typedef enum {
    INT32_FIELD,
    CHARP_FIELD,
    INT64_FIELD,
} InfoType;

int32_t ReadInfo(void *handle, InfoType info, ...)
{
    va_list arg;
    va_start(arg, info);
    void *argPtr = va_arg(arg, void*);
    va_end(arg);

    int32_t ret = 0;
    int32_t *paramInt = NULL;
    char **paramCharp = NULL;
    myint64 *paramInt64 = NULL;

    switch (info) {
    case INT32_FIELD:
        paramInt = static_cast<int32_t*>(argPtr);
        *paramInt = functionWhichReturnsInt32();
        break;
    case CHARP_FIELD:
        paramCharp = static_cast<char**>(argPtr);
        *paramCharp = functionWhichReturnsCharPtr();
        break;
    case INT64_FIELD:
        paramInt64 = static_cast<myint64*>(argPtr);
        *paramInt64 = functionWhichReturnsInt64();
        break;
     default:
        ret = -1;
        break;
    }
    return ret;
}

从单独的 c 文件中调用此函数。此文件不包含 ReadInfo 函数的定义:

extern "C" {int32_t CDECL ReadInfo(intptr_t, int32_t, int32_t*);}

int32_t readInt()
{
    int32_t value = 0;
    int32_t *ptr = &value;
    ReadInfo(handle, INT32_FIELD, ptr);
    return value;
}

此调用仅在 iOS arm64 下失败。 arm7s 和 win32 可以很好地处理这个调用。 (是的,我们唯一的 64 位目标平台是 iOS arm64。) 在调试器中,我发现 readInt 函数中 ptr 的地址与我得到的不同: void argPtr = va_arg(arg, void);

我对 arg_list 的处理有误吗?

附言它不是一个普通的 Objective C 应用程序。它是 native Unity 插件的一部分。但在 iOS 中,Unity 代码只是从 C# 转换为 Objective C/C++。这就是为什么您可以看到第二个声明:

extern "C" {int32_t CDECL ReadInfo(intptr_t, int32_t, int32_t*);}

最佳答案

这不是 IL2CPP 的问题,而是 iOS 或编译器的问题。

即使在最新的 Xcode (10.1) 和 iOS (12.1) 上,以下代码也可以重现该问题

typedef int __cdecl (*PInvokeFunc) (const char*, int);

int test()
{
    PInvokeFunc fp = (PInvokeFunc)printf;
    fp("Hello World: %d", 10);
    return 0;
}

预期的输出是:Hello World: 10 但它会给出 Hello World: ??? (随机数) 但是在 iOS 上。

我在 macOS 和 Linux 上尝试了相同的代码,它们都运行良好。

我不确定它是否与 Apple 文档有关:

Variadic Functions The iOS ABI for functions that take a variable number of arguments is entirely different from the generic version.

Stages A and B of the generic procedure call standard are performed as usual—in particular, even variadic aggregates larger than 16 bytes are passed via a reference to temporary memory allocated by the caller. After that, the fixed arguments are allocated to registers and stack slots as usual in iOS.

The NSRN is then rounded up to the next multiple of 8 bytes, and each variadic argument is assigned to the appropriate number of 8-byte stack slots.

The C language requires arguments smaller than int to be promoted before a call, but beyond that, unused bytes on the stack are not specified by this ABI.

As a result of this change, the type va_list is an alias for char * rather than for the struct type specified in the generic PCS. It is also not in the std namespace when compiling C++ code.

https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html


更新:

给苹果工程师的回复:

Casting function pointers to add a different calling convention doesn’t change how the callee is represented, it only changes how the caller performs its call. printf already has a calling convention, and what you’re doing might happen to work for some combinations on some platforms, while not working on others. You want to declare a wrapper function instead, which has the desired calling convention, and which calls the function you want. You’ll need to marshal the arguments manually.

也就是说可变参数函数不能被直接p/invoke,除非IL2CPP为它生成包装函数。只有一个函数指针是不够的。

关于c++ - 可变参数函数 : Bad access with va_arg at iOS arm64,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35536515/

相关文章:

ios - 无法在 iOS 中创建新的标签栏 Controller

ios - 验证后未从所有 View 中删除 View

c++ - 在 V8 中使用访问器的问题

c++ - 划分忽略休息的有效方法

iphone - 如何将带有参数的 block 传递给方法

unity3d - unity安装AR foundation和ar core有问题?

c# - 在C#中收集X项时使用SpeedBoost

unity3d - Unity 分析器 Device.present

c++ - 使用 STL 算法合并 2 个 vector

c++ - clang 没有看到通过 typedef 引入的基类构造函数