是否可以在标准C中执行堆栈中的代码?

Bla*_*iev 17 c stack executable

以下代码不能按预期工作,但希望说明我的尝试:

long foo (int a, int b) {
  return a + b;
}

void call_foo_from_stack (void) {
  /* reserve space on the stack to store foo's code */
  char code[sizeof(*foo)];

  /* have a pointer to the beginning of the code */
  long (*fooptr)(int, int) = (long (*)(int, int)) code;

  /* copy foo's code to the stack */
  memcpy(code, foo, sizeof(*foo));

  /* execute foo from the stack */
  fooptr(3, 5);
}
Run Code Online (Sandbox Code Playgroud)

显然,sizeof(*foo)不返回foo()函数代码的大小.

我知道在某些CPU上执行堆栈是受限制的(或者至少在设置了限制标志的情况下).除了最终可以存储在堆栈中的GCC嵌套函数之外,还有一种方法可以在标准C中实现吗?

RBe*_*eig 10

这种事情的一个有效用例是嵌入式系统,它通常用完FLASH存储器,但需要能够在现场重新编程.要做到这一点,代码的一部分必须从其他一些存储设备运行(在我的情况下,FLASH设备本身不能擦除和编程一个页面,同时允许从任何其他页面读取,但有些设备可以做到这一点),并且系统中有足够的RAM来保存闪存写入器和要写入的新应用程序映像.

我们在C中编写了必要的FLASH编程函数,但是使用了#pragma指令将它放在.text与其余代码不同的段中.在链接器控制文件中,我们让链接器为该段的开始和结束定义全局符号,并将其放在RAM中的基址,同时将生成的代码放在FLASH中的加载区中..data段的初始化数据和纯只读.rodata段; 计算FLASH中的基址并将其定义为全局符号.

在运行时,当执行应用程序更新功能时,我们将新的应用程序映像读入其缓冲区(并进行了所有应该进行的完整性检查,以确保它实际上是该设备的应用程序映像).然后,我们将更新内核从FLASH中的休眠位置​​复制到其在RAM中的链接位置(使用链接器定义的全局符号),然后像任何其他函数一样调用它.我们不必在调用站点做任何特殊操作(甚至不是函数指针),因为就链接器而言,它始终位于RAM中.在正常操作期间,特定RAM具有非常不同的目的对于链接器而言并不重要.

也就是说,使这成为可能的所有机制要么超出标准的范围,要么坚定地实现定义的行为.标准不关心代码在执行之前如何加载到内存中.它只是说系统可以执行代码.


Dan*_*idy 9

sizeof(*foo)它不是函数的大小foo,它是指向 foo 的指针的大小(通常与平台上的每个其他指针的大小相同).

sizeof无法测量函数的大小.原因是它sizeof是一个静态运算符,并且在编译时不知道函数的大小.

由于函数的大小在编译时是未知的,这也意味着您无法定义足够大的静态大小数组来包含函数.

你可能能够做一些可怕的使用alloca和一些讨厌的黑客,但简短的答案是否定的,我不认为你可以用标准C做到这一点.

还应注意,堆栈不能在现代的安全操作系统上执行.在某些情况下,您可能能够将其设置为可执行文件,但这是一个非常糟糕的主意,它会使您的程序对堆栈粉碎攻击和可怕的错误保持开放.