Ticket #12438: 0001-libroot-Add-brk-and-sbrk.patch
File 0001-libroot-Add-brk-and-sbrk.patch, 19.4 KB (added by , 9 years ago) |
---|
-
headers/posix/unistd.h
From afa7eda16ba3053d7718efeb20a30bc0fc943ca0 Mon Sep 17 00:00:00 2001 From: Simon South <ssouth@simonsouth.com> Date: Thu, 29 Oct 2015 13:31:28 -0400 Subject: [PATCH] libroot: Add brk() and sbrk(). This commit replaces the placeholder implementation of sbrk(), which operated on a process' heap, with real implementations of brk() and sbrk() that adjust a process' program break. * unistd.h: Add standard definitions of brk() and sbrk(); include stdint.h for intptr_t. * thread.cpp: Recognize RLIMIT_AS and RLIMIT_DATA resource limits (both currently unlimited); order limit identifiers alphabetically. * arch-specific.cpp: Remove sbrk_hook(). * malloc_debug_api.cpp: Remove sbrk_hook(). * unistd/Jamfile: Build brk.c instead of sbrk.c. * unistd/brk.c: Add. * unistd/sbrk.c: Delete (placeholder implementation). * libroot_stubs.c: Remove sbrk_hook(). * libroot_stubs_legacy.c: Remove sbrk_hook(). * src/tests/.../posix/Jamfile: Build brk_test.c. * brk_test.c: Add (simple unit test that demonstrates behaviour of sbrk()). --- headers/posix/unistd.h | 5 +- src/system/kernel/thread.cpp | 24 +- src/system/libroot/posix/malloc/arch-specific.cpp | 3 - .../posix/malloc_debug/malloc_debug_api.cpp | 9 - src/system/libroot/posix/unistd/Jamfile | 2 +- src/system/libroot/posix/unistd/brk.c | 268 +++++++++++++++++++++ src/system/libroot/posix/unistd/sbrk.c | 28 --- src/system/libroot/stubbed/libroot_stubs.c | 1 - src/system/libroot/stubbed/libroot_stubs_legacy.c | 1 - src/tests/system/libroot/posix/Jamfile | 1 + src/tests/system/libroot/posix/brk_test.c | 138 +++++++++++ 11 files changed, 429 insertions(+), 51 deletions(-) create mode 100644 src/system/libroot/posix/unistd/brk.c delete mode 100644 src/system/libroot/posix/unistd/sbrk.c create mode 100644 src/tests/system/libroot/posix/brk_test.c diff --git a/headers/posix/unistd.h b/headers/posix/unistd.h index 42e4584..9c8fc77 100644
a b 7 7 8 8 9 9 #include <null.h> 10 #include <stdint.h> 10 11 #include <sys/types.h> 11 12 12 13 … … extern void _exit(int status) __attribute__ ((noreturn)); 221 222 222 223 extern pid_t tcgetpgrp(int fd); 223 224 extern int tcsetpgrp(int fd, pid_t pgrpid); 224 extern void *sbrk(long incr); 225 226 extern int brk(void *addr); 227 extern void *sbrk(intptr_t increment); 225 228 226 229 extern unsigned int alarm(unsigned int seconds); 227 230 extern useconds_t ualarm(useconds_t microSeconds, useconds_t interval); -
src/system/kernel/thread.cpp
diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index de4adb5..12203f0 100644
a b common_getrlimit(int resource, struct rlimit * rlp) 1307 1307 return B_BAD_ADDRESS; 1308 1308 1309 1309 switch (resource) { 1310 case RLIMIT_NOFILE: 1311 case RLIMIT_NOVMON: 1312 return vfs_getrlimit(resource, rlp); 1310 case RLIMIT_AS: 1311 rlp->rlim_cur = __HAIKU_ADDR_MAX; 1312 rlp->rlim_max = __HAIKU_ADDR_MAX; 1313 return B_OK; 1313 1314 1314 1315 case RLIMIT_CORE: 1315 1316 rlp->rlim_cur = 0; 1316 1317 rlp->rlim_max = 0; 1317 1318 return B_OK; 1318 1319 1320 case RLIMIT_DATA: 1321 rlp->rlim_cur = RLIM_INFINITY; 1322 rlp->rlim_max = RLIM_INFINITY; 1323 return B_OK; 1324 1325 case RLIMIT_NOFILE: 1326 case RLIMIT_NOVMON: 1327 return vfs_getrlimit(resource, rlp); 1328 1319 1329 case RLIMIT_STACK: 1320 1330 { 1321 1331 rlp->rlim_cur = USER_MAIN_THREAD_STACK_SIZE; … … common_setrlimit(int resource, const struct rlimit * rlp) 1338 1348 return B_BAD_ADDRESS; 1339 1349 1340 1350 switch (resource) { 1341 case RLIMIT_NOFILE:1342 case RLIMIT_NOVMON:1343 return vfs_setrlimit(resource, rlp);1344 1345 1351 case RLIMIT_CORE: 1346 1352 // We don't support core file, so allow settings to 0/0 only. 1347 1353 if (rlp->rlim_cur != 0 || rlp->rlim_max != 0) 1348 1354 return EINVAL; 1349 1355 return B_OK; 1350 1356 1357 case RLIMIT_NOFILE: 1358 case RLIMIT_NOVMON: 1359 return vfs_setrlimit(resource, rlp); 1360 1351 1361 default: 1352 1362 return EINVAL; 1353 1363 } -
src/system/libroot/posix/malloc/arch-specific.cpp
diff --git a/src/system/libroot/posix/malloc/arch-specific.cpp b/src/system/libroot/posix/malloc/arch-specific.cpp index 872867c..4167f04 100644
a b 36 36 # define CTRACE(x) ; 37 37 #endif 38 38 39 extern "C" void *(*sbrk_hook)(long);40 void *(*sbrk_hook)(long) = &BPrivate::hoardSbrk;41 42 39 using namespace BPrivate; 43 40 44 41 struct free_chunk { -
src/system/libroot/posix/malloc_debug/malloc_debug_api.cpp
diff --git a/src/system/libroot/posix/malloc_debug/malloc_debug_api.cpp b/src/system/libroot/posix/malloc_debug/malloc_debug_api.cpp index 45d3c1c..3d3b062 100644
a b __heap_terminate_after() 206 206 207 207 208 208 extern "C" void* 209 sbrk_hook(long)210 {211 debug_printf("sbrk not supported on malloc debug\n");212 debugger("sbrk not supported on malloc debug");213 return NULL;214 }215 216 217 extern "C" void*218 209 memalign(size_t alignment, size_t size) 219 210 { 220 211 return sCurrentHeap->memalign(alignment, size); -
src/system/libroot/posix/unistd/Jamfile
diff --git a/src/system/libroot/posix/unistd/Jamfile b/src/system/libroot/posix/unistd/Jamfile index c57c52f..f973a9c 100644
a b for architectureObject in [ MultiArchSubDirSetup ] { 12 12 MergeObject <$(architecture)>posix_unistd.o : 13 13 access.c 14 14 alarm.c 15 brk.c 15 16 chown.c 16 17 chroot.cpp 17 18 close.c … … for architectureObject in [ MultiArchSubDirSetup ] { 33 34 pipe.c 34 35 process.c 35 36 read.c 36 sbrk.c37 37 sleep.c 38 38 sync.c 39 39 system.cpp -
new file src/system/libroot/posix/unistd/brk.c
diff --git a/src/system/libroot/posix/unistd/brk.c b/src/system/libroot/posix/unistd/brk.c new file mode 100644 index 0000000..abc6e0c
- + 1 /* 2 * Copyright 2015 Simon South, ssouth@simonsouth.com 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 /*! 8 * \file brk.c 9 * 10 * This module provides \c brk and \c sbrk, functions that adjust the program 11 * break of their calling process. 12 * 13 * A process' program break is the first memory location past the end of its 14 * data segment. Moving the program break has the effect of increasing or 15 * decreasing the amount of memory allocated to the process for its data and 16 * can be used to achieve a simple form of memory management. 17 * 18 * An ELF executable may contain multiple writable segments that make up a 19 * program's data segment, each of which may be placed by Haiku's \c 20 * runtime_loader in either one or two areas in memory. Additionally, nothing 21 * stops a program from adding onto its data segment directly using \c 22 * create_area. For these reasons this implementation avoids making 23 * assumptions about the process' data segment except that it is contiguous. 24 * \c brk and \c sbrk will function correctly regardless of the number of 25 * (adjacent) areas used to hold the process' data. 26 * 27 * Note this implementation never creates new areas; its operation is limited 28 * to expanding, contracting or deleting areas as necessary. 29 */ 30 31 32 #include <errno.h> 33 #include <stdint.h> 34 #include <sys/resource.h> 35 #include <unistd.h> 36 37 #include <image.h> 38 #include <OS.h> 39 40 #include <errno_private.h> 41 42 43 /*! 44 * Locates and returns information about the base (lowest in memory) area of 45 * the contiguous set of areas that hold the calling process' data segment. 46 * 47 * \param base_area_info A pointer to an \c area_info structure to receive 48 * information about the area. 49 * \return \c B_OK on success; \c B_BAD_VALUE otherwise. 50 */ 51 static status_t 52 get_data_segment_base_area_info(area_info *base_area_info) 53 { 54 status_t result = B_ERROR; 55 bool app_image_found = false; 56 57 image_info i_info; 58 int32 image_cookie = 0; 59 60 /* Locate our app image and output information for the area that holds the 61 * start of its data segment */ 62 while (!app_image_found 63 && get_next_image_info(0, &image_cookie, &i_info) == B_OK) { 64 if (i_info.type == B_APP_IMAGE) { 65 app_image_found = true; 66 67 result = get_area_info(area_for(i_info.data), base_area_info); 68 } 69 } 70 71 return result; 72 } 73 74 75 /*! 76 * Checks whether a proposed new program break would be valid given the 77 * resource limits imposed on the calling process. 78 * 79 * \param program_break The proposed new program break. 80 * \param base_address The base (start) address of the process' data segment. 81 * \return \c true if the program break would be valid given this process' 82 * resource limits; \c false otherwise. 83 */ 84 static bool 85 program_break_within_resource_limits(void *program_break, void *base_address) 86 { 87 bool result = false; 88 89 struct rlimit rlim; 90 91 if (getrlimit(RLIMIT_AS, &rlim) == 0 92 && (rlim.rlim_cur == RLIM_INFINITY 93 || program_break < (void *)rlim.rlim_cur) 94 && getrlimit(RLIMIT_DATA, &rlim) == 0 95 && (rlim.rlim_cur == RLIM_INFINITY 96 || ((rlim_t)((uint8_t *)program_break - (uint8_t *)(base_address)) 97 < rlim.rlim_cur))) 98 result = true; 99 100 return result; 101 } 102 103 104 /*! 105 * Resizes an area, up or down as necessary, so it ends at the specified 106 * address (rounded up to the nearest page boundary). 107 * 108 * \param a_info An \c area_info structure corresponding to the area on which 109 * to operate. 110 * \param address The new address at which the area should end. This address 111 * will be rounded up to the nearest page boundary. 112 * \return \c B_OK on success; \c B_BAD_VALUE, \c B_NO_MEMORY or \c B_ERROR 113 * otherwise (refer to the documentation for \c resize_area). 114 */ 115 static status_t 116 resize_area_to_address(area_info *a_info, void *address) 117 { 118 size_t new_size = (uint8_t *)address - (uint8_t *)(a_info->address); 119 size_t new_size_aligned = (new_size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 120 121 return resize_area(a_info->area, new_size_aligned); 122 } 123 124 125 /*! 126 * An internal method that sets the program break of the calling process to the 127 * specified address. 128 * 129 * \param addr The requested new program break. This address will be rounded up 130 * as necessary to align the program break on a page boundary. 131 * \param base_area_info An \c area_info structure corresponding to the base 132 * (lowest in memory) area of the contiguous set of areas 133 * that hold the calling process' data segment. 134 * \return 0 on success; -1 otherwise. 135 */ 136 static int 137 brk_internal(void *addr, area_info *base_area_info) 138 { 139 int result = 0; 140 141 void *next_area_address; 142 area_id next_area; 143 area_info next_area_info; 144 145 /* First, recursively process the next (higher) adjacent area, if any */ 146 next_area_address = (uint8_t *)base_area_info->address 147 + base_area_info->size; 148 next_area = area_for(next_area_address); 149 if (next_area != B_ERROR) { 150 result = get_area_info(next_area, &next_area_info) == B_OK ? 151 brk_internal(addr, &next_area_info) : -1; 152 } else { 153 /* This is the highest ("last") area in the data segment, so any 154 * increase in size needs to occur on this area */ 155 if (addr > next_area_address) { 156 result = resize_area_to_address(base_area_info, addr) == B_OK ? 0 157 : -1; 158 } 159 } 160 161 if (result == 0) { 162 if (addr <= base_area_info->address) { 163 /* This area starts at or above the program break the caller has 164 * requested, so the entire area can be deleted */ 165 result = delete_area(base_area_info->area) == B_OK ? 0 : -1; 166 } else if (addr < next_area_address) { 167 /* The requested program break lies within this area, so this area 168 * must be contracted */ 169 result = resize_area_to_address(base_area_info, addr) == B_OK ? 0 170 : -1; 171 } 172 } 173 174 return result; 175 } 176 177 178 /*! 179 * Sets the calling process' program break to the specified address, if valid 180 * and within the limits of available resources, expanding or contracting the 181 * process' data segment as necessary. 182 * 183 * \param addr The requested new program break. 184 * \return 0 on success; -1 on error (in which case \c errno will be set to 185 * \c ENOMEM). 186 */ 187 int 188 brk(void *addr) 189 { 190 int result = -1; 191 192 area_info base_area_info; 193 194 if (get_data_segment_base_area_info(&base_area_info) == B_OK 195 && addr > base_area_info.address 196 && program_break_within_resource_limits(addr, base_area_info.address)) 197 result = brk_internal(addr, &base_area_info); 198 199 if (result == -1) 200 __set_errno(ENOMEM); 201 202 return result; 203 } 204 205 206 /*! 207 * Adjusts the calling process' program break up or down by the requested 208 * number of bytes, if valid and within the limits of available resources, 209 * expanding or contracting the process' data segment as necessary. 210 * 211 * \param increment The amount, positive or negative, in bytes by which to 212 * adjust the program break. This value will be rounded up as 213 * necessary to align the program break on a page boundary. 214 * \return The previous program break on success; (void *)-1 on error (in which 215 * case \c errno will be set to \c ENOMEM). 216 */ 217 void * 218 sbrk(intptr_t increment) 219 { 220 void *result = NULL; 221 222 area_info base_area_info; 223 224 area_id next_area; 225 area_info next_area_info; 226 227 uint8_t *program_break; 228 uint8_t *new_program_break; 229 230 if (get_data_segment_base_area_info(&base_area_info) == B_OK) { 231 /* Find the current program break, which will be the memory address 232 * just past the end of the highest ("last") of the areas that hold the 233 * data segment */ 234 next_area = base_area_info.area; 235 do { 236 if (get_area_info(next_area, &next_area_info) == B_OK) { 237 next_area = area_for((uint8_t *)(next_area_info.address) 238 + next_area_info.size); 239 } else { 240 result = (void *)-1; 241 } 242 } while (next_area != B_ERROR && result == NULL); 243 244 if (result == NULL) { 245 program_break = (uint8_t *)(next_area_info.address) 246 + next_area_info.size; 247 new_program_break = program_break + increment; 248 249 /* If the requested increment is zero, just return the address of 250 * the current program break; otherwise, set a new program break 251 * and return the address of the old one */ 252 if (increment == 0 253 || (program_break_within_resource_limits(new_program_break, 254 base_area_info.address) 255 && brk_internal(new_program_break, &base_area_info) == 0)) 256 result = program_break; 257 else 258 result = (void *)-1; 259 } 260 } else { 261 result = (void *)-1; 262 } 263 264 if (result == (void *)-1) 265 __set_errno(ENOMEM); 266 267 return result; 268 } -
deleted file src/system/libroot/posix/unistd/sbrk.c
diff --git a/src/system/libroot/posix/unistd/sbrk.c b/src/system/libroot/posix/unistd/sbrk.c deleted file mode 100644 index 42a9838..0000000
+ - 1 /*2 * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.3 * Distributed under the terms of the MIT License.4 */5 6 7 #include <unistd.h>8 #include <errno.h>9 10 #include <errno_private.h>11 12 13 /* in hoard wrapper */14 extern void *(*sbrk_hook)(long);15 16 17 void *18 sbrk(long increment)19 {20 // TODO: we only support extending the heap for now using this method21 if (increment <= 0)22 return NULL;23 24 if (sbrk_hook)25 return (*sbrk_hook)(increment);26 27 return NULL;28 } -
src/system/libroot/stubbed/libroot_stubs.c
diff --git a/src/system/libroot/stubbed/libroot_stubs.c b/src/system/libroot/stubbed/libroot_stubs.c index cd8ff77..76cff4e 100644
a b int opterr; 79 79 int optind; 80 80 int optopt; 81 81 int re_syntax_options; 82 int sbrk_hook;83 82 int signgam; 84 83 int stderr; 85 84 int stdin; -
src/system/libroot/stubbed/libroot_stubs_legacy.c
diff --git a/src/system/libroot/stubbed/libroot_stubs_legacy.c b/src/system/libroot/stubbed/libroot_stubs_legacy.c index 855c3bb..d53e8a0 100644
a b int opterr; 103 103 int optind; 104 104 int optopt; 105 105 int re_syntax_options; 106 int sbrk_hook;107 106 int signgam; 108 107 int stderr; 109 108 int stdin; -
src/tests/system/libroot/posix/Jamfile
diff --git a/src/tests/system/libroot/posix/Jamfile b/src/tests/system/libroot/posix/Jamfile index 92b09df..131972c 100644
a b TARGET_WARNING_C++FLAGS_$(TARGET_PACKAGING_ARCH) 7 7 # POSIX/libc tests 8 8 SimpleTest abort_test : abort_test.cpp ; 9 9 SimpleTest SyslogTest : SyslogTest.cpp ; 10 SimpleTest brk_test : brk_test.c ; 10 11 SimpleTest clearenv : clearenv.cpp ; 11 12 SimpleTest dirent_test : dirent_test.cpp ; 12 13 SimpleTest flock_test : flock_test.cpp ; -
new file src/tests/system/libroot/posix/brk_test.c
diff --git a/src/tests/system/libroot/posix/brk_test.c b/src/tests/system/libroot/posix/brk_test.c new file mode 100644 index 0000000..fd8d6d0
- + 1 /* 2 * Copyright 2015 Simon South, ssouth@simonsouth.com 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <errno.h> 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <string.h> 11 #include <unistd.h> 12 13 #include <image.h> 14 #include <OS.h> 15 16 17 struct sbrk_test { 18 char *name; 19 char *sbrk_arg_text; 20 intptr_t sbrk_arg; 21 }; 22 23 24 static void 25 output_area_info(void *base_address) 26 { 27 uint8_t *next_area_address; 28 area_id next_area; 29 area_info next_area_info; 30 31 next_area = area_for(base_address); 32 while (next_area != B_ERROR) { 33 if (get_area_info(next_area, &next_area_info) == B_OK) { 34 next_area_address = (uint8_t *)(next_area_info.address) 35 + next_area_info.size; 36 37 printf("Area ID 0x%x: Addr: %p - %p Size: 0x%04zx %s\n", 38 next_area_info.area, next_area_info.address, 39 next_area_address - 1, next_area_info.size, 40 next_area_info.name); 41 42 next_area = area_for(next_area_address); 43 } else { 44 fprintf(stderr, "PROBLEM: Couldn't get area info"); 45 } 46 } 47 } 48 49 50 static void 51 output_data_segment_info(void *address) 52 { 53 puts("Current data segment layout:"); 54 output_area_info(address); 55 puts(""); 56 } 57 58 59 static void 60 output_sbrk_result(intptr_t sbrk_arg, char *sbrk_arg_text) 61 { 62 printf("\tsbrk(%s) returns %p\n", sbrk_arg_text, sbrk(sbrk_arg)); 63 if (errno != 0) { 64 printf("\tError: %s\n", strerror(errno)); 65 errno = 0; 66 } 67 } 68 69 70 int 71 main(int argc, char **argv) 72 { 73 static const uint NUM_TESTS = 7; 74 static struct sbrk_test sbrk_tests[] = { 75 { "Get current program break", "0", 0 }, 76 77 { "Expand data segment (less than one page)", "0x7ff", 0x7ff }, 78 { "Expand data segment (more than one page)", "0x57ff", 0x57ff }, 79 { 80 "Expand data segment (unreasonable value)", 81 "INTPTR_MAX", 82 INTPTR_MAX 83 }, 84 85 { "Shrink data segment (less than one page)", "-0x7ff", -0x7ff }, 86 { "Shrink data segment (more than one page)", "-0x27ff", -0x27ff }, 87 { 88 "Shrink data segment (unreasonable value)", 89 "INTPTR_MIN", 90 INTPTR_MIN 91 } 92 }; 93 94 int result = -1; 95 96 bool app_image_found = false; 97 image_info i_info; 98 int32 image_cookie = 0; 99 100 void *data_segment_address = NULL; 101 102 uint test_index; 103 struct sbrk_test *next_sbrk_test; 104 105 /* Find the address of our data segment */ 106 while (!app_image_found 107 && get_next_image_info(0, &image_cookie, &i_info) == B_OK) { 108 if (i_info.type == B_APP_IMAGE) { 109 app_image_found = true; 110 111 data_segment_address = i_info.data; 112 } 113 } 114 115 if (data_segment_address != NULL) { 116 /* Run our tests */ 117 test_index = 0; 118 while (test_index < NUM_TESTS) { 119 next_sbrk_test = &sbrk_tests[test_index]; 120 121 output_data_segment_info(data_segment_address); 122 printf("%s:\n", next_sbrk_test->name); 123 output_sbrk_result(next_sbrk_test->sbrk_arg, 124 next_sbrk_test->sbrk_arg_text); 125 printf("\n"); 126 127 test_index += 1; 128 } 129 130 output_data_segment_info(data_segment_address); 131 132 result = 0; 133 } else { 134 fprintf(stderr, "PROBLEM: Couldn't locate data segment\n"); 135 } 136 137 return result; 138 }