2

I'm playing with assembly on mac. On linux I implemented realloc by using mmap/mremap/munmap but there doesn't seem to be a mremap on mac. How would I implement realloc using virtual memory in assembly? What system call(s) would I need? I'm targeting M1 but x86-64 solutions are fine

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Cal
  • 121
  • 8
  • The old fashioned way, with `sbrk()`? – Erik Eidt Jun 15 '22 at 21:51
  • 1
    You might try a `mmap` with the address fixed to the end of the current block and the size containing the necessary additional space. If that fails, `mmap` a whole new block, copy the contents of the old, and `munmap` when done. – Jester Jun 15 '22 at 21:51
  • I don't think this system call exists on macOS. You'll have to implement `realloc` by allocating new pages, copying the contents over, and then releasing the old ones. – fuz Jun 15 '22 at 22:20

1 Answers1

1

Using only POSIX mmap flags, the "optimistic" strategy is what Jester suggested, using mmap without MAP_FIXED to try to allocate new pages contiguous with what you already have. (The first arg is a "hint" of where you'd like it to allocate).

Instead of failing, it will allocate somewhere else (unless virtual address space is full, but that's unlikely on 64-bit). So you need to detect that mmap's return value != your hint. Probably just munmap that untouched space and ask again with the full size you need, then copy. You could attempt to mmap the remaining space onto the end of the new pages you just got, but that could fail and then you're making even more system calls.

On Linux you'd use mmap(MAP_FIXED_NOREPLACE) to return an error if it can't allocate where you want (without overlapping / replacing existing mappings).

Of course Linux mremap is even better, avoiding ever copying the data, just mapping the same physical pages to a new virtual address if you let it (with MREMAP_MAYMOVE). If MacOS doesn't have similar functionality via any MacOS-specific function calls or mmap flags, you simply can't get that functionality.


I find it really dumb that C++ std::vector is designed so it can't easily take advantage of realloc and thus mremap even if it exists, with replaceable new being a potentially visible side effect. And the new/delete allocator API entirely lacking a try-realloc that you could use even with non-trivially-copyable types. But this overly-conservative design in some higher-level languages means that low-level features might not get much use even if they existed, so I wouldn't be surprised if MacOS lacked it.

OTOH, C realloc certainly can use mremap if the original allocation has its pages to itself, and lots of stuff is written in C, not hobbled by C++'s allocator API. So MacOS might well support something like this somehow, but I don't know MacOS-specific system call details.

I did have a look at the table of BSD system calls in the Darwin XNU kernel https://github.com/opensource-apple/xnu/blob/master/bsd/kern/syscalls.master as suggested by macOS 64-bit System Call Table

There might be other whole categories of system call, but I'd hope that any mmap-related calls would be in the BSD family of calls, using the 0x2000000 class bit.

There is a int memorystatus_control(uint32_t command, int32_t pid, uint32_t flags, user_addr_t buffer, size_t buffersize); but that returns an int, so I assume it's not what we're looking for.

I didn't see any other system calls that looked at all promising for this.

I didn't check the MacOS man page for mmap; if it has any MacOS-specific flags like MAP_FIXED_NOREPLACE, they'd hopefully be there.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • "to try to allocate new pages contiguous with what you already have" <-- I didn't understand this part. On linux virtual memory seems to go top to bottom so even if I could get it continuous I would still have to copy? Thus defeating the point? – Cal Jun 16 '22 at 03:35
  • I did a trick on linux using mmap with memfd_create to map one piece of memory many times. I hope something like this would work. memfd_create doesn't exist on mac/openbsd either. shmget seems to return an int which might be a pointer so it currently looks like I'm stuck and need to use memcpy – Cal Jun 16 '22 at 03:38
  • 1
    @Cal: `mmap` allocations are randomized somewhat, IIRC, so there may be a gap after your allocation. Especially if you've freed some allocations before trying to realloc an earlier one. If you anticipate wanting to realloc later, provide a hint address when you allocate in the first place that puts it separate from other allocations. (Don't just randomize everything, though; page tables are a radix tree, so page walks on TLB misses are cheaper if they share a common upper part of the tree, and waste less space on PTEs. Like being within the same 1GiB hugepage, or 2^9 * 1GiB top level area.) – Peter Cordes Jun 16 '22 at 03:39
  • @Cal: I don't know MacOS-specific stuff in general; I know some about the system-call ABI in x86-64 asm because it's come up in stack overflow Q&As, but as for what system calls are actually available and what they do, IDK any better than you. https://github.com/opensource-apple/xnu/blob/master/bsd/kern/syscalls.master doesn't contain the string `memfd`, so if it has it, it calls it something else. (But thanks for the tip on Linux memfd giving a good way to map the same page to multiple addresses; I used a hackier way for [testing stale insn fetch](//stackoverflow.com/a/54587099/224132)) – Peter Cordes Jun 16 '22 at 03:41
  • I found the question that helped me when I originally tried this trick https://stackoverflow.com/questions/71575020/can-i-create-a-circular-buffer-on-linux-current-code-segfaults it looks like shm_open does exist and I can probably use it in /tmp for the length I need – Cal Jun 16 '22 at 05:04