Overlapping pages with mmap (MAP_FIXED)

前端 未结 3 636
慢半拍i
慢半拍i 2021-02-04 07:08

Due to some obscure reasons which are not relevant for this question, I need to resort to use MAP_FIXED in order to obtain a page close to where the text section of libc lives i

3条回答
  •  迷失自我
    2021-02-04 07:43

    "Which explains what I am seeing, but I have a couple of questions:"

    "Is there a way to detect if something was already mapped to certain address? without accessing /proc/maps?"

    Yes, use mmap without MAP_FIXED.

    "Is there a way to force mmap to fail in the case of finding overlapping pages?"

    Apparently not, but simply use munmap after the mmap if mmap returns a mapping at other than the requested address.

    When used without MAP_FIXED, mmap on both linux and Mac OS X (and I suspect elsewhere also) obeys the address parameter iff no existing mapping in the range [address, address + length) exists. So if mmap answers a mapping at a different address to the one you supply you can infer there already exists a mapping in that range and you need to use a different range. Since mmap will typically answer a mapping at a very high address when it ignores the address parameter, simply unmap the region using munmap, and try again at a different address.

    Using mincore to check for use of an address range is not only a waste of time (one has to probe a page at a time), it may not work. Older linux kernels will only fail mincore appropriately for file mappings. They won't answer anything at all for MAP_ANON mappings. But as I've pointed out, all you need is mmap and munmap.

    I've just been through this exercise in implementing a memory manager for a Smalltalk VM. I use sbrk(0) to find out the first address at which I can map the first segment, and then use mmap and an increment of 1Mb to search for room for subsequent segments:

    static long          pageSize = 0;
    static unsigned long pageMask = 0;
    
    #define roundDownToPage(v) ((v)&pageMask)
    #define roundUpToPage(v) (((v)+pageSize-1)&pageMask)
    
    void *
    sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize)
    {
        char *hint, *address, *alloc;
        unsigned long alignment, allocBytes;
    
        if (pageSize) {
            fprintf(stderr, "sqAllocateMemory: already called\n");
            exit(1);
        }
        pageSize = getpagesize();
        pageMask = ~(pageSize - 1);
    
        hint = sbrk(0); /* the first unmapped address above existing data */
    
        alignment = max(pageSize,1024*1024);
        address = (char *)(((usqInt)hint + alignment - 1) & ~(alignment - 1));
    
        alloc = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto
                    (roundUpToPage(desiredHeapSize), address, &allocBytes);
        if (!alloc) {
            fprintf(stderr, "sqAllocateMemory: initial alloc failed!\n");
            exit(errno);
        }
        return (usqInt)alloc;
    }
    
    /* Allocate a region of memory of at least size bytes, at or above minAddress.
     *  If the attempt fails, answer null.  If the attempt succeeds, answer the
     * start of the region and assign its size through allocatedSizePointer.
     */
    void *
    sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(sqInt size, void *minAddress, sqInt *allocatedSizePointer)
    {
        char *address, *alloc;
        long bytes, delta;
    
        address = (char *)roundUpToPage((unsigned long)minAddress);
        bytes = roundUpToPage(size);
        delta = max(pageSize,1024*1024);
    
        while ((unsigned long)(address + bytes) > (unsigned long)address) {
            alloc = mmap(address, bytes, PROT_READ | PROT_WRITE,
                         MAP_ANON | MAP_PRIVATE, -1, 0);
            if (alloc == MAP_FAILED) {
                perror("sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto mmap");
                return 0;
            }
            /* is the mapping both at or above address and not too far above address? */
            if (alloc >= address && alloc <= address + delta) {
                *allocatedSizePointer = bytes;
                return alloc;
            }
            /* mmap answered a mapping well away from where Spur prefers.  Discard
             * the mapping and try again delta higher.
             */
            if (munmap(alloc, bytes) != 0)
                perror("sqAllocateMemorySegment... munmap");
            address += delta;
        }
        return 0;
    }
    

    This appears to work well, allocating memory at ascending addresses while skipping over any existing mappings.

    HTH

提交回复
热议问题