我正在寻找一种直接从内存加载生成的目标代码的方法。
据我所知,如果我将其写入文件,我可以调用dlopen动态加载其符号并链接它们。然而,考虑到它从内存开始,写入磁盘,然后由dlopen重新加载到内存中,这似乎有点迂回。我想知道是否有某种方法来动态链接存在于内存中的目标代码。据我所知,可能有几种不同的方法可以做到这一点:
欺骗dlopen认为你的记忆位置是一个文件,即使它永远不会留下记忆。
找一些其他系统调用来完成我正在寻找的东西(我认为这不存在)。
找到一些动态链接库,它可以直接在内存中链接代码。显然,这个有点难以谷歌,因为“动态链接库”会显示有关如何动态链接库的信息,而不是执行动态链接任务的库。
从链接器中提取一些API并在其代码库中创建一个新库。 (显然这对我来说是最不可取的选择)。
那么哪些是可能的?可行?你能指出我假设存在的任何事情吗?还有另一种我甚至没想过的方法吗?
答案 0 :(得分:10)
我不明白为什么你会考虑dlopen
,因为那将需要更多的非便携代码来在磁盘上生成正确的对象格式(例如ELF)以进行加载。如果您已经知道如何为您的体系结构生成机器代码,只需mmap
内存PROT_READ|PROT_WRITE|PROT_EXEC
并将代码放在那里,然后将地址分配给函数指针并调用它。非常简单。
答案 1 :(得分:9)
我需要一个解决方案,因为我有一个没有文件系统的脚本系统(使用数据库中的blob),需要加载二进制插件来支持一些脚本。这是我提出的可以在FreeBSD上运行的解决方案,但可能无法移植。
void *dlblob(const void *blob, size_t len) {
/* Create shared-memory file descriptor */
int fd = shm_open(SHM_ANON, O_RDWR, 0);
ftruncate(fd, len);
/* MemMap file descriptor, and load data */
void *mem = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(mem, blob, len);
munmap(mem, len);
/* Open Dynamic Library from SHM file descriptor */
void *so = fdlopen(fd,RTLD_LAZY);
close(fd);
return so;
}
显然,代码缺少任何类型的错误检查等,但这是核心功能。
ETA:我最初假设fdlopen
是POSIX错误,这似乎是一个FreeBSD主义。
答案 2 :(得分:7)
除了写出文件然后再次使用dlopen()
加载文件外,没有其他标准方法可以执行此操作。
您可以在当前的特定平台上找到一些替代方法。由您决定是否比使用“标准和(相对)便携式”方法更好。
由于首先生成目标代码是特定于平台的,因此对其他特定于平台的技术可能无关紧要。但这是一个判断调用 - 无论如何都取决于存在一种相对不太可能的非标准技术。
答案 3 :(得分:2)
你不需要加载在内存中生成的代码,因为它已经在内存中了!
但是,您可以 - 以非可移植的方式 - 在内存中生成机器代码(前提是它位于内存段 mmap -ed带有PROT_EXEC
标志)。
(在这种情况下,不需要“链接”或重定位步骤,因为您生成具有确定绝对或相对地址的机器代码,特别是调用外部函数)
有些库存在这样做:在 x86 或 x86-64 下的GNU / Linux上,我知道GNU Lightning(生成快速的机器代码,运行缓慢),DotGNU LibJIT(生成中等质量代码)和LLVM& GCCJIT(它能够在内存中生成相当优化的代码,但需要时间来发出它)。 LuaJit也有类似的设施。自2015年以来,GCC 5有一个gccjit库。
当然,您仍然可以在文件中生成C代码,派生编译器将其编译为共享对象,然后dlopen该共享对象文件。我在GCC MELT 中这样做,这是一种扩展GCC的领域特定语言。它在实践中确实很有效。
如果要编写生成的C文件的性能是一个问题(它不应该,因为编译C文件要慢于编写它)考虑使用一些tmpfs文件系统(可能在{{1这通常是Linux上的 tmpfs 文件系统)