【问题标题】:Does the C Standard Allow for Self-Modifying Code?C 标准是否允许自修改代码?
【发布时间】:2011-09-17 22:42:03
【问题描述】:

是否可以在 C 中以可移植的方式进行自修改代码?

我问的原因是,在某种程度上,OOP 依赖于自修改代码(因为在运行时执行的代码实际上是作为数据生成的,例如在 v-table 中),然而,看起来如果这样做太过分了,它会阻止编译器中的大多数优化。

例如:

void add(char *restrict p, char *restrict pAddend, int len)
{
    for (int i = 0; i < len; i++)
        p[i] += *pAddend;
}

优化编译器可以将*pAddend 提升出循环,因为它不会干扰p。但是,这不再是自修改代码中的有效优化

这样看来,C 语言似乎不允许自我修改代码,但同时,这是否意味着您不能在 C 语言中做一些诸如 OOP 之类的事情? C 真的支持自修改代码吗?

【问题讨论】:

  • C 具有函数指针,这是构建运行时调度机制所需的全部内容。您不需要“自修改代码”。
  • 我不会说(比如)C++ 中的 OOP 实现使用自修改代码,在我看来,它只是数据驱动的代码。虚函数在概念上与简单的switch 没有什么不同。
  • @Nemo:那不还是一种自修改代码吗?
  • @Mehrdad:“自修改代码”通常是指更改实际的机器指令,但这实际上是一个定义问题。例如,您给出的优化示例 is 在存在函数指针的情况下有效。那么“自修改代码”到底是什么意思?
  • @Mehrdad:你在说自相矛盾的事情。 “我的示例中断了自修改代码”+“函数指针是自修改代码的一种形式”=您实际上并不知道自修改代码是什么意思。

标签: c self-modifying


【解决方案1】:

在 C 中无法进行自修改代码的原因有很多,其中最重要的是:

  1. 编译器生成的代码完全取决于编译器,可能看起来不像程序员试图编写修改自身的代码所期望的那样。这完全是 SMC 的基本问题,而不仅仅是可移植性问题。
  2. 函数和数据指针在 C 中是完全分开的;该语言无法在它们之间来回转换。这个问题不是根本问题,因为某些实现或更高级别的标准 (POSIX) 保证代码和数据指针共享一个表示。

除此之外,自我修改代码只是一个非常糟糕的主意。 20 年前它可能有一些用途,但现在它只会导致错误、糟糕的性能和可移植性失败。请注意,在某些 ISA 上,指令缓存是否甚至可以看到对缓存代码所做的更改可能是未指定/不可预测的!

最后,vtables 与自修改代码无关。这纯粹是修改函数指针的问题,它们是数据,而不是代码。

【讨论】:

  • +1 你的最后一句话是关键。出于某种原因,我认为像jmp EAX 这样的间接指令会在EAX 发生变化时自行修改……愚蠢的思维错误。感谢您的回答。
  • 这不是真的。查看 POSIX 和 WinAPI 上的页面保护机制。没有什么能阻止您(除了 iOS 内核中的代码签名和类似的代码)在运行时生成机器代码、将页面保护标志设置为 EXEC 并使用 C 风格的函数指针将控制权传递给它。
【解决方案2】:

严格来说,如果我正确理解标准,自修改代码不能以可移植的方式在 C 或 C++ 中实现。

在 C/C++ 中自我修改代码的含义如下:

uint8_t code_buffer[FUNCTION_SIZE];
void call_function(void)
{
   ... modify code_buffer here to the machine code we'd like to run.
   ((void (*)(void))code_buffer)();
}

这是不合法的,并且会在大多数现代架构上崩溃。这在哈佛架构上是不可能实现的,因为可执行代码是严格只读的,所以它不能成为任何标准的一部分。

大多数现代操作系统确实具有能够进行这种黑客攻击的工具,动态重新编译器会使用这种工具。以 Unix 中的 mprotect() 为例。

【讨论】:

  • self 修改代码又是另一个来自编写“新鲜”代码并执行它的代码的鱼。正如 Mehrdad 所观察到的,如果您一开始不知道代码是如何生成/优化的,那么修改编译器生成的代码是非常困难的,因为机器指令不一定与 AST 有任何特别明显的关系。
  • 虽然您不能编写“自修改 C”,但 C 编译器当然有可能发出“自修改机器代码”。我认为这个问题同时考虑到了两者,我不知道 OP 是否同时考虑了两者,是否有任何 C 编译器做过我也不知道。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
  • 2019-02-04
  • 2012-08-25
  • 2022-01-02
  • 1970-01-01
  • 2018-05-28
相关资源
最近更新 更多