Background:
On a real 68k host, where the Mac memory addresses need to be real host program memory addresses, and for emulation speed on non-68k hosts, macemu can take advantage of having the mac memory in host memory at a fixed location known at compile time, so that the offset between mac addresses and host addresses is fixed. The general case of a fixed offset is DIRECT_ADDRESSING; the special case where that offset is 0 is REAL_ADDRESSING. For more information on addressing modes see TECH. These fixed offsets are especially useful under JIT where it means you don't have to deal with shuffling a memory offset around, and reserve another register or incur access to another global to get it basically every load/store.
Of course this is used for the mac's entire main RAM and ROM, which we know the sizes of at launch time.
But also the framebuffer: The various SDL video implementations basically assume you are doing one of the fixed addressing modes, and always use their vm_acquire_framebuffer() to do a fixed allocation for the framebuffer, using whatever suitable OS platform functionality exists in vm_alloc.cpp, and the way the code is organized it needs to free and reallocate that framebuffer as the size changes when we change the video mode.
To support a fixed address block on a modern OS with virtual addressing you need to allocate process memory at fixed addresses.
On a platform where there is mmap() that supports the MAP_FIXED flag we use that...
The issue:
Unfortunately the common mmap() MAP_FIXED behaviour is that if there is already a memory allocation in the process address space covering the memory address range you asked for, it just cuts it up and deletes the part that overlaps the request and gives it to the mmap() caller instead.
And with multiple threads potentially making allocations you can't just use the API to check there are no pages currently in the range and then allocate.
Let me just quote the Linux manpage verbatim:
MAP_FIXED
Don't interpret addr as a hint: place the mapping at
exactly that address. addr must be suitably aligned: for
most architectures a multiple of the page size is
sufficient; however, some architectures may impose
additional restrictions. If the memory region specified
by addr and length overlaps pages of any existing
mapping(s), then the overlapped part of the existing
mapping(s) will be discarded. If the specified address
cannot be used, mmap() will fail.
Software that aspires to be portable should use the
MAP_FIXED flag with care, keeping in mind that the exact
layout of a process's memory mappings is allowed to change
significantly between Linux versions, C library versions,
and operating system releases. Carefully read the
discussion of this flag in NOTES!
[From NOTES:]
Using MAP_FIXED safely
The only safe use for MAP_FIXED is where the address range
specified by addr and length was previously reserved using
another mapping; otherwise, the use of MAP_FIXED is hazardous
because it forcibly removes preexisting mappings, making it easy
for a multithreaded process to corrupt its own address space.
For example, suppose that thread A looks through /proc/pid/maps
in order to locate an unused address range that it can map using
MAP_FIXED, while thread B simultaneously acquires part or all of
that same address range. When thread A subsequently employs
mmap(MAP_FIXED), it will effectively clobber the mapping that
thread B created. In this scenario, thread B need not create a
mapping directly; simply making a library call that, internally,
uses [dlopen(3)](https://man7.org/linux/man-pages/man3/dlopen.3.html) to load some other shared library, will suffice.
The [dlopen(3)](https://man7.org/linux/man-pages/man3/dlopen.3.html) call will map the library into the process's
address space. Furthermore, almost any library call may be
implemented in a way that adds memory mappings to the address
space, either with this technique, or by simply allocating
memory. Examples include [brk(2)](https://man7.org/linux/man-pages/man2/brk.2.html), [malloc(3)](https://man7.org/linux/man-pages/man3/malloc.3.html), [pthread_create(3)](https://man7.org/linux/man-pages/man3/pthread_create.3.html),
and the PAM libraries ⟨http://www.linux-pam.org/⟩.
Since Linux 4.17, a multithreaded program can use the
MAP_FIXED_NOREPLACE flag to avoid the hazard described above when
attempting to create a mapping at a fixed address that has not
been reserved by a preexisting mapping.
Here's FreeBSD:
MAP_FIXED Do not permit the system to select a different ad-
dress than the one specified. If the specified ad-
dress cannot be used, mmap() will fail. If
MAP_FIXED is specified, addr must be a multiple of
the page size. If MAP_EXCL is not specified, a suc-
cessful MAP_FIXED request replaces any previous map-
pings for the process' pages in the range from addr
to addr + len. In contrast, if MAP_EXCL is speci-
fied, the request will fail if a mapping already ex-
ists within the range.
OpenBSD:
If the MAP_FIXED flag is specified, the allocation will happen at the specified address,
replacing any previously established mappings in its range.
Solutions:
There's no way to actually recover if the fixed memory allocation is actually needed and the memory space in the process is already allocated; the only possible improvement to a given fixed allocation call as it concerns memory already allocated is that we detect that it can't not overlap and crash out and at least not give the user a partly working system that is slowly destroying itself. But of course, if we can do the fixed allocations more wisely in the first place, i.e. 1) do them once and never change them and 2) do them as soon as possible at program start so the least possible other stuff is allocated, that would be an improvement irrespective of whether we can detect the overlap or not.
Background:
On a real 68k host, where the Mac memory addresses need to be real host program memory addresses, and for emulation speed on non-68k hosts, macemu can take advantage of having the mac memory in host memory at a fixed location known at compile time, so that the offset between mac addresses and host addresses is fixed. The general case of a fixed offset is
DIRECT_ADDRESSING; the special case where that offset is0isREAL_ADDRESSING. For more information on addressing modes seeTECH. These fixed offsets are especially useful under JIT where it means you don't have to deal with shuffling a memory offset around, and reserve another register or incur access to another global to get it basically every load/store.Of course this is used for the mac's entire main RAM and ROM, which we know the sizes of at launch time.
But also the framebuffer: The various SDL video implementations basically assume you are doing one of the fixed addressing modes, and always use their
vm_acquire_framebuffer()to do a fixed allocation for the framebuffer, using whatever suitable OS platform functionality exists invm_alloc.cpp, and the way the code is organized it needs to free and reallocate that framebuffer as the size changes when we change the video mode.To support a fixed address block on a modern OS with virtual addressing you need to allocate process memory at fixed addresses.
On a platform where there is
mmap()that supports theMAP_FIXEDflag we use that...The issue:
Unfortunately the common
mmap()MAP_FIXEDbehaviour is that if there is already a memory allocation in the process address space covering the memory address range you asked for, it just cuts it up and deletes the part that overlaps the request and gives it to themmap()caller instead.And with multiple threads potentially making allocations you can't just use the API to check there are no pages currently in the range and then allocate.
Let me just quote the Linux manpage verbatim:
Here's FreeBSD:
OpenBSD:
Solutions:
There's no way to actually recover if the fixed memory allocation is actually needed and the memory space in the process is already allocated; the only possible improvement to a given fixed allocation call as it concerns memory already allocated is that we detect that it can't not overlap and crash out and at least not give the user a partly working system that is slowly destroying itself. But of course, if we can do the fixed allocations more wisely in the first place, i.e. 1) do them once and never change them and 2) do them as soon as possible at program start so the least possible other stuff is allocated, that would be an improvement irrespective of whether we can detect the overlap or not.