Ticket #12438: 0001-libroot-Add-brk-and-sbrk.patch

File 0001-libroot-Add-brk-and-sbrk.patch, 19.4 KB (added by simonsouth, 9 years ago)

Add brk and sbrk (and remove sbrk_hook)

  • 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  
    77
    88
    99#include <null.h>
     10#include <stdint.h>
    1011#include <sys/types.h>
    1112
    1213
    extern void _exit(int status) __attribute__ ((noreturn));  
    221222
    222223extern pid_t    tcgetpgrp(int fd);
    223224extern int      tcsetpgrp(int fd, pid_t pgrpid);
    224 extern void     *sbrk(long incr);
     225
     226extern int      brk(void *addr);
     227extern void     *sbrk(intptr_t increment);
    225228
    226229extern unsigned int alarm(unsigned int seconds);
    227230extern 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)  
    13071307        return B_BAD_ADDRESS;
    13081308
    13091309    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;
    13131314
    13141315        case RLIMIT_CORE:
    13151316            rlp->rlim_cur = 0;
    13161317            rlp->rlim_max = 0;
    13171318            return B_OK;
    13181319
     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
    13191329        case RLIMIT_STACK:
    13201330        {
    13211331            rlp->rlim_cur = USER_MAIN_THREAD_STACK_SIZE;
    common_setrlimit(int resource, const struct rlimit * rlp)  
    13381348        return B_BAD_ADDRESS;
    13391349
    13401350    switch (resource) {
    1341         case RLIMIT_NOFILE:
    1342         case RLIMIT_NOVMON:
    1343             return vfs_setrlimit(resource, rlp);
    1344 
    13451351        case RLIMIT_CORE:
    13461352            // We don't support core file, so allow settings to 0/0 only.
    13471353            if (rlp->rlim_cur != 0 || rlp->rlim_max != 0)
    13481354                return EINVAL;
    13491355            return B_OK;
    13501356
     1357        case RLIMIT_NOFILE:
     1358        case RLIMIT_NOVMON:
     1359            return vfs_setrlimit(resource, rlp);
     1360
    13511361        default:
    13521362            return EINVAL;
    13531363    }
  • 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  
    3636#   define CTRACE(x) ;
    3737#endif
    3838
    39 extern "C" void *(*sbrk_hook)(long);
    40 void *(*sbrk_hook)(long) = &BPrivate::hoardSbrk;
    41 
    4239using namespace BPrivate;
    4340
    4441struct 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()  
    206206
    207207
    208208extern "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*
    218209memalign(size_t alignment, size_t size)
    219210{
    220211    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 ] {  
    1212        MergeObject <$(architecture)>posix_unistd.o :
    1313            access.c
    1414            alarm.c
     15            brk.c
    1516            chown.c
    1617            chroot.cpp
    1718            close.c
    for architectureObject in [ MultiArchSubDirSetup ] {  
    3334            pipe.c
    3435            process.c
    3536            read.c
    36             sbrk.c
    3737            sleep.c
    3838            sync.c
    3939            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 */
     51static status_t
     52get_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 */
     84static bool
     85program_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 */
     115static status_t
     116resize_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 */
     136static int
     137brk_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 */
     187int
     188brk(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 */
     217void *
     218sbrk(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 method
    21     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;  
    7979int optind;
    8080int optopt;
    8181int re_syntax_options;
    82 int sbrk_hook;
    8382int signgam;
    8483int stderr;
    8584int 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;  
    103103int optind;
    104104int optopt;
    105105int re_syntax_options;
    106 int sbrk_hook;
    107106int signgam;
    108107int stderr;
    109108int 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)  
    77# POSIX/libc tests
    88SimpleTest abort_test : abort_test.cpp ;
    99SimpleTest SyslogTest : SyslogTest.cpp ;
     10SimpleTest brk_test : brk_test.c ;
    1011SimpleTest clearenv : clearenv.cpp ;
    1112SimpleTest dirent_test : dirent_test.cpp ;
    1213SimpleTest 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
     17struct sbrk_test {
     18    char *name;
     19    char *sbrk_arg_text;
     20    intptr_t sbrk_arg;
     21};
     22
     23
     24static void
     25output_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
     50static void
     51output_data_segment_info(void *address)
     52{
     53    puts("Current data segment layout:");
     54    output_area_info(address);
     55    puts("");
     56}
     57
     58
     59static void
     60output_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
     70int
     71main(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}