在代码执行时将未对齐写入机器代码中的立即操作数是否安全?

Is it safe to write unaligned to an immediate operand in machine code while that code is executing?

假设我有这样的 x86-64 代码(尽管这个问题更普遍地适用于所有代码):

mov rbx,7F0140E5247Dh
jmp rbx

如果目标值未对齐,覆盖目标常量是否安全,而该代码可能正在执行?换句话说,我是否可以观察到部分更新的跳转目标,从而导致跳转到不存在的地址?另外,如果目标常量跨越页面或缓存行边界,这是否安全?

编辑:

我只对更改单个指令感兴趣,对更改指令边界位置不感兴趣。

仅当写入是原子的时,这是保证 ,但在 AMD 上不保证。 lowest-common-denominator 原子性保证是 8 字节对齐的存储是原子的,仅此而已。

用一个xchg做一个guaranteed-atomic RMW。如果常量本身跨越 cache-line 边界,那将非常缓慢,但我相信是正确的。 (总线锁,不仅仅是高速缓存锁;太慢了,甚至只有 split-lock 都有一个性能计数器,甚至还有一个 CPU 功能,至少在内核代码中会造成该错误,因此您可以找到实例它在 VM 中。)如果常量不跨越有问题的边界,无论 CPU 这是什么,它应该与对齐的原子操作一样快。

或者,如果您的 CPU 支持 AVX,则 16 字节对齐的 SSE/AVX 存储在具有 AVX 的 CPU 上保证是原子的。 (直到最近几年才知道这在实践中基本上是安全的,但幸运的是它对所有 AVX CPUs 都有追溯力,没有新的 feature-bit。)所以如果你能让你的常量排队为了不跨越 16 字节边界,您可以那样更新它。 (用自己覆盖周围的字节不会导致问题,除非另一个线程也在附近更新另一个常量。)

如果性能对此很重要(例如,大约每分钟执行一次以上),可能值得使用一些填充或 NOP 来使常量 8 字节对齐,尤其是如果您可以 不需要实际的 NOP,甚至不需要 mov r64,imm64 本身。 (虽然它是 10 个字节,一条指令的最大长度是 15。)


不会完全概括为替换多条指令

在其他情况下,您可能要重写指令序列,其中一个指令边界在不同的地方,那就另当别论了。您说这个问题“更普遍”适用,但仅适用于替换立即数或用相同长度的指令替换整个 4 字节或 8 字节指令。如果另一个线程可能在您正在写入的区域内休眠或 运行 RIP,您必须考虑更新后来自旧序列的任何可能 RIP 的 code-fetch 的情况。所以正如我所说,改变指令边界是有问题的。

但是如果您遵守该限制,cross-modifying 代码是 AFAIK 安全的。我认为 Windows hot-patching 会暂停其他可能是 运行 代码的线程,但我不知道为什么,因为它已经确保有一个 large-enough 指令可以覆盖.它们要么是 over-cautious,要么存在一些我不知道 code-fetch 不尊重存储原子性的风险。也许只是他们不想在未对齐函数的情况下依赖 2 字节存储原子性,甚至认为这是正常编译器设置的默认设置。