是否可以将函数加载到某个已分配的内存中并从那里运行它?

时间:2010-08-25 20:17:04

标签: c++ interprocess

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

类似的东西:

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

我意识到你不能接受这个功能的大小,但这就是我脑海里浮现的。

7 个答案:

答案 0 :(得分:4)

理论上,由于函数只是内存中某个字节代码的序列,你可以复制函数的内存块并调用(跳转)它。虽然c ++提取了这种可能性,正如你所注意到的,我们实际上无法知道函数的大小(尽管我们可以得到它的指针)。

仍然有图书馆。例如,您可以告诉远程可执行文件从动态库加载特定函数并执行它。检查wikipedia-article是否有参考文献。

答案 1 :(得分:4)

这很有趣。
但似乎你可以。虽然我会从不这样做:

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

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

另请注意,功能类型非常有限:

在这个例子中,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;

}

答案 2 :(得分:2)

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

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

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

希望有所帮助。

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

是。类似的技术由Just-In-Time代码生成器(例如Java VM)使用。实际上,您几乎可以说操作系统的运行时加载器和链接器正在为您执行此操作,因为它会将动态库加载到您的进程中。

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

答案 5 :(得分:0)

如果生成代码字节并将其注入进程,则称为运行时代码生成(RTCG)。您可以look up some examples

现代内核会阻止它在非特权级别工作, 所以你必须先进入正确的模式或响铃。为了 找到代码大小,你当然要计算字节数 在最后一个返回代码之前运行它的代码段。

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

答案 6 :(得分:-1)

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

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

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