iOS 构建中的非法操作码

Illegal opcode in iOS build

以下代码(从大型项目中最小化)在使用 XCode 7.3.1、Boost 1.61 for iOS 构建时导致 EXC_BAD_INSTRUCTION 崩溃:

main.mm:

#include "stdio.h"
#include "boost/lockfree/queue.hpp"

int main(int argc, char * argv[]) {
    printf("Test1 in\n");
    boost::lockfree::queue<int*> q(100);
    printf("Test1 out\n");
    return 0;
}

堆栈跟踪似乎告诉我,问题出在 C++ 原子操作上:

#0  0x0000000100047a78 in std::__1::__atomic_base<boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, false>::store(boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, std::__1::memory_order) [inlined] at /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/atomic:842
#1  0x0000000100047a74 in boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::deallocate_impl_unsafe(boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node*) at /Users/deinzer/src/pipeline.ios/boost/boost/lockfree/detail/freelist.hpp:251
#2  0x00000001000479e8 in boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_stack<std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >(std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> const&, unsigned long) at /Users/deinzer/src/pipeline.ios/boost/boost/lockfree/detail/freelist.hpp:64
#3  0x00000001000478e0 in boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::queue(unsigned long) at /Users/deinzer/src/pipeline.ios/boost/boost/lockfree/queue.hpp:205
#4  0x0000000100047840 in main at /Users/deinzer/src/iostester/lockfreecrash/lockfree_crash/lockfree_crash/main.mm:7
#5  0x00000001821d68b8 in start ()

反汇编输出显示非法操作码:

0x100047a5c <+72>:  mov    x20, x0
0x100047a60 <+76>:  add    x0, sp, #16               ; =16 
0x100047a64 <+80>:  bl     0x100047aac               ; boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>::get_ptr at tagged_ptr_dcas.hpp:78
0x100047a68 <+84>:  mov    x1, x0
0x100047a6c <+88>:  mov    x0, x20
0x100047a70 <+92>:  bl     0x100047aa4               ; boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>::set_ptr at tagged_ptr_dcas.hpp:83
0x100047a74 <+96>:  ldp    x9, x8, [sp]
->  0x100047a78 <+100>: .long  0xc87f7e7f                ; unknown opcode
0x100047a7c <+104>: stxp   w10, x9, x8, [x19]
0x100047a80 <+108>: cbnz   w10, 0x100047a78          ; <+100> [inlined] std::__1::__atomic_base<boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, false>::store(boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, std::__1::memory_order) + 4 at freelist.hpp:251

问题只发生在

代码运行正常,如果

我想知道问题的根本原因。是 clang 问题、std::atomic 还是 boost 错误?可以做些什么来避免这个问题?

您的反汇编程序拒绝确认的指令是 ldxp xzr, xzr, [x19] - 换句话说,加载以准备独占监视器,以便存储独占成功(或者失败并重新启动,如果确实有一些并发内存access,从而保证store的原子性)。由于这是在这种情况下唯一重要的方面,即我们不关心实际加载的数据,它厚颜无耻地使用零寄存器作为目标来简单地丢弃数据并避免分配临时寄存器来加载。

这里的问题是使用 same 寄存器用于负载对的 both 目标在体系结构上是不可预测的。这一定是 Boost 或 Clang 中的错误,具体取决于违规指令是来自某些显式汇编代码还是来自编译器内部实现。从取消选择这些模板,我 认为 它在 std::atomic 中,但由于我的知识在 C++98 之后停止,我不太确定它指向哪里。

引用 the ARMv8 ARM 不可预知行为附录的 ldxp 部分:

If t == t2, then one of the following behaviors must occur:

  • The instruction is UNDEFINED.
  • The instruction executes as a NOP.
  • The instruction performs a load using the specified addressing mode, and the base register [sic] is set to an UNKNOWN value.

很有可能在某些 CPU 中,设计人员选择了第三个选项,此代码最终会按预期工作(并且确实可以进行测试)。然而,Apple 的 CPU 设计师似乎至少在所讨论设备中的任何内核上都采取了第一个选项,因此爆炸。

__atomic_base::store() 实现应该能够巧妙地修复它的方法是简单地重用分配给商店独占状态的暂存寄存器,而不是 xzr 之一,例如ldxp xzr, x10, [x19] 对于这个例子。这应该使指令定义明确而不影响任何其他代码(以下 stxp 将始终无条件覆盖整个寄存器),并且不需要优化器分配额外的寄存器。可以想象编写一个工具来 post 处理编译后的二进制文件,扫描相关指令对并因此修复加载操作数,但更明智的做法可能是只提交适当的错误报告并将其修复在来源 - 事实证明,我怀疑是潜在的优化器问题 has been reported against upstream LLVM already.