2 * Copyright 2015 Simon South, ssouth@simonsouth.com
3 * All rights reserved. Distributed under the terms of the MIT License.
10 * This module provides \c brk and \c sbrk, functions that adjust the program
11 * break of their calling process.
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.
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.
27 * Note this implementation never creates new areas; its operation is limited
28 * to expanding, contracting or deleting areas as necessary.
34 #include <sys/resource.h>
40 #include <errno_private.h>
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.
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.
52 get_data_segment_base_area_info(area_info
*base_area_info
)
54 status_t result
= B_ERROR
;
55 bool app_image_found
= false;
58 int32 image_cookie
= 0;
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;
67 result
= get_area_info(area_for(i_info
.data
), base_area_info
);
76 * Checks whether a proposed new program break would be valid given the
77 * resource limits imposed on the calling process.
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.
85 program_break_within_resource_limits(void *program_break
, void *base_address
)
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
))
105 * Resizes an area, up or down as necessary, so it ends at the specified
106 * address (rounded up to the nearest page boundary).
108 * \param a_info An \c area_info structure corresponding to the area on which
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).
116 resize_area_to_address(area_info
*a_info
, void *address
)
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);
121 return resize_area(a_info
->area
, new_size_aligned
);
126 * An internal method that sets the program break of the calling process to the
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.
137 brk_internal(void *addr
, area_info
*base_area_info
)
141 void *next_area_address
;
143 area_info next_area_info
;
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;
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
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
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.
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
192 area_info base_area_info
;
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
);
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.
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).
218 sbrk(intptr_t increment
)
222 area_info base_area_info
;
225 area_info next_area_info
;
227 uint8_t *program_break
;
228 uint8_t *new_program_break
;
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
234 next_area
= base_area_info
.area
;
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
);
242 } while (next_area
!= B_ERROR
&& result
== NULL
);
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
;
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 */
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
;
264 if (result
== (void *)-1)