可以安全地将 objc_msgsend 转换为可变长度参数函数

标签 c objective-c objective-c-runtime

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

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

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

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

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

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

最佳答案

底线:

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

请,请,请使用 NSInvocation 它为您解决了大约 99% 的问题,同时更加安全。可能存在它无法解决的边缘情况(例如,参数列表中的 SSE/AVX vector ),但它会比您可能一起破解的内容实现跨越式发展。

让我们按架构对其进行分解,看看它可能在哪里起作用。

通用

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

  1. 当作为 va_args 传递时, float 总是被放大为 double 值。参见 the C standard ,第 6.5.2.2 节

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

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

  4. 如果您开始接触返回值,事情会变得更加复杂,并且您需要了解架构何时将结构打包到寄存器中以获得返回值(请参阅 a lot of 之前的讨论) .

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这是你的 friend 。

ARM64

这是我的知识最不稳定的地方,如果这些信息中有任何不正确的地方,我深表歉意。

大部分函数调用都是完全通过寄存器来执行的,所以我们之前所有的担心都还在。现在有新的 FPU 寄存器,您必须为您的 float 处理,但如果您已经达到这一点,就没有什么是不可逾越的了。

关于可以安全地将 objc_msgsend 转换为可变长度参数函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55841366/

相关文章:

c - 休眠 N 秒并等待按键

c - 读取编译错误报告格式: format_io. c:3:10:

ios - 保护 iOS 应用程序免受运行时 Hook

iphone - iOS Facebook 登录但无法连接?

iphone - 将导航 Controller 添加到 UIViewController

objective-c - 在 ARC 下,使用运行时方法分配给对象类型的 ivar 是否合法/安全?

objective-c - OBJC_ASSOCIATION_ASSIGN 是原子的还是非原子的?

puncte 的冲突类型

c - 了解代码示例是受 CPU 限制还是受内存限制

iphone - 什么时候触发自动释放池