4

我正在处理一些进程间通信的东西,我很好奇是否可以将一个函数复制到一些共享内存中并从任何一个进程中运行它。

就像是:

memcpy(shared_memory_address, &func, &func + sizeof(func));

我意识到你不能接受函数的大小,但这就是我想到的。

4

7 回答 7

4

从理论上讲,由于函数只是内存中某处的字节码序列,因此您可以复制函数的内存块并调用(跳转)它。尽管 c++ 抽象出这种可能性,但正如您所注意到的,我们实际上无法知道函数的大小(尽管我们可以获得指向它的指针)。

不过,还有图书馆。例如,您可以告诉远程可执行文件从动态库加载特定函数并执行它。检查wikipedia-article以获取参考资料。

于 2010-08-25T20:31:35.013 回答
4

那很有趣。
但看起来你可以。虽然我永远不会这样做:

在运行 Windows 7 的 lenovo:T61p 上编译:使用 g++ 4.3.4

我会注意到某些类型的硬件会阻止这种情况,因为您只能从特定内存区域(程序区域)执行代码,该内存区域在硬件内存映射文件中标记为只读(以防止自我修改代码)。

另请注意,函数的类型非常有限:

在这个例子中 func() 做的很少,因此可以工作。
但是,如果您执行以下任何操作,它将无法移植到其他进程:

  • 调用函数或方法。
  • 传递一个指针(或引用)
    • 包含指针或引用的对象也不会起作用。
  • 使用全局变量。
  • 您可以传递一个方法指针:
    • 但是使用它的对象必须按值传递。

以上都不起作用,因为一个进程的地址空间与另一个进程的地址空间没有相似之处(因为它在硬件级别映射到物理内存)。

愚蠢的例子

#include <vector>
#include <iostream>
#include <string.h>

int func(int x)
{
    return x+1;
}

typedef int (*FUNC)(int);


int main()
{
    std::vector<char>   buffer(5000);

    ::memcpy(&buffer[0],reinterpret_cast<char*>(&func),5000);

    FUNC func   = reinterpret_cast<FUNC>(&buffer[0]);

    int result  = (*func)(5);

    std::cout << result << std::endl;

}
于 2010-08-25T20:52:45.693 回答
2

上次我尝试这个时,我遇到了一个障碍:确定函数中的字节数。任务是使用函数的地址,将字节复制到内存中(假设代码被编译为位置无关代码,PIC)。

一种更独立于平台的方法是查看编译器文档以查看是否有#pragma编译器选项或关键字允许您指定要在加载时加载的函数地址或段。

另外,搜索嵌入式系统组,因为这是一种流行的技术:将闪存编程到 RAM 中的代码加载,在 RAM 中执行功能,然后重置系统。

希望有帮助。

编辑:
建议:使用汇编语言文件或链接器指令(在构建脚本中)创建数据或代码段。将您的函数放入单独的代码文件中。告诉编译器和链接器将此函数编译到新的代码段中。可能有编译器特定的语句来获取段的起始地址和大小。此外,操作系统可能能够为您加载给定地址的段。

还可以在操作系统的帮助下查看可以在运行时加载的 DLL 或共享库。

于 2010-08-25T20:36:11.120 回答
1

如果你尝试这样的事情,你可能会遇到从内存中运行不应该包含可执行代码的代码的问题。有关更多信息,请参阅此 Wikipedia 文章:http ://en.wikipedia.org/wiki/Executable_space_protection

于 2010-08-25T20:36:15.820 回答
1

是的。Java VM 等即时代码生成器使用了类似的技术。事实上,您几乎可以说操作系统的运行时加载程序和链接器正在为您执行此操作,因为它将动态库加载到您的进程中。

但是,您确实必须从操作系统请求可执行内存。并且您要跳入的代码必须以允许其位于内存中的任何位置(与位置无关)的方式编写。

于 2010-08-25T20:38:45.190 回答
0

如果您生成代码字节并将其注入到进程中,则称为 运行时代码生成 (RTCG)。你可以 一些例子

现代内核会阻止它从非特权级别工作,因此您必须先进入正确的模式或响铃。为了找到代码大小,您(当然)必须计算函数的字节数,直到最后一个返回码为止。

Afaik 图形驱动程序有时会在为运行中的光栅操作创建代码时使用 RTCG(问题依赖)。

于 2010-08-25T20:45:43.190 回答
-1

您可以合理地假设这在 Linux、Windows 或更复杂的嵌入式操作系统上是完全不可能的。

但是,如果您没有使用这种讨厌的限制,您可以在程序集中添加一些表示函数开始/结束的保护字节,并使用它们来帮助您将内容复制到共享内存(当然使用程序集),然后发布任何感兴趣的进程的过程地址列表(也可以使用程序集访问/运行)。

当然,有一个定义良好的机制为多个进程提供代码库,Linux 和 Windows 提供的动态库系统。可能不像你想要的那样灵活。:-)

于 2010-08-25T20:40:10.800 回答