如何确保内存分配在某个地址附近?

问题描述 投票:0回答:1

我正在为 x86_64 编写一个 JIT 重新编译器,有时发出的代码需要从编译的二进制文件中调用函数。

由于 ASLR,我的程序的 .text 段被放置在某个随机地址。

我是否可以分配 32 MiB 的 JIT 代码缓存,使其地址始终足够接近 .text 段,以便我可以安全地发出相对调用而不是绝对调用?我在Linux下。

c linux x86-64
1个回答
0
投票

就像评论所说,最好不要依赖相对的调用。

您可以使用

mmap
请求某个地址的内存映射。问题是知道哪些地址是免费的。

这样的东西可以工作:

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

extern char __executable_start[];
extern char __etext[];
extern char _end[];

#define OFFSET (32 * 1024 * 1024)
#define SIZE (32 * 1024 * 1024)

int main()
{
    printf(".text start = %p\n", (void*)__executable_start);
    printf(".text end   = %p\n", (void*)__etext);
    printf("binary end  = %p\n", (void*)_end);
    
    char* buffer = mmap(_end + OFFSET, SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    printf("buffer      = %p\n", buffer);
    printf("distance    = %d\n", buffer - __etext);
}

但是,它并不能“保证”有效。如果请求的区域已经包含映射,内核可能会忽略您的提示(事实上,Linux 似乎这样做)。 如果你真的想这样做,我建议你尝试分配接近

.text

的内存 - 如果成功,则使用相对调用或绝对调用。

上述程序的更好版本是:

#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <sys/mman.h> #include <unistd.h> extern char __executable_start[]; extern char __etext[]; extern char _end[]; #define OFFSET (32 * 1024 * 1024) #define SIZE (32 * 1024 * 1024) #define MAX_ATTEMPTS 10 int main() { printf(".text start = %p\n", (void*)__executable_start); printf(".text end = %p\n", (void*)__etext); printf("end = %p\n", (void*)_end); // align to next page char* buffer = (char*)(((uintptr_t)_end + 4095) & ~4095); int attempts; for (attempts = 0; attempts < MAX_ATTEMPTS; attempts++) { char* result = mmap(buffer, SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0); if (result != MAP_FAILED) { printf("success at attempt #%d\n", attempts + 1); break; } if (errno != EEXIST) { return 1; } buffer += OFFSET; } if (attempts == MAX_ATTEMPTS) { printf("failed after %d attempts, falling back to absolute instructions\n", attempts); buffer = mmap(NULL, SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } printf("buffer = %p\n", buffer); printf("distance = %d\n", buffer - __etext); }

现在,我们告诉内核我们希望映射到一个确切的地址。如果那里已经存在映射,我们会尝试另一个地址。如果失败一定次数,我们就可以退回到绝对指令。

或者,您可以解析

/proc/self/maps

并找到靠近

.text
的空闲区域。
但是,首先,我会尝试对绝对调用是否实际上比相对调用慢进行基准测试。您可能不需要关心。

© www.soinside.com 2019 - 2024. All rights reserved.