在 linux 中如何在不使用 libc 的情况下在堆上分配内存

How do you allocate memory on the heap without using libc in linux

我试图在不使用 libc 和使用 linux 系统调用的情况下在堆上分配内存。我试过使用 mmap 和 brk,但是 brk 不像我读过的大多数系统那样 return 堆的末尾,sbrk 不会工作,因为它不作为系统调用存在,而 mmap只会导致段错误。

_start.c

#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define MAP_PRIVATE 0x2
#define MAP_ANONYMOUS 0x20

extern void *mmap(void *addr, unsigned long sz, int prot, int mode, int fd, unsigned long offset);
extern void  exit(int exit_code);

int _start()
{
    void *mem = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    *(int*)mem = 4;

    exit(*(int*)mem);
}

我尝试这样做的原因是因为我正在研究一个替代 libc(如果我不知道如何做,显然不是一个称职的人,它主要是一个学习 exercise/fun 项目)我需要弄清楚如何在堆上实际分配。我找了一段时间,但我仍然不知道它是如何工作的。

syscalls.s

    .text
    .global mmap
mmap:
    mov , %rax
    syscall
    ret

    .global exit
exit:
    mov , %rax
    syscall
    ret

我使用的编译命令是gcc -nostdlib _start.c syscalls.s.

就像我说的,我是 运行 Linux。具体来说:Ubuntu 20.04 LTS with kernel 5.11.0-43-generic.

好吧,这是使用 strace 和调试器的好机会。来自 man 2 syscall:

   Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
   ──────────────────────────────────────────────────────────────
   x86-64        rdi   rsi   rdx   r10   r8    r9    -

gdb a.out:

(gdb) b syscalls.s:5
Breakpoint 1 at 0x1050: file syscalls.s, line 5.
(gdb) r
Starting program: /dev/shm/.1000.home.tmp.dir/a.out 

Breakpoint 1, mmap () at syscalls.s:5
5           syscall
(gdb) info registers
rax            0x9                 9
rbx            0x0                 0
rcx            0x22                34                # here it is
rdx            0x3                 3
rsi            0x1000              4096
rdi            0x0                 0
rbp            0x7fffffffd968      0x7fffffffd968
rsp            0x7fffffffd950      0x7fffffffd950
r8             0xffffffffffffffff  -1
r9             0x0                 0
r10            0x555555554000      93824992231424     # WRONG!
r11            0x206               518
r12            0x555555555000      93824992235520
r13            0x7fffffffd970      140737488345456
r14            0x0                 0
r15            0x0                 0
rip            0x555555555050      0x555555555050 <mmap+7>
eflags         0x202               [ IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0

我们看到 r10 中的值是一些垃圾,而 0x22rcx 中。咨询https://uclibc.org/docs/psABI-x86_64.pdf.

你必须做 https://github.com/numactl/numactl/blob/master/syscall.c#L160 一些旋转。 mov %rcx, %r10 就够了。

总体而言,使用 https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/x86_64/syscall.S#L29 .