记忆中的d?

Jer*_*wen 28 c dynamic-linking ld dlopen

我正在寻找一种直接从内存加载生成的目标代码的方法.

我知道如果我将它写入文件,我可以调用dlopen动态加载其符号并链接它们.然而,考虑到它从内存开始,写入磁盘,然后由dlopen重新加载到内存中,这似乎有点迂回.我想知道是否有某种方法来动态链接存在于内存中的目标代码.据我所知,可能有几种不同的方法来做到这一点:

  1. 尽管它永远不会留下记忆,但我还是认为你的记忆位置是一个文件.

  2. 找一些其他的系统调用来完成我正在寻找的东西(我认为这不存在).

  3. 找到一些动态链接库,它可以直接在内存中链接代码.显然,这个有点难以谷歌,因为"动态链接库"会显示有关如何动态链接库的信息,而不是关于执行动态链接任务的库的信息.

  4. 从链接器中提取一些API并在其代码库中创建一个新库.(显然这对我来说是最不可取的选择).

那么这些可能是哪些?可行?你能指出我假设存在的任何事情吗?还有另一种我甚至没想过的方法吗?

Par*_*eta 11

我需要一个解决方案,因为我有一个没有文件系统的脚本系统(使用数据库中的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;
}
Run Code Online (Sandbox Code Playgroud)

显然,代码缺少任何类型的错误检查等,但这是核心功能.

ETA:我最初的假设fdlopen是POSIX错了,这似乎是一个FreeBSD主义.

  • @yugr你的建议正是提问者已经驳回的小例子。 (2认同)
  • @yugr `/run/shm` 不是 POSIX,它是 Linux 主义,没有它,函数就会退回到只写到 `/tmp`。无论文件是否写入磁盘(“/tmp”在某些系统上可能是 ramdisk),您仍然必须与文件系统交互,拥有创建它的权限,控制其他人是否可以访问它,确保取消链接它当你完成(或崩溃)时正确地进行。您为什么不发布您的提案的答案并让人们对其进行评论和投票呢? (2认同)

R..*_*R.. 10

我不明白你为什么要考虑dlopen,因为这需要更多的非便携代码才能在磁盘(例如ELF)上生成正确的对象格式以进行加载.如果您已经知道如何为您的体系结构生成机器代码,只需将mmap内存PROT_READ|PROT_WRITE|PROT_EXEC和代码放在那里,然后将地址分配给函数指针并调用它.非常简单.

  • 如果开发人员不只几个,这似乎不是一个很好的方法。另外,您注入的代码不需要解析自己的函数指针,并且是 PIC 等吗?看起来就像编译一个 .so 然后能够`dlopen`它会好得多。 (3认同)

gps*_*gps 8

我们在 Google 实现了一种方法来做到这一点。不幸的是上游 glibc 未能理解这一需求,因此它从未被接受。带有补丁的功能请求已停止。它被称为dlopen_from_offset.

dlopen_with_offset glibc 代码可在 glibc google/grte* 分支中找到。但没有人应该喜欢修改自己的 glibc。


Jon*_*ler 7

除了写出文件然后再次加载文件之外,没有标准的方法可以做到这一点dlopen().

您可以在当前的特定平台上找到一些替代方法.由您决定是否比使用"标准和(相对)便携式"方法更好.

由于首先生成目标代码是特定于平台的,因此对其他特定于平台的技术可能并不重要.但这是一个判断调用 - 无论如何取决于存在一种相对不太可能的非标准技术.

  • @Simon:如果要加载的代码不需要调用任何其他函数(完全自包含),则可以直接使用`mmap()`,它可能会起作用。如果要加载的代码调用其他函数,则必须通过某种方法或其他方法解析这些符号的地址。这通常由`dlopen()` 为您完成。例如,如果您将 `dlopen()` 短路,那么作为代码创建者,您有责任确保您已将 ASLR 考虑在内,并在代码中的正确位置拥有正确的函数地址。 (2认同)
  • 需要注意的一个小“陷阱”:在 Linux 上,我发现如果我想要一个程序写出一个 .so,则 dlopen 它,从中 dlsym,然后写出另一个 .so,dlopen 它,然后从 dlsym那么两个 .so 文件名必须不同。 (2认同)