diff --git a/headers/private/kernel/vm.h b/headers/private/kernel/vm.h index 399d0eb9ff..eb3dd08039 100644 --- a/headers/private/kernel/vm.h +++ b/headers/private/kernel/vm.h @@ -139,8 +139,9 @@ area_id _user_transfer_area(area_id area, void **_address, uint32 addressSpec, status_t _user_set_area_protection(area_id area, uint32 newProtection); area_id _user_clone_area(const char *name, void **_address, uint32 addressSpec, uint32 protection, area_id sourceArea); -status_t _user_reserve_heap_address_range(addr_t* userAddress, uint32 addressSpec, +status_t _user_reserve_address_range(addr_t* userAddress, uint32 addressSpec, addr_t size); +status_t _user_unreserve_address_range(addr_t address, addr_t size); #ifdef __cplusplus } diff --git a/headers/private/system/syscalls.h b/headers/private/system/syscalls.h index fb36a34907..cbcf670ae8 100644 --- a/headers/private/system/syscalls.h +++ b/headers/private/system/syscalls.h @@ -373,8 +373,9 @@ extern status_t _kern_set_area_protection(area_id area, extern area_id _kern_clone_area(const char *name, void **_address, uint32 addressSpec, uint32 protection, area_id sourceArea); -extern status_t _kern_reserve_heap_address_range(addr_t* _address, +extern status_t _kern_reserve_address_range(addr_t* _address, uint32 addressSpec, addr_t size); +extern status_t _kern_unreserve_address_range(addr_t address, addr_t size); extern area_id _kern_map_file(const char *name, void **address, int addressSpec, size_t size, int protection, diff --git a/src/kits/app/ServerMemoryAllocator.cpp b/src/kits/app/ServerMemoryAllocator.cpp index 2ed6cb46a5..5235f67819 100644 --- a/src/kits/app/ServerMemoryAllocator.cpp +++ b/src/kits/app/ServerMemoryAllocator.cpp @@ -1,13 +1,12 @@ /* - * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2009, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de */ -/*! - Note, this class don't provide any locking whatsoever - you are +/*! Note, this class don't provide any locking whatsoever - you are supposed to have a BPrivate::AppServerLink object around which does the necessary locking. However, this is not enforced in the methods here, you have to @@ -74,14 +73,16 @@ ServerMemoryAllocator::AddArea(area_id serverArea, area_id& _area, uint8*& _base if (!readOnly) { // reserve 128 MB of space for the area base = (void*)0x60000000; - status = _kern_reserve_heap_address_range((addr_t*)&base, - B_BASE_ADDRESS, 128 * 1024 * 1024); + status = _kern_reserve_address_range((addr_t*)&base, B_BASE_ADDRESS, + 128 * 1024 * 1024); addressSpec = status == B_OK ? B_EXACT_ADDRESS : B_BASE_ADDRESS; } #endif - mapping->local_area = clone_area(readOnly ? "server read-only memory" : "server_memory", - &base, addressSpec, B_READ_AREA | (readOnly ? 0 : B_WRITE_AREA), serverArea); + mapping->local_area = clone_area(readOnly + ? "server read-only memory" : "server_memory", + &base, addressSpec, + B_READ_AREA | (readOnly ? 0 : B_WRITE_AREA), serverArea); if (mapping->local_area < B_OK) { status = mapping->local_area; @@ -118,7 +119,8 @@ ServerMemoryAllocator::RemoveArea(area_id serverArea) status_t -ServerMemoryAllocator::AreaAndBaseFor(area_id serverArea, area_id& _area, uint8*& _base) +ServerMemoryAllocator::AreaAndBaseFor(area_id serverArea, area_id& _area, + uint8*& _base) { for (int32 i = fAreas.CountItems(); i-- > 0;) { area_mapping* mapping = (area_mapping*)fAreas.ItemAt(i); diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index e1cf29ee1e..e474307200 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -1632,7 +1632,7 @@ map_backing_store(vm_address_space* addressSpace, vm_cache* cache, } status = insert_area(addressSpace, _virtualAddress, addressSpec, size, area); - if (status < B_OK) + if (status != B_OK) goto err2; // attach the cache to the area @@ -1741,7 +1741,7 @@ vm_reserve_address_range(team_id team, void** _address, uint32 addressSpec, status_t status = insert_area(locker.AddressSpace(), _address, addressSpec, size, area); - if (status < B_OK) { + if (status != B_OK) { free(area); return status; } @@ -5907,8 +5907,7 @@ delete_area(area_id area) status_t -_user_reserve_heap_address_range(addr_t* userAddress, uint32 addressSpec, - addr_t size) +_user_reserve_address_range(addr_t* userAddress, uint32 addressSpec, addr_t size) { // filter out some unavailable values (for userland) switch (addressSpec) { @@ -5920,16 +5919,16 @@ _user_reserve_heap_address_range(addr_t* userAddress, uint32 addressSpec, addr_t address; if (!IS_USER_ADDRESS(userAddress) - || user_memcpy(&address, userAddress, sizeof(address)) < B_OK) + || user_memcpy(&address, userAddress, sizeof(address)) != B_OK) return B_BAD_ADDRESS; status_t status = vm_reserve_address_range( vm_current_user_address_space_id(), (void**)&address, addressSpec, size, RESERVED_AVOID_BASE); - if (status < B_OK) + if (status != B_OK) return status; - if (user_memcpy(userAddress, &address, sizeof(address)) < B_OK) { + if (user_memcpy(userAddress, &address, sizeof(address)) != B_OK) { vm_unreserve_address_range(vm_current_user_address_space_id(), (void*)address, size); return B_BAD_ADDRESS; @@ -5939,6 +5938,14 @@ _user_reserve_heap_address_range(addr_t* userAddress, uint32 addressSpec, } +status_t +_user_unreserve_address_range(addr_t address, addr_t size) +{ + return vm_unreserve_address_range(vm_current_user_address_space_id(), + (void*)address, size); +} + + area_id _user_area_for(void* address) { diff --git a/src/system/libroot/posix/malloc/arch-specific.cpp b/src/system/libroot/posix/malloc/arch-specific.cpp index c0b002721f..95f0e3963b 100644 --- a/src/system/libroot/posix/malloc/arch-specific.cpp +++ b/src/system/libroot/posix/malloc/arch-specific.cpp @@ -88,7 +88,7 @@ __init_heap(void) // for it. They may get reclaimed by other areas, though, but the maximum // size of the heap is guaranteed until the space is really needed. sHeapBase = (void *)0x18000000; - status_t status = _kern_reserve_heap_address_range((addr_t *)&sHeapBase, + status_t status = _kern_reserve_address_range((addr_t *)&sHeapBase, B_EXACT_ADDRESS, 0x48000000); sHeapArea = create_area("heap", (void **)&sHeapBase, diff --git a/src/system/runtime_loader/images.cpp b/src/system/runtime_loader/images.cpp index 11f896dddf..516fad1284 100644 --- a/src/system/runtime_loader/images.cpp +++ b/src/system/runtime_loader/images.cpp @@ -154,6 +154,30 @@ topological_sort(image_t* image, uint32 slot, image_t** initList, } +/*! Finds the load address and address specifier of the given image region. +*/ +static void +get_image_region_load_address(image_t* image, uint32 index, int32 lastDelta, + bool fixed, addr_t& loadAddress, uint32& addressSpecifier) +{ + if (image->dynamic_ptr != 0 && !fixed) { + // relocatable image... we can afford to place wherever + if (index == 0) { + // but only the first segment gets a free ride + loadAddress = RLD_PROGRAM_BASE; + addressSpecifier = B_BASE_ADDRESS; + } else { + loadAddress = image->regions[index].vmstart + lastDelta; + addressSpecifier = B_EXACT_ADDRESS; + } + } else { + // not relocatable, put it where it asks or die trying + loadAddress = image->regions[index].vmstart; + addressSpecifier = B_EXACT_ADDRESS; + } +} + + // #pragma mark - @@ -260,74 +284,95 @@ map_image(int fd, char const* path, image_t* image, bool fixed) else baseName = path; - for (uint32 i = 0; i < image->num_regions; i++) { - char regionName[B_OS_NAME_LENGTH]; - addr_t loadAddress; - uint32 addressSpecifier; + // determine how much space we need for all loaded segments + addr_t reservedAddress = 0; + addr_t loadAddress; + size_t reservedSize = 0; + size_t length = 0; + uint32 addressSpecifier = B_ANY_ADDRESS; + + for (uint32 i = 0; i < image->num_regions; i++) { // for BeOS compatibility: if we load an old BeOS executable, we // have to relocate it, if possible - we recognize it because the // vmstart is set to 0 (hopefully always) if (fixed && image->regions[i].vmstart == 0) fixed = false; + uint32 regionAddressSpecifier; + get_image_region_load_address(image, i, + loadAddress - image->regions[i - 1].vmstart, fixed, + loadAddress, regionAddressSpecifier); + if (i == 0) { + reservedAddress = loadAddress; + addressSpecifier = regionAddressSpecifier; + } + + length += TO_PAGE_SIZE(image->regions[i].vmsize + + (loadAddress % B_PAGE_SIZE)); + + size_t size = TO_PAGE_SIZE(loadAddress + image->regions[i].vmsize) + - reservedAddress; + if (size > reservedSize) + reservedSize = size; + } + + // Check whether the segments have an unreasonable amount of unused space + // inbetween. + if (reservedSize > length + 8 * 1024) + return B_BAD_DATA; + + // reserve that space and allocate the areas from that one + if (_kern_reserve_address_range(&reservedAddress, addressSpecifier, + reservedSize) != B_OK) + return B_NO_MEMORY; + + for (uint32 i = 0; i < image->num_regions; i++) { + char regionName[B_OS_NAME_LENGTH]; + snprintf(regionName, sizeof(regionName), "%s_seg%lu%s", baseName, i, (image->regions[i].flags & RFLAG_RW) ? "rw" : "ro"); - if (image->dynamic_ptr && !fixed) { - // relocatable image... we can afford to place wherever - if (i == 0) { - // but only the first segment gets a free ride - loadAddress = RLD_PROGRAM_BASE; - addressSpecifier = B_BASE_ADDRESS; - } else { - loadAddress = image->regions[i].vmstart - + image->regions[i-1].delta; - addressSpecifier = B_EXACT_ADDRESS; - } - } else { - // not relocatable, put it where it asks or die trying - loadAddress = image->regions[i].vmstart; - addressSpecifier = B_EXACT_ADDRESS; - } + get_image_region_load_address(image, i, image->regions[i - 1].delta, + fixed, loadAddress, addressSpecifier); - if (image->regions[i].flags & RFLAG_ANON) { + // If the image position is arbitrary, we must let it point to the start + // of the reserved address range. + if (addressSpecifier != B_EXACT_ADDRESS) + loadAddress = reservedAddress; + + if ((image->regions[i].flags & RFLAG_ANON) != 0) { image->regions[i].id = _kern_create_area(regionName, - (void**)&loadAddress, addressSpecifier, + (void**)&loadAddress, B_EXACT_ADDRESS, image->regions[i].vmsize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); - if (image->regions[i].id < 0) + if (image->regions[i].id < 0) { + _kern_unreserve_address_range(reservedAddress, reservedSize); return image->regions[i].id; - - image->regions[i].delta = loadAddress - image->regions[i].vmstart; - image->regions[i].vmstart = loadAddress; + } } else { image->regions[i].id = _kern_map_file(regionName, - (void**)&loadAddress, addressSpecifier, + (void**)&loadAddress, B_EXACT_ADDRESS, image->regions[i].vmsize, B_READ_AREA | B_WRITE_AREA, REGION_PRIVATE_MAP, false, fd, PAGE_BASE(image->regions[i].fdstart)); - if (image->regions[i].id < 0) + if (image->regions[i].id < 0) { + _kern_unreserve_address_range(reservedAddress, reservedSize); return image->regions[i].id; + } TRACE(("\"%s\" at %p, 0x%lx bytes (%s)\n", path, (void *)loadAddress, image->regions[i].vmsize, image->regions[i].flags & RFLAG_RW ? "rw" : "read-only")); - image->regions[i].delta = loadAddress - image->regions[i].vmstart; - image->regions[i].vmstart = loadAddress; - // handle trailer bits in data segment if (image->regions[i].flags & RFLAG_RW) { - addr_t startClearing; - addr_t toClear; - - startClearing = image->regions[i].vmstart + addr_t startClearing = loadAddress + PAGE_OFFSET(image->regions[i].start) + image->regions[i].size; - toClear = image->regions[i].vmsize + addr_t toClear = image->regions[i].vmsize - PAGE_OFFSET(image->regions[i].start) - image->regions[i].size; @@ -336,9 +381,12 @@ map_image(int fd, char const* path, image_t* image, bool fixed) memset((void *)startClearing, 0, toClear); } } + + image->regions[i].delta = loadAddress - image->regions[i].vmstart; + image->regions[i].vmstart = loadAddress; } - if (image->dynamic_ptr) + if (image->dynamic_ptr != 0) image->dynamic_ptr += image->regions[0].delta; return B_OK;