Background information:
I'm using 64 bit Arch on an x86 system.
I'm not using libc or any language that depends on libc. I'm using a proprietary research language. I am making my syscalls through inline assembly.
I'm writing an experimental custom allocator for a research project, so a portable solution is a nice-to-have, but not a requirement.
My programs are statically linked and I am willing and able to rewrite the libraries I'm using to account for a given solution.
According to this SO post: Where is the stack memory allocated from for a Linux process? A program's virtual address space is organized like this:
------------------ <--- Top of the process address space
Stack (grows down)
v v v v v v v v v
------------------
(unmapped)
------------------ <--- Maximum stack size.
(unmapped)
-------------------
mmap
-------------------
(unmapped)
-------------------
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
brk (grows up)
-------------------
BSS
-------------------
Data
-------------------
Text
-------------------
------------------- <--- Bottom or process address space.
Presuming this is correct, I'm trying to find the range of MMAPable addresses between brk and the stack.
Is it sufficient to find the lower bound by getting a ptr from sbrk(0)
, page aligning the ptr upwards, and then ensuring that brk
and sbrk
are never called again?
Is it sufficient to safely approximate the upper bound by getting a ptr to some location on the stack, getting the max size of the stack from getrlimit
, subtracting that from the ptr, page aligning the ptr downwards, and then ensuring the size of the stack is never changed by setrlimit
?
I don't need an exact range here, but I do need a large contiguous range of addresses that are guaranteed to be safely MMAPable(presuming I don't clobber them myself, of course). During my research I came across this: https://www.ibm.com/docs/en/aix/7.2?topic=memory-understanding-mapping , which says:
For 64-bit processes, two sets of address ranges with the process address space are available for mmap or shmat mappings. The first, consisting of the single range 0x07000000_00000000-0x07FFFFFF_FFFFFFFF, is available for both fixed-location and variable-location mappings. The second set of address ranges is available for fixed-location mappings only and consists of the ranges 0x30000000-0xCFFFFFFF, 0xE0000000-0xEFFFFFFF, and 0x10_00000000-0x06FFFFFF_FFFFFFFF. The last range of this set, consisting of 0x10_00000000-0x06FFFFFF_FFFFFFFF, is also made available to system loader to hold program text, data and heap, so only unused portions of the range are available for fixed-location mappings.
This is the exact kind of information I need, but it's for the wrong OS. If anyone can help me find this same information about Linux in general and Arch specifically, then that would be a great help.
After a lot of testing I've found the solution I proposed in the question does work. I've been using cat /proc/<pid>/maps
to check my custom allocator, and it's behaving as I expected. To reiterate the solution:
To find the lower bound use sbrk(0)
, make sure the ptr is page aligned, and then ensure that brk
and sbrk
are never called again.
To safely approximate the upper bound find the stack size with getrlimit
, subtract that from a ptr into the stack, page align the ptr, and then never change the stack size with setrlimit
.
If you might need to touch brk
, sbrk
, or setrlimit
, then you can also add some padding to the lower bound and subtract some padding from the upper bound. You can dynamically compute a safe amount of padding by finding how much memory the system has with /proc/meminfo
, or if you don't need a general solution you can just over-approximate how much you'll need based on what you're doing.