【问题标题】:could cast objc_msgsend to variable-length arguments function safely可以安全地将 objc_msgsend 转换为可变长度参数函数
【发布时间】:2019-09-14 10:02:54
【问题描述】:

假设我在运行时得到一个对象和它的一个选择器,我打算安全地调用它,所以我定义了

#define objc_msgsend_va ((void (*)(id, SEL, ...))objc_msgsend)

#define Call_object(obj, sel, ...) objc_msgsend_va(obj, sel, ## __VA_ARGS__)

我能否用 Call_object 宏安全地替换任何方法调用,否则可能会导致某种崩溃。

这是为了定义一个宏来在 iOS 设备上运行任意运行时方法,我无法为每个方法显式转换为正确的函数类型,所以我使用了可变长度参数。

主要考虑的是这样使用宏的安全性。

【问题讨论】:

    标签: c objective-c objective-c-runtime


    【解决方案1】:

    底线:

    不,这绝对不安全。它适用于很多常见情况,但 va_args 与将参数直接传递给函数不同。

    请,请,请,请使用NSInvocation它为您解决了大约 99% 的问题,同时更安全。可能存在它无法解决的极端情况(例如,参数列表中的 SSE/AVX 向量),但它会比你可能一起破解的问题要突飞猛进。

    让我们按架构分解,看看这可能可能在哪里工作。

    通用

    这些问题无处不在,没有真正的解决方案。

    1. 当作为 va_args 传递时,浮点数总是放大为双精度数。参见the C standard,第 6.5.2.2 节

    2. shorts 和 chars 也是如此。它们也会变大,通过时会造成很大的痛苦和痛苦。从技术上讲(与浮点数不同,因为它们有时在特殊的 FPU 寄存器中传递),对于整数类型,您可以通过将它们包装在单个元素结构中来解决这个问题。这将阻止他们被提升。不过请不要这样做。

    3. 联合,或复杂的结构结构。需要额外的对齐和填充以及注意事项。如果您想要一个稳定的解决方案,您将不得不深入研究编译器代码。

    4. 1234563 .

    PowerPC

    我不介意这个。如果您仍然需要针对这些 macOS 设备,有两件事:

    1. 我为你感到非常难过。

    2. 你可能比我有更多的上下文。祝你好运,祝你好运。

    x86

    32 位 x86 相当健全。没有复杂的寄存器参数传递,我的头顶,我觉得这可能是相当理智的。除了前面提到的问题之外,最大的问题是由 x87 FPU 引起的问题。避免浮动,你可能不会在这里烧毁世界。

    x86-64

    64 位 x86 完全是另一种野兽。您将不得不学习很多关于编译器如何将寄存器分配给参数(和返回值!),当然还有用于浮点的新 SSE 寄存器。

    ARMv6/ARMv7/ARMv7s(32 位 ARM)

    我将把这些放在一起,从这个角度来看没有太大区别。

    与 x86-64 一样,如果您想 100% 安全,您必须了解哪些寄存器在堆栈中的位置,但如果内存调用,浮点传递在这里更简单。

    Apple 的ABI guide 将成为您的朋友。

    ARM64

    这是我的知识最不可靠的地方,如果其中任何信息不正确,我深表歉意。

    大多数函数调用完全通过寄存器执行,因此我们之前的所有担忧仍然存在。现在有新的 FPU 寄存器,你必须处理你的浮点数,但如果你已经到了这一点,没有什么是不可克服的。

    【讨论】:

    • 感谢回答,NSInvocation,与va_list集成,在定义的_C_TYPE的帮助下,确实解决了我的大部分问题,最后可能是我做不到使用 va_args 出列任意大小的类型,GNU 澄清它的实现可能会随着时间而改变。对于这些类型的函数,我只返回 nil,现在它就像 objc_msgSend 和 KVC 包装的功能一样工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 1970-01-01
    相关资源
    最近更新 更多