Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5195 clevermous 1
MALLOC_ALIGNMENT = 8    ; must be power of two not less than 8, not greater than 4096
2
CHUNK_ALIGN_MASK = MALLOC_ALIGNMENT - 1
3
 
4
; -----------------------  Chunk representations ------------------------
5
;
6
;  (The following includes lightly edited explanations by Colin Plumb.)
7
;
8
;  The malloc_chunk declaration below is misleading (but accurate and
9
;  necessary).  It declares a "view" into memory allowing access to
10
;  necessary fields at known offsets from a given base.
11
;
12
;  Chunks of memory are maintained using a `boundary tag' method as
13
;  originally described by Knuth.  (See the paper by Paul Wilson
14
;  ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such
15
;  techniques.)  Sizes of free chunks are stored both in the front of
16
;  each chunk and at the end.  This makes consolidating fragmented
17
;  chunks into bigger chunks fast.  The head fields also hold bits
18
;  representing whether chunks are free or in use.
19
;
20
;  Here are some pictures to make it clearer.  They are "exploded" to
21
;  show that the state of a chunk can be thought of as extending from
22
;  the high 31 bits of the head field of its header through the
23
;  prev_foot and PINUSE_BIT bit of the following chunk header.
24
;
25
;  A chunk that's in use looks like:
26
;
27
;   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28
;           | Size of previous chunk (if P = 0)                             |
29
;           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30
;         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
31
;         | Size of this chunk                                         1| +-+
32
;   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33
;         |                                                               |
34
;         +-                                                             -+
35
;         |                                                               |
36
;         +-                                                             -+
37
;         |                                                               :
38
;         +-      size - sizeof(size_t) available payload bytes          -+
39
;         :                                                               |
40
; chunk-> +-                                                             -+
41
;         |                                                               |
42
;         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43
;       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|
44
;       | Size of next chunk (may or may not be in use)               | +-+
45
; mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46
;
47
;    And if it's free, it looks like this:
48
;
49
;   chunk-> +-                                                             -+
50
;           | User payload (must be in use, or we would have merged!)       |
51
;           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52
;         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
53
;         | Size of this chunk                                         0| +-+
54
;   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55
;         | Next pointer                                                  |
56
;         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57
;         | Prev pointer                                                  |
58
;         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59
;         |                                                               :
60
;         +-      size - sizeof(struct chunk) unused bytes               -+
61
;         :                                                               |
62
; chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63
;         | Size of this chunk                                            |
64
;         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65
;       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|
66
;       | Size of next chunk (must be in use, or we would have merged)| +-+
67
; mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68
;       |                                                               :
69
;       +- User payload                                                -+
70
;       :                                                               |
71
;       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72
;                                                                     |0|
73
;                                                                     +-+
74
;  Note that since we always merge adjacent free chunks, the chunks
75
;  adjacent to a free chunk must be in use.
76
;
77
;  Given a pointer to a chunk (which can be derived trivially from the
78
;  payload pointer) we can, in O(1) time, find out whether the adjacent
79
;  chunks are free, and if so, unlink them from the lists that they
80
;  are on and merge them with the current chunk.
81
;
82
;  Chunks always begin on even word boundaries, so the mem portion
83
;  (which is returned to the user) is also on an even word boundary, and
84
;  thus at least double-word aligned.
85
;
86
;  The P (PINUSE_BIT) bit, stored in the unused low-order bit of the
87
;  chunk size (which is always a multiple of two words), is an in-use
88
;  bit for the *previous* chunk.  If that bit is *clear*, then the
89
;  word before the current chunk size contains the previous chunk
90
;  size, and can be used to find the front of the previous chunk.
91
;  The very first chunk allocated always has this bit set, preventing
92
;  access to non-existent (or non-owned) memory. If pinuse is set for
93
;  any given chunk, then you CANNOT determine the size of the
94
;  previous chunk, and might even get a memory addressing fault when
95
;  trying to do so.
96
;
97
;  The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of
98
;  the chunk size redundantly records whether the current chunk is
99
;  inuse (unless the chunk is mmapped). This redundancy enables usage
100
;  checks within free and realloc, and reduces indirection when freeing
101
;  and consolidating chunks.
102
;
103
;  Each freshly allocated chunk must have both cinuse and pinuse set.
104
;  That is, each allocated chunk borders either a previously allocated
105
;  and still in-use chunk, or the base of its memory arena. This is
106
;  ensured by making all allocations from the `lowest' part of any
107
;  found chunk.  Further, no free chunk physically borders another one,
108
;  so each free chunk is known to be preceded and followed by either
109
;  inuse chunks or the ends of memory.
110
;
111
;  Note that the `foot' of the current chunk is actually represented
112
;  as the prev_foot of the NEXT chunk. This makes it easier to
113
;  deal with alignments etc but can be very confusing when trying
114
;  to extend or adapt this code.
115
;
116
;  The exceptions to all this are
117
;
118
;     1. The special chunk `top' is the top-most available chunk (i.e.,
119
;        the one bordering the end of available memory). It is treated
120
;        specially.  Top is never included in any bin, is used only if
121
;        no other chunk is available. In effect,
122
;        the top chunk is treated as larger (and thus less well
123
;        fitting) than any other available chunk.  The top chunk
124
;        doesn't update its trailing size field since there is no next
125
;        contiguous chunk that would have to index off it. However,
126
;        space is still allocated for it (TOP_FOOT_SIZE) to enable
127
;        separation or merging when space is extended.
128
;
129
;     2. Chunks allocated via mmap, have both cinuse and pinuse bits
130
;        cleared in their head fields.  Because they are allocated
131
;        one-by-one, each must carry its own prev_foot field, which is
132
;        also used to hold the offset this chunk has within its mmapped
133
;        region, which is needed to preserve alignment. Each mmapped
134
;        chunk is trailed by the first two fields of a fake next-chunk
135
;        for sake of usage checks.
136
;
137
struct malloc_chunk
138
prev_foot       dd      ?       ; size of previous chunk (if free)
139
head            dd      ?       ; size and inuse bits
140
data            rd      0       ; label for data (if busy)
141
fd              dd      ?       ; pointer to data in next malloc_chunk (if free)
142
bk              dd      ?       ; pointer to data in prev malloc_chunk (if free)
143
ends
144
mchunk_prev_foot = malloc_chunk.prev_foot - malloc_chunk.data
145
mchunk_head      = malloc_chunk.head      - malloc_chunk.data
146
mchunk_fd        = malloc_chunk.fd        - malloc_chunk.data
147
mchunk_bk        = malloc_chunk.bk        - malloc_chunk.data
148
 
149
; ------------------- Chunks sizes and alignments -----------------------
150
MCHUNK_SIZE = sizeof.malloc_chunk
151
if FOOTERS
152
CHUNK_OVERHEAD = 8
153
else
154
CHUNK_OVERHEAD = 4
155
end if
156
; MMapped chunks need a second word of overhead ...
157
MMAP_CHUNK_OVERHEAD = 8
158
; ... and additional padding for fake next-chunk at foot
159
MMAP_FOOT_PAD = 16
160
; The smallest size we can malloc is an aligned minimal chunk
161
MIN_CHUNK_SIZE = (MCHUNK_SIZE + CHUNK_ALIGN_MASK) and not CHUNK_ALIGN_MASK
162
; Bounds on request (not chunk) sizes.
163
MAX_REQUEST = (-MIN_CHUNK_SIZE) * 4
164
MIN_REQUEST = MIN_CHUNK_SIZE - CHUNK_OVERHEAD
165
 
166
; ------------------ Operations on head and foot fields -----------------
167
;  The head field of a chunk is or'ed with PINUSE_BIT when previous
168
;  adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
169
;  use, unless mmapped, in which case both bits are cleared.
170
;
171
;  FLAG4_BIT is not used by this malloc, but might be useful in extensions.
172
PINUSE_BIT = 1
173
CINUSE_BIT = 2
174
FLAG4_BIT = 4
175
INUSE_BITS = PINUSE_BIT + CINUSE_BIT
176
FLAG_BITS = PINUSE_BIT + CINUSE_BIT + FLAG4_BIT
177
; Head value for fenceposts
178
FENCEPOST_HEAD = 4 + INUSE_BITS
179
 
180
; ---------------------- Overlaid data structures -----------------------
181
;  When chunks are not in use, they are treated as nodes of either
182
;  lists or trees.
183
;
184
;  "Small"  chunks are stored in circular doubly-linked lists, and look
185
;  like this:
186
;
187
;    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
188
;            |             Size of previous chunk                            |
189
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
190
;    `head:' |             Size of chunk, in bytes                         |P|
191
;      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192
;            |             Forward pointer to next chunk in list             |
193
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194
;            |             Back pointer to previous chunk in list            |
195
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
196
;            |             Unused space (may be 0 bytes long)                .
197
;            .                                                               .
198
;            .                                                               |
199
;nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200
;    `foot:' |             Size of chunk, in bytes                           |
201
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
202
;
203
;  Larger chunks are kept in a form of bitwise digital trees (aka
204
;  tries) keyed on chunksizes.  Because malloc_tree_chunks are only for
205
;  free chunks greater than 256 bytes, their size doesn't impose any
206
;  constraints on user chunk sizes.  Each node looks like:
207
;
208
;    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209
;            |             Size of previous chunk                            |
210
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211
;    `head:' |             Size of chunk, in bytes                         |P|
212
;      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
213
;            |             Forward pointer to next chunk of same size        |
214
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
215
;            |             Back pointer to previous chunk of same size       |
216
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
217
;            |             Pointer to left child (child[0])                  |
218
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
219
;            |             Pointer to right child (child[1])                 |
220
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
221
;            |             Pointer to parent                                 |
222
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
223
;            |             bin index of this chunk                           |
224
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
225
;            |             Unused space                                      .
226
;            .                                                               |
227
;nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228
;    `foot:' |             Size of chunk, in bytes                           |
229
;            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
230
;
231
;  Each tree holding treenodes is a tree of unique chunk sizes.  Chunks
232
;  of the same size are arranged in a circularly-linked list, with only
233
;  the oldest chunk (the next to be used, in our FIFO ordering)
234
;  actually in the tree.  (Tree members are distinguished by a non-null
235
;  parent pointer.)  If a chunk with the same size an an existing node
236
;  is inserted, it is linked off the existing node using pointers that
237
;  work in the same way as fd/bk pointers of small chunks.
238
;
239
;  Each tree contains a power of 2 sized range of chunk sizes (the
240
;  smallest is 0x100 <= x < 0x180), which is is divided in half at each
241
;  tree level, with the chunks in the smaller half of the range (0x100
242
;  <= x < 0x140 for the top nose) in the left subtree and the larger
243
;  half (0x140 <= x < 0x180) in the right subtree.  This is, of course,
244
;  done by inspecting individual bits.
245
;
246
;  Using these rules, each node's left subtree contains all smaller
247
;  sizes than its right subtree.  However, the node at the root of each
248
;  subtree has no particular ordering relationship to either.  (The
249
;  dividing line between the subtree sizes is based on trie relation.)
250
;  If we remove the last chunk of a given size from the interior of the
251
;  tree, we need to replace it with a leaf node.  The tree ordering
252
;  rules permit a node to be replaced by any leaf below it.
253
;
254
;  The smallest chunk in a tree (a common operation in a best-fit
255
;  allocator) can be found by walking a path to the leftmost leaf in
256
;  the tree.  Unlike a usual binary tree, where we follow left child
257
;  pointers until we reach a null, here we follow the right child
258
;  pointer any time the left one is null, until we reach a leaf with
259
;  both child pointers null. The smallest chunk in the tree will be
260
;  somewhere along that path.
261
;
262
;  The worst case number of steps to add, find, or remove a node is
263
;  bounded by the number of bits differentiating chunks within
264
;  bins. Under current bin calculations, this ranges from 6 up to 21
265
;  (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
266
;  is of course much better.
267
;
268
;  All large chunks which have been extended after last call to sys_trim
269
;  are kept in a single-linked list with head inside malloc_state.
270
;  For a chunk not in the list, release_fd/release_bk point to itself.
271
struct malloc_tree_chunk malloc_chunk
272
left_child      dd      ?       ; pointer to malloc_tree_chunk.data
273
right_child     dd      ?       ; pointer to malloc_tree_chunk.data
274
parent          dd      ?       ; pointer to malloc_tree_chunk.data
275
index           dd      ?
276
ends
277
tchunk_left_child  = malloc_tree_chunk.left_child  - malloc_chunk.data
278
tchunk_right_child = malloc_tree_chunk.right_child - malloc_chunk.data
279
tchunk_parent      = malloc_tree_chunk.parent      - malloc_chunk.data
280
tchunk_index       = malloc_tree_chunk.index       - malloc_chunk.data
281
tchunk_release_fd  = mchunk_prev_foot - 8
282
tchunk_release_bk  = tchunk_release_fd + 4
283
 
284
; ----------------------------- Segments --------------------------------
285
;
286
;  Each malloc space may include non-contiguous segments, held in a
287
;  list headed by an embedded malloc_segment record representing the
288
;  top-most space. Large chunks that are directly allocated by mmap are not
289
;  included in this list. They are instead independently created and
290
;  destroyed without otherwise keeping track of them.
291
;
292
;  Except for the top-most segment of an mstate, each segment record
293
;  is kept at the tail of its segment. Segments are added by pushing
294
;  segment records onto the list headed by &mstate.seg for the
295
;  containing mstate.
296
;
297
struct malloc_segment
298
base            dd      ?       ; base address
299
size            dd      ?       ; allocated size
300
next            dd      ?       ; pointer to next malloc_segment
301
ends
302
 
303
; ---------------------------- malloc_state -----------------------------
304
;   A malloc_state holds all of the bookkeeping for a space.
305
;   The main fields are:
306
;
307
;  Top
308
;    The topmost chunk of the currently active segment. Its size is
309
;    cached in topsize.  The actual size of topmost space is
310
;    topsize+TOP_FOOT_SIZE, which includes space reserved for adding
311
;    fenceposts and segment records if necessary when getting more
312
;    space from the system.
313
;
314
;  Designated victim (dv)
315
;    This is the preferred chunk for servicing small requests that
316
;    don't have exact fits.  It is normally the chunk split off most
317
;    recently to service another small request.  Its size is cached in
318
;    dvsize. The link fields of this chunk are not maintained since it
319
;    is not kept in a bin.
320
;
321
;  SmallBins
322
;    An array of bin headers for free chunks.  These bins hold chunks
323
;    with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
324
;    chunks of all the same size, spaced 8 bytes apart.  To simplify
325
;    use in double-linked lists, each bin header acts as a malloc_chunk
326
;    pointing to the real first node, if it exists (else pointing to
327
;    itself).  This avoids special-casing for headers.  But to avoid
328
;    waste, we allocate only the fd/bk pointers of bins, and then use
329
;    repositioning tricks to treat these as the fields of a chunk.
330
;
331
;  TreeBins
332
;    Treebins are pointers to the roots of trees holding a range of
333
;    sizes. There are 2 equally spaced treebins for each power of two
334
;    from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
335
;    larger.
336
;
337
;  Bin maps
338
;    There is one bit map for small bins ("smallmap") and one for
339
;    treebins ("treemap).  Each bin sets its bit when non-empty, and
340
;    clears the bit when empty.  Bit operations are then used to avoid
341
;    bin-by-bin searching -- nearly all "search" is done without ever
342
;    looking at bins that won't be selected.  The bit maps
343
;    conservatively use 32 bits per map word, even if on 64bit system.
344
;    For a good description of some of the bit-based techniques used
345
;    here, see Henry S. Warren Jr's book "Hacker's Delight" (and
346
;    supplement at http://hackersdelight.org/). Many of these are
347
;    intended to reduce the branchiness of paths through malloc etc, as
348
;    well as to reduce the number of memory locations read or written.
349
;
350
;  Segments
351
;    A list of segments headed by an embedded malloc_segment record
352
;    representing the initial space.
353
;
354
;  Magic tag
355
;    A cross-check field that should always hold same value as malloc_magic.
356
;
357
;  Max allowed footprint
358
;    The maximum allowed bytes to allocate from system (zero means no limit)
359
;
360
;  Flags
361
;    Bits recording whether to use locks
362
;
363
;  Statistics
364
;    Each space keeps track of current and maximum system memory.
365
;
366
;  Trim support
367
;    Fields holding the amount of unused topmost memory that should trigger
368
;    trimming, and a counter to force periodic scanning to release unused
369
;    non-topmost segments.
370
;
371
;  Locking
372
;    If USE_LOCKS is defined, the "mutex" lock is acquired and released
373
;    around every public call using this mspace.
374
;typedef struct malloc_segment  msegment;
375
;typedef struct malloc_segment* msegmentptr;
376
NSMALLBINS              =       32
377
NTREEBINS               =       32
378
SMALLBIN_SHIFT          =       3
379
SMALLBIN_WIDTH          =       1 shl SMALLBIN_SHIFT
380
TREEBIN_SHIFT           =       8
381
MIN_LARGE_SIZE          =       1 shl TREEBIN_SHIFT
382
assert MIN_LARGE_SIZE = NSMALLBINS * SMALLBIN_WIDTH
383
MAX_SMALL_SIZE          =       MIN_LARGE_SIZE - 1
384
MAX_SMALL_REQUEST       =       MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD
385
 
386
struct malloc_state
387
smallmap        dd      ?       ; bitmap for non-empty small bins
388
treemap         dd      ?       ; bitmap for non-empty tree bins
389
dvsize          dd      ?       ; designated victim size
390
topsize         dd      ?       ; size of free top area
391
dv              dd      ?       ; pointer to malloc_chunk.data of designated victim
392
top             dd      ?       ; pointer to malloc_chunk.data of top area
393
topmax          dd      ?       ; maximum value of top after prev release check
394
release_checks  dd      ?       ; counter before next release check
395
release_list    rd      2       ; list of malloc_tree_chunk with pages to release
396
mmap_list       rd      2       ; list of mmapped chunks
397
magic           dd      ?       ; for FOOTERS
398
smallbins       rd      NSMALLBINS*2    ; pointers to malloc_chunk.data
399
treebins        rd      NTREEBINS       ; pointers to malloc_tree_chunk.data
400
footprint       dd      ?
401
max_footprint   dd      ?
402
footprint_limit dd      ?       ; zero means no limit
403
mmap_threshold  dd      ?
404
segment_size    dd      ?
405
mflags          dd      ?
406
mutex           dd      ?
407
seg             malloc_segment
408
ends
409
; Bits for malloc_state.mflags
410
USE_LOCK_BIT = 2
411
;
412
TOP_FOOT_SIZE = (-8) and CHUNK_ALIGN_MASK + (sizeof.malloc_segment + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) and not CHUNK_ALIGN_MASK + MIN_CHUNK_SIZE
413
SYS_ALLOC_PADDING = TOP_FOOT_SIZE + MALLOC_ALIGNMENT
414
 
415
; in: ebp -> malloc_state
416
; out: eax -> malloc_segment
417
macro segment_holding address
418
{
419
        lea     eax, [ebp+malloc_state.seg]
420
@@:
421
        mov     ecx, address
422
        sub     ecx, [eax+malloc_segment.base]
423
        cmp     ecx, [eax+malloc_segment.size]
424
        jb      @f
425
        mov     eax, [eax+malloc_segment.next]
426
        test    eax, eax
427
        jnz     @b
428
@@:
429
}
430
 
431
; in: ebp -> malloc_state
432
macro lock_heap
433
{
434
local .done
435
        test    [ebp+malloc_state.mflags], USE_LOCK_BIT
436
        jz      .done
437
; TODO: use mutex when it will be available in kernel API
438
@@:
439
        mov     eax, 1
440
        xchg    [ebp+malloc_state.mutex], eax
441
        test    eax, eax
442
        jz      .done
443
        mov     eax, 68
444
        mov     ebx, 1
445
        call    FS_SYSCALL_PTR
446
        jmp     @b
447
.done:
448
}
449
; in: ebp -> malloc_state
450
macro unlock_heap
451
{
452
local .done
453
        test    [ebp+malloc_state.mflags], USE_LOCK_BIT
454
        jz      .done
455
; TODO: use mutex when it will be available in kernel API
456
        mov     [ebp+malloc_state.mutex], 0
457
.done:
458
}
459
 
460
; ---------------------------- Indexing Bins ----------------------------
461
MIN_SMALL_INDEX = MIN_CHUNK_SIZE shr SMALLBIN_SHIFT
462
; in: sizereg = size, must be >= 1 shl TREEBIN_SHIFT
463
; out: ecx = index, sizereg = size << leftshift_for_tree_index
464
macro compute_tree_index sizereg
465
{
466
        mov     ecx, NTREEBINS-1
467
        cmp     sizereg, 0xC000 shl TREEBIN_SHIFT
468
        jae     @f
469
        bsr     ecx, sizereg
470
        ror     sizereg, cl
471
        adc     ecx, ecx
472
        lea     sizereg, [(sizereg-1)*2]
473
        sub     ecx, TREEBIN_SHIFT*2
474
@@:
475
}
476
; ----------------------- Runtime Check Support -------------------------
477
macro mark_inuse_foot where
478
{
479
if FOOTERS
480
        mov     ebx, ebp
481
        xor     ebx, [malloc_magic]
482
        mov     [where+mchunk_prev_foot], ebx
483
end if
484
}
485
 
486
; ----------------------- Operations on smallbins -----------------------
487
; Link a free chunk into a smallbin
488
; in: ebp -> malloc_state, esi -> malloc_chunk.data, edx = size
489
macro insert_small_chunk_noshift
490
{
491
        bts     [ebp+malloc_state.smallmap], edx
492
        mov     ecx, [ebp+malloc_state.smallbins+edx*8+mchunk_fd]
493
        lea     edx, [ebp+malloc_state.smallbins+edx*8]
494
        mov     [ecx+mchunk_bk], esi
495
        mov     [edx+mchunk_fd], esi
496
        mov     [esi+mchunk_fd], ecx
497
        mov     [esi+mchunk_bk], edx
498
}
499
macro insert_small_chunk
500
{
501
        shr     edx, SMALLBIN_SHIFT
502
        insert_small_chunk_noshift
503
}
504
 
505
; Unlink a chunk from a smallbin
506
; in: ebp -> malloc_state, eax -> malloc_chunk.data, edx = size
507
macro unlink_small_chunk
508
{
509
        mov     ecx, [eax+mchunk_fd]
510
        mov     ebx, [eax+mchunk_bk]
511
        cmp     [ecx+mchunk_bk], eax
512
        jnz     heap_corrupted
513
        cmp     [ebx+mchunk_fd], eax
514
        jnz     heap_corrupted
515
        mov     [ecx+mchunk_bk], ebx
516
        mov     [ebx+mchunk_fd], ecx
517
        cmp     ecx, ebx
518
        jnz     @f
519
        shr     edx, SMALLBIN_SHIFT
520
        btr     [ebp+malloc_state.smallmap], edx
521
@@:
522
}
523
 
524
; Unlink the first chunk from a smallbin
525
; in: ebp -> malloc_state, edx -> list header, eax -> malloc_chunk.data
526
; in: ecx = smallbin index
527
macro unlink_first_small_chunk
528
{
529
        mov     ebx, [eax+mchunk_fd]
530
        cmp     [ebx+mchunk_bk], eax
531
        jnz     heap_corrupted
532
        mov     [ebx+mchunk_bk], edx
533
        mov     [edx+mchunk_fd], ebx
534
        cmp     edx, ebx
535
        jnz     @f
536
        btr     [ebp+malloc_state.smallmap], ecx
537
@@:
538
}
539
 
540
; Replace dv node, binning the old one
541
; Used only when dvsize known to be small
542
; in: ebp -> malloc_state, ecx = chunk size, edx -> malloc_chunk.data
543
macro replace_dv
544
{
545
        mov     esi, [ebp+malloc_state.dv]
546
        mov     [ebp+malloc_state.dv], edx
547
        mov     edx, [ebp+malloc_state.dvsize]
548
        mov     [ebp+malloc_state.dvsize], ecx
549
        shr     edx, SMALLBIN_SHIFT
550
        jz      @f
551
        insert_small_chunk_noshift ; ebp,esi,edx
552
@@:
553
}
554
 
555
; ------------------------- Operations on trees -------------------------
556
; Insert chunk into tree
557
; in: ebp -> malloc_state, esi -> malloc_chunk.data, edx = size
558
macro insert_large_chunk return_action
559
{
560
local .not_first, .loop, .exact_size
561
        mov     edi, edx
562
        compute_tree_index edx
563
        lea     ebx, [ebp+malloc_state.treebins+ecx*4]
564
        mov     [esi+tchunk_index], ecx
565
        mov     dword [esi+tchunk_left_child], 0
566
        mov     dword [esi+tchunk_right_child], 0
567
        bts     [ebp+malloc_state.treemap], ecx
568
        jc      .not_first
569
        mov     [ebx], esi
570
        mov     [esi+tchunk_parent], ebx
571
        mov     [esi+mchunk_fd], esi
572
        mov     [esi+mchunk_bk], esi
573
        return_action
574
.not_first:
575
        mov     ebx, [ebx]
576
.loop:
577
        mov     ecx, [ebx+mchunk_head]
578
        and     ecx, not FLAG_BITS
579
        cmp     ecx, edi
580
        jz      .exact_size
581
        xor     ecx, ecx
582
        add     edx, edx
583
        adc     ecx, ecx
584
        lea     ecx, [ebx+tchunk_left_child+ecx*4]
585
        cmp     dword [ecx], 0
586
        jz      @f
587
        mov     ebx, [ecx]
588
        jmp     .loop
589
@@:
590
        mov     [ecx], esi
591
        mov     [esi+tchunk_parent], ebx
592
        mov     [esi+mchunk_fd], esi
593
        mov     [esi+mchunk_bk], esi
594
        return_action
595
.exact_size:
596
        mov     edx, [ebx+mchunk_fd]
597
        mov     [edx+mchunk_bk], esi
598
        mov     [ebx+mchunk_fd], esi
599
        mov     [esi+mchunk_fd], edx
600
        mov     [esi+mchunk_bk], ebx
601
        mov     dword [esi+tchunk_parent], 0
602
        return_action
603
}
604
 
605
;  Unlink steps:
606
;
607
;  1. If x is a chained node, unlink it from its same-sized fd/bk links
608
;     and choose its bk node as its replacement.
609
;  2. If x was the last node of its size, but not a leaf node, it must
610
;     be replaced with a leaf node (not merely one with an open left or
611
;     right), to make sure that lefts and rights of descendents
612
;     correspond properly to bit masks.  We use the rightmost descendent
613
;     of x.  We could use any other leaf, but this is easy to locate and
614
;     tends to counteract removal of leftmosts elsewhere, and so keeps
615
;     paths shorter than minimally guaranteed.  This doesn't loop much
616
;     because on average a node in a tree is near the bottom.
617
;  3. If x is the base of a chain (i.e., has parent links) relink
618
;     x's parent and children to x's replacement (or null if none).
619
 
620
; in: ebp -> malloc_state, eax -> malloc_tree_chunk.data
621
; destroys ebx,edx,esi, keeps eax,ecx
622
macro unlink_large_chunk
623
{
624
local .last, .common1, .find_rightmost_child, .done, .advance_right, .remove_root
625
        mov     esi, [eax+tchunk_parent]
626
        cmp     [eax+mchunk_bk], eax
627
        jz      .last
628
        mov     edx, [eax+mchunk_fd]
629
        mov     ebx, [eax+mchunk_bk]
630
        cmp     [edx+mchunk_bk], eax
631
        jnz     heap_corrupted
632
        cmp     [ebx+mchunk_fd], eax
633
        jnz     heap_corrupted
634
        mov     [edx+mchunk_bk], ebx
635
        mov     [ebx+mchunk_fd], edx
636
        jmp     .common1
637
.last:
638
        mov     ebx, [eax+tchunk_right_child]
639
        lea     edx, [eax+tchunk_right_child]
640
        test    ebx, ebx
641
        jnz     .find_rightmost_child
642
        mov     ebx, [eax+tchunk_left_child]
643
        lea     edx, [eax+tchunk_left_child]
644
        test    ebx, ebx
645
        jz      .common1
646
.find_rightmost_child:
647
        cmp     dword [ebx+tchunk_right_child], 0
648
        jnz     .advance_right
649
        cmp     dword [ebx+tchunk_left_child], 0
650
        jz      @f
651
        lea     edx, [ebx+tchunk_left_child]
652
        mov     ebx, [ebx+tchunk_left_child]
653
        jmp     .find_rightmost_child
654
.advance_right:
655
        lea     edx, [ebx+tchunk_right_child]
656
        mov     ebx, [ebx+tchunk_right_child]
657
        jmp     .find_rightmost_child
658
@@:
659
        mov     dword [edx], 0
660
.common1:
661
        test    esi, esi
662
        jz      .done
663
        mov     edx, [eax+tchunk_index]
664
        cmp     [ebp+malloc_state.treebins+edx*4], eax
665
        jz      .remove_root
666
        xor     edx, edx
667
        cmp     [esi+tchunk_left_child], eax
668
        setnz   dl
669
        mov     [esi+tchunk_left_child+edx*4], ebx
670
        jmp     @f
671
.remove_root:
672
        mov     [ebp+malloc_state.treebins+edx*4], ebx
673
        test    ebx, ebx
674
        jnz     @f
675
        btr     [ebp+malloc_state.treemap], edx
676
@@:
677
        test    ebx, ebx
678
        jz      .done
679
        mov     [ebx+tchunk_parent], esi
680
        cmp     dword [eax+tchunk_left_child], 0
681
        jz      @f
682
        mov     edx, [eax+tchunk_left_child]
683
        mov     [ebx+tchunk_left_child], edx
684
        mov     [edx+tchunk_parent], ebx
685
@@:
686
        cmp     dword [eax+tchunk_right_child], 0
687
        jz      @f
688
        mov     edx, [eax+tchunk_right_child]
689
        mov     [ebx+tchunk_right_child], edx
690
        mov     [edx+tchunk_parent], ebx
691
@@:
692
.done:
693
}
694
 
695
; Relays to large vs small bin operations
696
; in: ebp -> malloc_state, esi -> malloc_chunk.data, edx = size
697
macro insert_chunk return_action
698
{
699
local .large
700
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
701
        jae     .large
702
        insert_small_chunk      ; ebp, esi, edx
703
        return_action
704
.large:
705
        insert_large_chunk return_action        ; ebp, esi, edx
706
}
707
 
708
; in: ebp -> malloc_state, eax -> malloc_chunk.data, edx = size
709
macro unlink_chunk
710
{
711
local .large, .common
712
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
713
        jae     .large
714
        unlink_small_chunk      ; ebp, eax, edx
715
        jmp     .common
716
.large:
717
        unlink_large_chunk      ; ebp, eax
718
.common:
719
}
720
 
721
; -----------------------  Direct-mmapping chunks -----------------------
722
; Since a system call is used in these functions anyway,
723
; the speed is not of primary value here.
724
 
725
;   Directly mmapped chunks are set up with an offset to the start of
726
;  the mmapped region stored in the prev_foot field of the chunk. This
727
;  allows reconstruction of the required argument to MUNMAP when freed,
728
;  and also allows adjustment of the returned chunk to meet alignment
729
;  requirements (especially in memalign).
730
 
731
; in: ebp -> malloc_state, ecx = nb
732
; out: eax -> allocated block on success, 0 on failure
733
; destroys: eax, ebx, ecx
734
assert MALLOC_ALIGNMENT <= 4096 ; it is important here
735
proc mmap_alloc
736
        add     ecx, 8*4 + CHUNK_ALIGN_MASK + 0xFFF
737
        jc      .error
738
        and     ecx, not 0xFFF
739
        cmp     [ebp+malloc_state.footprint_limit], 0
740
        jz      @f
741
        mov     eax, [ebp+malloc_state.footprint]
742
        add     eax, ecx
743
        jc      .error
744
        cmp     eax, [ebp+malloc_state.footprint_limit]
745
        ja      .error
746
@@:
747
        mov     eax, 68
748
        mov     ebx, 12
749
        call    FS_SYSCALL_PTR
750
        test    eax, eax
751
        jz      .error
752
        lea     ebx, [ebp+malloc_state.mmap_list]
753
        mov     edx, [ebp+malloc_state.mmap_list]
754
        cmp     [edx+4], ebx
755
        jnz     heap_corrupted
756
        mov     [ebx], eax
757
        mov     [edx+4], eax
758
        mov     [eax], edx
759
        mov     [eax+4], ebx
760
        add     eax, ((-16) and CHUNK_ALIGN_MASK) + 16
761
        lea     edx, [ecx-((-16) and CHUNK_ALIGN_MASK)-8-MMAP_FOOT_PAD]
762
        mov     dword [eax+mchunk_prev_foot], ((-16) and CHUNK_ALIGN_MASK) + 16
763
        mov     [eax+mchunk_head], edx
764
        mark_inuse_foot eax+edx
765
        mov     dword [eax+edx+mchunk_head], FENCEPOST_HEAD
766
        mov     dword [eax+edx+4+mchunk_head], 0
767
        add     ecx, [ebp+malloc_state.footprint]
768
        mov     [ebp+malloc_state.footprint], ecx
769
        cmp     ecx, [ebp+malloc_state.max_footprint]
770
        jb      @f
771
        mov     [ebp+malloc_state.max_footprint], ecx
772
@@:
773
        ret
774
.error:
775
        xor     eax, eax
776
        ret
777
endp
778
 
779
; in: ebp -> malloc_state, ecx = nb, edx -> old malloc_chunk.data
780
; in: ebx = can_move: if zero, fail unless realloc in place is possible
781
proc mmap_resize
782
        mov     eax, [edx+mchunk_head]
783
        cmp     ecx, NSMALLBINS shl SMALLBIN_SHIFT
784
        jb      .error
785
        sub     eax, ecx
786
        jc      .realloc
787
        cmp     eax, 4
788
        jb      .realloc
789
        cmp     eax, 0x2000
790
        ja      .realloc
791
        mov     eax, edx
792
        ret
793
.realloc:
794
        test    ebx, ebx
795
        jz      .error
796
        sub     edx, [edx+mchunk_prev_foot]
797
        mov     eax, [edx]
798
        mov     ebx, [edx+4]
799
        cmp     [eax+4], edx
800
        jnz     heap_corrupted
801
        cmp     [ebx], edx
802
        jnz     heap_corrupted
803
        mov     [eax+4], ebx
804
        mov     [ebx], eax
805
        mov     eax, 68
806
        mov     ebx, 20
807
        add     ecx, 8*4 + CHUNK_ALIGN_MASK + 0xFFF
808
        and     ecx, not 0xFFF
809
        call    FS_SYSCALL_PTR
810
        test    eax, eax
811
        jz      .error
812
        mov     ebx, [eax]
813
        mov     [ebx+4], eax
814
        mov     ebx, [eax+4]
815
        mov     [ebx], eax
816
        add     eax, ((-16) and CHUNK_ALIGN_MASK)+16
817
        mov     edx, [eax+mchunk_head]
818
        add     edx, ((-16) and CHUNK_ALIGN_MASK)+8+MMAP_FOOT_PAD
819
        lea     ebx, [ecx-((-16) and CHUNK_ALIGN_MASK)-8-MMAP_FOOT_PAD]
820
        mov     [eax+mchunk_head], ebx
821
        mark_inuse_foot eax+ecx-((-16) and CHUNK_ALIGN_MASK)-8-MMAP_FOOT_PAD
822
        mov     dword [eax+ecx-((-16) and CHUNK_ALIGN_MASK)-8-MMAP_FOOT_PAD+mchunk_head], FENCEPOST_HEAD
823
        mov     dword [eax+ecx-((-16) and CHUNK_ALIGN_MASK)-8-MMAP_FOOT_PAD+4+mchunk_head], 0
824
        add     ecx, [ebp+malloc_state.footprint]
825
        sub     ecx, edx
826
        mov     [ebp+malloc_state.footprint], ecx
827
        cmp     ecx, [ebp+malloc_state.max_footprint]
828
        jb      @f
829
        mov     [ebp+malloc_state.max_footprint], ecx
830
@@:
831
        ret
832
.error:
833
        xor     eax, eax
834
        ret
835
endp
836
 
837
; -------------------------- mspace management --------------------------
838
 
839
; Initialize top chunk and its size
840
; in: eax -> malloc_state, ebx -> malloc_chunk.data for top, ecx = size
841
; ebx must be aligned to MALLOC_ALIGNMENT
842
macro init_top
843
{
844
        mov     [eax+malloc_state.top], ebx
845
        mov     [eax+malloc_state.topmax], ebx
846
        mov     [eax+malloc_state.topsize], ecx
847
        lea     edx, [ecx+PINUSE_BIT]
848
        mov     [ebx+mchunk_head], edx
849
        mov     dword [ebx+ecx+mchunk_head], TOP_FOOT_SIZE
850
}
851
 
852
; Same as init_top, but return the previous top
853
; in: ebp -> malloc_state, eax -> malloc_chunk.data for top, ecx = size
854
; out: edx -> malloc_chunk.data for old top
855
macro reinit_top
856
{
857
        lea     edx, [ecx+PINUSE_BIT]
858
        mov     [eax+mchunk_head], edx
859
        mov     dword [eax+ecx+mchunk_head], TOP_FOOT_SIZE
860
        mov     edx, [ebp+malloc_state.top]
861
        mov     [ebp+malloc_state.top], eax
862
        mov     [ebp+malloc_state.topmax], eax
863
        mov     [ebp+malloc_state.topsize], ecx
864
}
865
 
866
; Initialize bins for a new mstate that is otherwise zeroed out
867
; in: eax -> malloc_state
868
macro init_bins
869
{
870
        lea     ebx, [eax+malloc_state.smallbins]
871
        mov     edx, NSMALLBINS
872
@@:
873
        mov     [ebx+mchunk_fd], ebx
874
        mov     [ebx+mchunk_bk], ebx
875
        add     ebx, 8
876
        dec     edx
877
        jnz     @b
878
}
879
 
880
; Add a segment to hold a new noncontiguous region
881
; in: ebp -> malloc_state, eax = segment base, ecx = segment size
882
malloc_seg_chunk_size = (sizeof.malloc_segment + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) and not CHUNK_ALIGN_MASK
883
macro add_segment
884
{
885
local .nothing, .large
886
        push    edi
887
        push    eax ecx
888
; Ensure alignment for top
889
        add     eax, 8 + ((-8) and CHUNK_ALIGN_MASK)
890
        sub     ecx, TOP_FOOT_SIZE + ((-8) and CHUNK_ALIGN_MASK)
891
; reset top to new space
892
        reinit_top
893
        segment_holding edx
894
        mov     ecx, [eax+malloc_segment.base]
895
        add     ecx, [eax+malloc_segment.size]
896
        lea     ebx, [edx+MIN_CHUNK_SIZE]
897
        lea     eax, [ecx - malloc_seg_chunk_size - MALLOC_ALIGNMENT]
898
        cmp     eax, ebx
899
        jae     @f
900
        mov     eax, edx
901
@@:
902
; Set up segment record
903
        mov     dword [eax+mchunk_head], malloc_seg_chunk_size+PINUSE_BIT+CINUSE_BIT
904
        mark_inuse_foot eax+malloc_seg_chunk_size
905
; Push current record
906
        mov     ebx, [ebp+malloc_state.seg.base]
907
        mov     [eax+malloc_segment.base], ebx
908
        mov     ebx, [ebp+malloc_state.seg.size]
909
        mov     [eax+malloc_segment.size], ebx
910
        mov     ebx, [ebp+malloc_state.seg.next]
911
        mov     [eax+malloc_segment.next], ebx
912
        popd    [ebp+malloc_state.seg.size] [ebp+malloc_state.seg.base]
913
        mov     [ebp+malloc_state.seg.next], eax
914
; Insert trailing fenceposts
915
        mov     esi, edx
916
        sub     edx, eax
917
        lea     edi, [eax+malloc_seg_chunk_size+mchunk_head]
918
        mov     eax, FENCEPOST_HEAD
919
        sub     ecx, edi
920
        shr     ecx, 2
921
        rep stosd
922
; Insert the rest of old top into a bin as an ordinary free chunk
923
        neg     edx
924
        jz      .nothing
925
        and     dword [esi+edx+mchunk_head], not PINUSE_BIT
926
        lea     ecx, [edx+PINUSE_BIT]
927
        mov     [esi+mchunk_head], ecx
928
        mov     [esi+edx+mchunk_prev_foot], edx
929
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
930
        jb      .small
931
        lea     ecx, [esi+edx]
932
        mov     [ecx+tchunk_release_fd], ecx
933
        mov     [ecx+tchunk_release_bk], ecx
934
        insert_large_chunk        ; ebp, esi, edx
935
.small:
936
        insert_small_chunk      ; ebp, esi, edx
937
.nothing:
938
        pop     edi
939
}
940
 
941
; -------------------------- System allocation --------------------------
942
 
943
; Get memory from system using MMAP
944
; in: ebp -> malloc_state, esi = size
945
 
946
; Allocate a new segment and a chunk inside that segment.
947
; Algorithm of segment size calculation:
948
; 1. Calculate minimal size required to
949
;    successful subsequent allocation of a chunk.
950
; 2. Set maximum of it and malloc_state.segment_size as the new size.
951
; 3. If footprint limit is set and the selected size would overflow it,
952
;    decrease to the maximum allowed by the limit.
953
; 4. If the selected size is less than the minimal size, fail.
954
; 5. Call the kernel. If failed, retry several times halving
955
;    the requested size until either allocation succeeds or
956
;    the minimal size is reached; in the former case, try one last time
957
;    requesting the minimal size. If failed, pass the fail to the caller.
958
; 6. If allocation succeeded, add the final size to malloc_state.segment_size
959
;    for subsequent allocations.
960
proc sys_alloc
961
        call    trim_top
962
        mov     edx, esi
963
        add     edx, SYS_ALLOC_PADDING + 0xFFF
964
        jc      .fail
965
        and     edx, not 0xFFF
966
; edx = minimal memory required
967
        mov     ecx, [ebp+malloc_state.segment_size]
968
        cmp     ecx, edx
969
        ja      @f
970
        mov     ecx, edx
971
@@:
972
        mov     ebx, [ebp+malloc_state.footprint_limit]
973
        test    ebx, ebx
974
        jz      @f
975
        sub     ebx, [ebp+malloc_state.footprint]
976
        cmp     ecx, ebx
977
        jb      @f
978
        mov     ecx, ebx
979
@@:
980
        cmp     ecx, edx
981
        jb      .fail
982
.retry:
983
        mov     eax, 68
984
        mov     ebx, 12
985
        call    FS_SYSCALL_PTR
986
        test    eax, eax
987
        jnz     .ok
988
        cmp     ecx, edx
989
        jbe     .fail
990
        shr     ecx, 1
991
        cmp     ecx, edx
992
        jae     .retry
993
        mov     ecx, edx
994
        jmp     .retry
995
.fail:
996
        mov     FS_ERRNO, ENOMEM
997
        xor     eax, eax
998
        ret
999
.ok:
1000
        add     [ebp+malloc_state.segment_size], ecx
1001
        add     [ebp+malloc_state.footprint], ecx
1002
        mov     ebx, [ebp+malloc_state.footprint]
1003
        cmp     ebx, [ebp+malloc_state.max_footprint]
1004
        jb      @f
1005
        mov     [ebp+malloc_state.max_footprint], ebx
1006
@@:
1007
        push    esi
1008
        add_segment     ; ebp, eax, ecx
1009
        pop     esi
1010
        mov     ecx, [ebp+malloc_state.topsize]
1011
        sub     ecx, esi
1012
        jbe     .fail
1013
        mov     [ebp+malloc_state.topsize], ecx
1014
        mov     eax, [ebp+malloc_state.top]
1015
        add     [ebp+malloc_state.top], esi
1016
        add     [ebp+malloc_state.topmax], esi
1017
        lea     edx, [ecx+PINUSE_BIT]
1018
        mov     [eax+esi+mchunk_head], edx
1019
        lea     edx, [esi+PINUSE_BIT+CINUSE_BIT]
1020
        mov     [eax+mchunk_head], edx
1021
        mark_inuse_foot eax+esi
1022
        ret
1023
endp
1024
 
1025
; -----------------------  system deallocation --------------------------
1026
 
1027
proc chunk_trim
1028
        add     edx, 0xFFF
1029
        and     edx, not 0xFFF
1030
        and     esi, not 0xFFF
1031
        sub     esi, edx
1032
        jbe     .nothing
1033
        segment_holding edx
1034
        mov     ecx, [eax+malloc_segment.base]
1035
        mov     eax, 68
1036
        mov     ebx, 26
1037
        sub     edx, ecx
1038
        call    FS_SYSCALL_PTR
1039
.nothing:
1040
        ret
1041
endp
1042
 
1043
proc trim_top
1044
        mov     edx, [ebp+malloc_state.top]
1045
        xor     edx, [ebp+malloc_state.topmax]
1046
        test    edx, not 0xFFF
1047
        jz      .nothing
1048
        push    esi
1049
        mov     edx, [ebp+malloc_state.top]
1050
        mov     esi, [ebp+malloc_state.topmax]
1051
        mov     [ebp+malloc_state.topmax], edx
1052
        add     esi, 0xFFF
1053
        call    chunk_trim
1054
        pop     esi
1055
.nothing:
1056
        ret
1057
endp
1058
 
1059
proc sys_trim
1060
        call    trim_top
1061
        push    edi
1062
        lea     edi, [ebp+malloc_state.release_list-tchunk_release_fd]
1063
        mov     eax, [ebp+malloc_state.release_list]
1064
        push    edi
1065
.release_list:
1066
        cmp     eax, [esp]
1067
        jz      .release_done
1068
        mov     edi, eax
1069
        mov     esi, [eax+mchunk_prev_foot]
1070
        sub     eax, [eax+mchunk_prev_foot]
1071
        lea     edx, [eax+sizeof.malloc_tree_chunk-malloc_chunk.data]
1072
        lea     esi, [eax+esi+tchunk_release_fd]
1073
        call    chunk_trim
1074
        mov     eax, [edi+tchunk_release_fd]
1075
        mov     [edi+tchunk_release_fd], edi
1076
        mov     [edi+tchunk_release_bk], edi
1077
        jmp     .release_list
1078
.release_done:
1079
        mov     [ebp+malloc_state.release_list+0], eax
1080
        mov     [ebp+malloc_state.release_list+4], eax
1081
        pop     edi edi
1082
        mov     [ebp+malloc_state.release_checks], RELEASE_CHECK_RATE
1083
        ret
1084
endp
1085
 
1086
macro trim_if_should
1087
{
1088
        dec     [ebp+malloc_state.release_checks]
1089
        jnz     @f
1090
        call    sys_trim
1091
@@:
1092
}
1093
 
1094
; ---------------------------- malloc ---------------------------
1095
 
1096
; allocate a large request from the best fitting chunk in a treebin
1097
; if succeeded, unlock the heap and return
1098
; if failed, go ahead
1099
; in: ebp -> malloc_state, esi = nb
1100
macro tmalloc_large_and_return
1101
{
1102
local .find_in_bin, .follow_right, .try_next_bin, .found_subtree, .found_match, .no_new_chunk, .follow_left, .nothing
1103
        push    edi
1104
macro ret \{
1105
        pop     edi
1106
        ret
1107
\}
1108
        push    0
1109
        push    0
1110
        mov     eax, esi
1111
        compute_tree_index eax
1112
; ecx = idx, eax = sizebits
1113
        sub     [esp], esi
1114
; edx = rsize
1115
        cmp     [ebp+malloc_state.treebins+ecx*4], 0
1116
        jz      .try_next_bin
1117
; Traverse tree for this bin looking for node with size == nb
1118
        mov     edi, [ebp+malloc_state.treebins+ecx*4]
1119
        xor     ebx, ebx        ; The deepest untaken right subtree
1120
.find_in_bin:
1121
        mov     edx, [edi+mchunk_head]
1122
        and     edx, not FLAG_BITS
1123
        sub     edx, esi
1124
        cmp     [esp], edx
1125
        jbe     @f
1126
        mov     [esp], edx
1127
        mov     [esp+4], edi
1128
        test    edx, edx
1129
        jz      .found_match
1130
@@:
1131
        add     eax, eax
1132
        jc      .follow_right
1133
        cmp     dword [edi+tchunk_right_child], 0
1134
        jz      @f
1135
        mov     ebx, [edi+tchunk_right_child]
1136
@@:
1137
        cmp     dword [edi+tchunk_left_child], 0
1138
        mov     edi, [edi+tchunk_left_child]
1139
        jnz     .find_in_bin
1140
        jmp     @f
1141
.follow_right:
1142
        cmp     dword [edi+tchunk_right_child], 0
1143
        mov     edi, [edi+tchunk_right_child]
1144
        jnz     .find_in_bin
1145
@@:
1146
        mov     edi, ebx        ; set t to least subtree holding sizes > nb
1147
        test    ebx, ebx
1148
        jnz     .found_subtree
1149
        cmp     dword [esp+4], 0
1150
        jnz     .found_match
1151
.try_next_bin:
1152
; set t to root of next non-empty treebin
1153
        mov     eax, [ebp+malloc_state.treemap]
1154
        shr     eax, cl
1155
        shr     eax, 1
1156
        jz      .nothing
1157
        bsf     eax, eax
1158
        add     eax, ecx
1159
        mov     edi, [ebp+malloc_state.treebins+(eax+1)*4]
1160
.found_subtree:
1161
; find smallest of tree or subtree
1162
        mov     edx, [edi+mchunk_head]
1163
        and     edx, not FLAG_BITS
1164
        sub     edx, esi
1165
        cmp     [esp], edx
1166
        jbe     @f
1167
        mov     [esp], edx
1168
        mov     [esp+4], edi
1169
@@:
1170
        cmp     dword [edi+tchunk_left_child], 0
1171
        jnz     .follow_left
1172
        cmp     dword [edi+tchunk_right_child], 0
1173
        mov     edi, [edi+tchunk_right_child]
1174
        jnz     .found_subtree
1175
        cmp     dword [esp+4], 0
1176
        jz      .nothing
1177
.found_match:
1178
; If dv is a better fit, return 0 so malloc will use it
1179
        mov     eax, [ebp+malloc_state.dvsize]
1180
        sub     eax, esi
1181
        cmp     [esp], eax
1182
        jae     .nothing
1183
        mov     eax, [esp+4]
1184
        cmp     dword [esp], NSMALLBINS shl SMALLBIN_SHIFT
1185
        jae     @f
1186
        mov     ecx, [esp]
1187
        add     ecx, esi
1188
        add     ecx, eax
1189
        mov     ebx, [ecx+tchunk_release_fd]
1190
        mov     edx, [ecx+tchunk_release_bk]
1191
        cmp     [ebx+tchunk_release_bk], ecx
1192
        jnz     heap_corrupted
1193
        cmp     [edx+tchunk_release_fd], ecx
1194
        jnz     heap_corrupted
1195
        mov     [ebx+tchunk_release_bk], edx
1196
        mov     [edx+tchunk_release_fd], ebx
1197
@@:
1198
        mov     ecx, esi
1199
        unlink_large_chunk      ; ebp, eax
1200
        cmp     dword [esp], MIN_CHUNK_SIZE
1201
        jb      .no_new_chunk
1202
        lea     edx, [ecx+PINUSE_BIT+CINUSE_BIT]
1203
        mov     [eax+mchunk_head], edx
1204
        mark_inuse_foot eax+ecx
1205
        lea     esi, [eax+ecx]
1206
        mov     edx, [esp]
1207
        lea     ecx, [edx+PINUSE_BIT]
1208
        mov     [esi+mchunk_head], ecx
1209
        mov     [esi+edx+mchunk_prev_foot], edx
1210
        add     esp, 8
1211
macro unlock_ret \{
1212
        unlock_heap
1213
        ret
1214
\}
1215
        insert_chunk unlock_ret ; ebp, esi, edx
1216
purge unlock_ret
1217
.no_new_chunk:
1218
        add     ecx, [esp]
1219
        lea     edx, [ecx+PINUSE_BIT+CINUSE_BIT]
1220
        mov     [eax+mchunk_head], edx
1221
        or      dword [eax+ecx+mchunk_head], PINUSE_BIT
1222
        mark_inuse_foot eax+ecx
1223
        add     esp, 8
1224
        unlock_heap
1225
        ret
1226
.follow_left:
1227
        mov     edi, [edi+tchunk_left_child]
1228
        jmp     .found_subtree
1229
.nothing:
1230
        add     esp, 8
1231
        pop     edi
1232
purge ret
1233
}
1234
 
1235
; allocate a small request from the best fitting chunk in a treebin
1236
; in: ebp -> malloc_state, esi = size
1237
macro tmalloc_small_and_return
1238
{
1239
local .follow_left, .found_best, .no_new_chunk
1240
        bsf     ecx, [ebp+malloc_state.treemap]
1241
        mov     eax, [ebp+malloc_state.treebins+ecx*4]
1242
        mov     edx, eax
1243
        mov     ecx, [eax+mchunk_head]
1244
        and     ecx, not FLAG_BITS
1245
        sub     ecx, esi
1246
@@:
1247
        cmp     dword [edx+tchunk_left_child], 0
1248
        jnz     .follow_left
1249
        cmp     dword [edx+tchunk_right_child], 0
1250
        jz      .found_best
1251
        mov     edx, [edx+tchunk_right_child]
1252
        mov     ebx, [edx+mchunk_head]
1253
        and     ebx, not FLAG_BITS
1254
        sub     ebx, esi
1255
        cmp     ecx, ebx
1256
        jbe     @b
1257
        mov     eax, edx
1258
        mov     ecx, ebx
1259
        jmp     @b
1260
.follow_left:
1261
        mov     edx, [edx+tchunk_left_child]
1262
        mov     ebx, [edx+mchunk_head]
1263
        and     ebx, not FLAG_BITS
1264
        sub     ebx, esi
1265
        cmp     ecx, ebx
1266
        jbe     @b
1267
        mov     eax, edx
1268
        mov     ecx, ebx
1269
        jmp     @b
1270
.found_best:
1271
        push    esi
1272
        cmp     ecx, NSMALLBINS shl SMALLBIN_SHIFT
1273
        jae     @f
1274
        add     esi, ecx
1275
        add     esi, eax
1276
        mov     ebx, [esi+tchunk_release_fd]
1277
        mov     edx, [esi+tchunk_release_bk]
1278
        cmp     [ebx+tchunk_release_bk], esi
1279
        jnz     heap_corrupted
1280
        cmp     [edx+tchunk_release_fd], esi
1281
        jnz     heap_corrupted
1282
        mov     [ebx+tchunk_release_bk], edx
1283
        mov     [edx+tchunk_release_fd], ebx
1284
@@:
1285
        unlink_large_chunk      ; ebp, eax
1286
        pop     esi
1287
        cmp     ecx, MIN_CHUNK_SIZE
1288
        jb      .no_new_chunk
1289
        lea     edx, [eax+esi]
1290
        lea     ebx, [esi+PINUSE_BIT+CINUSE_BIT]
1291
        mov     [eax+mchunk_head], ebx
1292
        mark_inuse_foot edx
1293
        lea     ebx, [ecx+PINUSE_BIT]
1294
        mov     [edx+mchunk_head], ebx
1295
        mov     [edx+ecx+mchunk_prev_foot], ecx
1296
        replace_dv      ; ebp, ecx, edx
1297
        unlock_heap
1298
        ret
1299
.no_new_chunk:
1300
        lea     edx, [ecx+esi+PINUSE_BIT+CINUSE_BIT]
1301
        add     ecx, esi
1302
        mov     [eax+mchunk_head], edx
1303
        or      dword [eax+ecx+mchunk_head], PINUSE_BIT
1304
        mark_inuse_foot eax+ecx
1305
        unlock_heap
1306
        ret
1307
}
1308
 
1309
; in: ebp -> malloc_state
1310
; in: [bytes] = stack var with number of bytes to allocate
1311
; out: eax -> allocated data
1312
macro do_malloc
1313
{
1314
;
1315
;     Basic algorithm:
1316
;     If a small request (< 256 bytes minus per-chunk overhead):
1317
;       1. If one exists, use a remainderless chunk in associated smallbin.
1318
;          (Remainderless means that there are too few excess bytes to
1319
;          represent as a chunk.)
1320
;       2. If it is big enough, use the dv chunk, which is normally the
1321
;          chunk adjacent to the one used for the most recent small request.
1322
;       3. If one exists, split the smallest available chunk in a bin,
1323
;          saving remainder in dv.
1324
;       4. If it is big enough, use the top chunk.
1325
;       5. If available, get memory from system and use it
1326
;     Otherwise, for a large request:
1327
;       1. Find the smallest available binned chunk that fits, and use it
1328
;          if it is better fitting than dv chunk, splitting if necessary.
1329
;       2. If better fitting than any binned chunk, use the dv chunk.
1330
;       3. If request size >= mmap threshold, try to directly mmap this chunk.
1331
;       4. If it is big enough, use the top chunk.
1332
;       5. If available, get memory from system and use it
1333
;
1334
        lock_heap
1335
        mov     eax, [bytes]
1336
        cmp     [bytes], MAX_SMALL_REQUEST
1337
        ja      .large
1338
        mov     ecx, MIN_CHUNK_SIZE
1339
        cmp     eax, MIN_REQUEST
1340
        jb      @f
1341
        lea     ecx, [eax + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK]
1342
        and     ecx, not CHUNK_ALIGN_MASK
1343
@@:
1344
        mov     esi, ecx
1345
        shr     ecx, SMALLBIN_SHIFT
1346
        mov     eax, [ebp + malloc_state.smallmap]
1347
        shr     eax, cl
1348
        test    eax, 3
1349
        jz      .small.remainder
1350
; Remainderless fit to a smallbin.
1351
        shr     eax, 1
1352
        sbb     ecx, -1         ; Uses next bin if idx empty
1353
        lea     edx, [ebp + malloc_state.smallbins + ecx*8]
1354
        mov     eax, [ebp + malloc_state.smallbins + ecx*8 + mchunk_fd]
1355
        unlink_first_small_chunk        ; ebp, edx, eax, ecx
1356
        lea     edx, [ecx*SMALLBIN_WIDTH+PINUSE_BIT+CINUSE_BIT]
1357
        mov     [eax+mchunk_head], edx
1358
        or      dword [eax+ecx*SMALLBIN_WIDTH+mchunk_head], PINUSE_BIT
1359
        mark_inuse_foot eax+ecx*SMALLBIN_WIDTH
1360
        unlock_heap
1361
        ret
1362
.small.remainder:
1363
        cmp     esi, [ebp+malloc_state.dvsize]
1364
        jbe     .use_dv_chunk
1365
        test    eax, eax
1366
        jz      .small.try_split_large
1367
        bsf     eax, eax
1368
        add     ecx, eax
1369
        lea     edx, [ebp + malloc_state.smallbins + ecx*8]
1370
        mov     eax, [ebp + malloc_state.smallbins + ecx*8 + mchunk_fd]
1371
        unlink_first_small_chunk        ; ebp, edx, eax, ecx
1372
        lea     edx, [esi+PINUSE_BIT+CINUSE_BIT]
1373
        mov     [eax+mchunk_head], edx
1374
        lea     edx, [eax+esi]
1375
        shl     ecx, SMALLBIN_SHIFT
1376
        sub     ecx, esi
1377
        mark_inuse_foot edx
1378
        lea     ebx, [ecx+PINUSE_BIT]
1379
        mov     [edx+mchunk_head], ebx
1380
        mov     [edx+ecx+mchunk_prev_foot], ecx
1381
        replace_dv      ; ebp, ecx, edx
1382
        unlock_heap
1383
        ret
1384
.use_dv_chunk:
1385
        mov     ecx, [ebp+malloc_state.dvsize]
1386
        mov     eax, [ebp+malloc_state.dv]
1387
        sub     ecx, esi
1388
        cmp     [ebp+malloc_state.dvsize], NSMALLBINS shl SMALLBIN_SHIFT
1389
        jb      @f
1390
        cmp     ecx, NSMALLBINS shl SMALLBIN_SHIFT
1391
        jae     @f
1392
        lea     edx, [eax+esi]
1393
        add     edx, ecx
1394
        mov     ebx, [edx+tchunk_release_fd]
1395
        cmp     [ebx+tchunk_release_bk], edx
1396
        jnz     heap_corrupted
1397
        mov     ebx, [edx+tchunk_release_bk]
1398
        cmp     [ebx+tchunk_release_fd], edx
1399
        jnz     heap_corrupted
1400
        mov     edx, [edx+tchunk_release_fd]
1401
        mov     [ebx+tchunk_release_fd], edx
1402
        mov     [edx+tchunk_release_bk], ebx
1403
@@:
1404
        cmp     ecx, MIN_CHUNK_SIZE
1405
        jb      .no_new_chunk_dv
1406
        lea     edx, [eax+esi]
1407
        mov     [ebp+malloc_state.dv], edx
1408
        mov     [ebp+malloc_state.dvsize], ecx
1409
        lea     ebx, [ecx+PINUSE_BIT]
1410
        mov     [edx+mchunk_head], ebx
1411
        mov     [edx+ecx+mchunk_prev_foot], ecx
1412
        lea     ebx, [esi+PINUSE_BIT+CINUSE_BIT]
1413
        mov     [eax+mchunk_head], ebx
1414
        mark_inuse_foot edx
1415
        unlock_heap
1416
        ret
1417
.no_new_chunk_dv:
1418
        add     ecx, esi
1419
        mov     [ebp+malloc_state.dv], 0
1420
        mov     [ebp+malloc_state.dvsize], 0
1421
        lea     ebx, [ecx+PINUSE_BIT+CINUSE_BIT]
1422
        mov     [eax+mchunk_head], ebx
1423
        or      dword [eax+ecx+mchunk_head], PINUSE_BIT
1424
        mark_inuse_foot eax+ecx
1425
        unlock_heap
1426
        ret
1427
.small.try_split_large:
1428
        cmp     [ebp+malloc_state.treemap], 0
1429
        jz      .from_top_or_sys
1430
        tmalloc_small_and_return
1431
.large:
1432
        cmp     eax, MAX_REQUEST
1433
        jae     .fail
1434
        lea     esi, [eax + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK]
1435
        and     esi, not CHUNK_ALIGN_MASK
1436
        cmp     [ebp+malloc_state.treemap], 0
1437
        jz      .from_dv_top_or_sys
1438
        tmalloc_large_and_return        ; can fail
1439
.from_dv_top_or_sys:
1440
        cmp     esi, [ebp+malloc_state.dvsize]
1441
        jbe     .use_dv_chunk
1442
.from_top_or_sys:
1443
; Directly map large chunks
1444
        cmp     esi, [ebp+malloc_state.mmap_threshold]
1445
        jb      @f
1446
        mov     ecx, esi
1447
        call    mmap_alloc
1448
        test    eax, eax
1449
        jz      @f
1450
        unlock_heap
1451
        ret
1452
@@:
1453
        cmp     esi, [ebp+malloc_state.topsize]
1454
        jae     .from_sys
1455
        mov     eax, [ebp+malloc_state.top]
1456
        mov     ecx, [ebp+malloc_state.topsize]
1457
        lea     edx, [eax+esi]
1458
        sub     ecx, esi
1459
        mov     [ebp+malloc_state.top], edx
1460
        cmp     edx, [ebp+malloc_state.topmax]
1461
        jb      @f
1462
        mov     [ebp+malloc_state.topmax], edx
1463
@@:
1464
        mov     [ebp+malloc_state.topsize], ecx
1465
        lea     ebx, [ecx+PINUSE_BIT]
1466
        mov     [edx+mchunk_head], ebx
1467
        lea     ebx, [esi+PINUSE_BIT+CINUSE_BIT]
1468
        mov     [eax+mchunk_head], ebx
1469
        mark_inuse_foot edx
1470
        unlock_heap
1471
        ret
1472
.from_sys:
1473
        call    sys_alloc
1474
        unlock_heap
1475
        ret
1476
.fail:
1477
        mov     FS_ERRNO, ENOMEM
1478
        xor     eax, eax
1479
        unlock_heap
1480
        ret
1481
}
1482
 
1483
; ---------------------------- free ---------------------------
1484
 
1485
; in: ebp -> malloc_state
1486
; in: [mem] = stack var with pointer to free
1487
macro do_free
1488
{
1489
;     Consolidate freed chunks with preceeding or succeeding bordering
1490
;     free chunks, if they exist, and then place in a bin.  Intermixed
1491
;     with special cases for top, dv, mmapped chunks, and usage errors.
1492
        cmp     [mem], 0
1493
        jz      .nothing
1494
if FOOTERS
1495
        mov     ecx, [mem]
1496
        mov     eax, [ecx+mchunk_head]
1497
        and     eax, not FLAG_BITS
1498
        mov     eax, [ecx+eax+mchunk_prev_foot]
1499
        xor     eax, [malloc_magic]
1500
        cmp     eax, ebp
1501
        jnz     heap_corrupted
1502
end if
1503
        lock_heap
1504
        mov     eax, [mem]
1505
        mov     ecx, [eax+mchunk_head]
1506
        mov     edx, ecx
1507
        and     ecx, INUSE_BITS
1508
assert PINUSE_BIT = 1
1509
assert CINUSE_BIT = 2
1510
; Possible values of ecx:
1511
; 0 = mmapped chunk, should be rare
1512
; 1 = illegal, trying to double-free
1513
; 2 = previous chunk is not in use, merge backwards
1514
; 3 = previous chunk is in use, no merge backwards
1515
        and     edx, not FLAG_BITS
1516
        cmp     ecx, 2
1517
        ja      .see_forward
1518
        jb      .free_mmapped
1519
; turn CINUSE_BIT to PINUSE_BIT, increase chances to detect double-free
1520
        dec     dword [eax+mchunk_head]
1521
        cmp     dword [eax+mchunk_prev_foot], NSMALLBINS shl SMALLBIN_SHIFT
1522
        jb      @f
1523
        mov     ebx, [eax+tchunk_release_fd]
1524
        mov     ecx, [eax+tchunk_release_bk]
1525
        cmp     [ebx+tchunk_release_bk], eax
1526
        jnz     heap_corrupted
1527
        cmp     [ecx+tchunk_release_fd], eax
1528
        jnz     heap_corrupted
1529
        mov     [ebx+tchunk_release_bk], ecx
1530
        mov     [ecx+tchunk_release_fd], ebx
1531
@@:
1532
        mov     ecx, [eax+mchunk_prev_foot]
1533
        add     edx, [eax+mchunk_prev_foot]
1534
        sub     eax, [eax+mchunk_prev_foot]
1535
        cmp     eax, [ebp+malloc_state.dv]
1536
        jz      .append_dv
1537
        push    edx
1538
        mov     edx, ecx
1539
        unlink_chunk    ; ebp, eax, edx
1540
        pop     edx
1541
.see_forward:
1542
        mov     esi, eax
1543
assert PINUSE_BIT = 1
1544
        btr     dword [eax+edx+mchunk_head], 0
1545
        jnc     heap_corrupted
1546
        test    dword [eax+edx+mchunk_head], CINUSE_BIT
1547
        jnz     .consolidated
1548
.consolidate_forward:
1549
        add     eax, edx
1550
        cmp     eax, [ebp+malloc_state.top]
1551
        jz      .prepend_top
1552
        cmp     eax, [ebp+malloc_state.dv]
1553
        jz      .prepend_dv
1554
        push    esi edx
1555
        mov     edx, [eax+mchunk_head]
1556
        and     edx, not FLAG_BITS
1557
        add     [esp], edx
1558
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1559
        jae     .consolidate_large
1560
        unlink_small_chunk      ; ebp, eax, edx
1561
        jmp     .consolidate_common
1562
.consolidate_large:
1563
        lea     ecx, [eax+edx]
1564
        mov     ebx, [eax+edx+tchunk_release_fd]
1565
        mov     edx, [eax+edx+tchunk_release_bk]
1566
        cmp     [ebx+tchunk_release_bk], ecx
1567
        jnz     heap_corrupted
1568
        cmp     [edx+tchunk_release_fd], ecx
1569
        jnz     heap_corrupted
1570
        mov     [ebx+tchunk_release_bk], edx
1571
        mov     [edx+tchunk_release_fd], ebx
1572
        unlink_large_chunk      ; ebp, eax
1573
.consolidate_common:
1574
        pop     edx esi
1575
        cmp     esi, [ebp+malloc_state.dv]
1576
        jz      .dv_consolidated
1577
.consolidated:
1578
        lea     ecx, [edx+PINUSE_BIT]
1579
        mov     [esi+mchunk_head], ecx
1580
        mov     [esi+edx+mchunk_prev_foot], edx
1581
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1582
        jae     .large
1583
        insert_small_chunk      ; ebp, esi, edx
1584
        unlock_heap
1585
.nothing:
1586
        ret
1587
.large:
1588
        lea     ecx, [esi+edx]
1589
        lea     eax, [ebp+malloc_state.release_list-tchunk_release_fd]
1590
        mov     ebx, [ebp+malloc_state.release_list]
1591
        cmp     [ebx+tchunk_release_bk], eax
1592
        jnz     heap_corrupted
1593
        mov     [ecx+tchunk_release_fd], ebx
1594
        mov     [ecx+tchunk_release_bk], eax
1595
        mov     [ebx+tchunk_release_bk], ecx
1596
        mov     [eax+tchunk_release_fd], ecx
1597
@@:
1598
        push    edi
1599
macro trim_ret \{
1600
        trim_if_should
1601
        unlock_heap
1602
        pop     edi
1603
        ret
1604
\}
1605
        insert_large_chunk trim_ret     ; ebp, esi, edx
1606
purge trim_ret
1607
.dv_consolidated:
1608
        mov     [esi+edx+mchunk_prev_foot], edx
1609
        mov     [ebp+malloc_state.dvsize], edx
1610
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1611
        jb      @f
1612
        lea     ecx, [esi+edx]
1613
        lea     eax, [ebp+malloc_state.release_list-tchunk_release_fd]
1614
        mov     ebx, [ebp+malloc_state.release_list]
1615
        cmp     [ebx+tchunk_release_bk], eax
1616
        jnz     heap_corrupted
1617
        mov     [ecx+tchunk_release_fd], ebx
1618
        mov     [ecx+tchunk_release_bk], eax
1619
        mov     [ebx+tchunk_release_bk], ecx
1620
        mov     [eax+tchunk_release_fd], ecx
1621
@@:
1622
assert PINUSE_BIT = 1
1623
        inc     edx
1624
        mov     [esi+mchunk_head], edx
1625
        unlock_heap
1626
        ret
1627
.append_dv:
1628
        mov     esi, eax
1629
assert PINUSE_BIT = 1
1630
        btr     dword [eax+edx+mchunk_head], 0
1631
        jnc     heap_corrupted
1632
        test    dword [eax+edx+mchunk_head], CINUSE_BIT
1633
        jz      .consolidate_forward
1634
        mov     [ebp+malloc_state.dvsize], edx
1635
        lea     ecx, [edx+PINUSE_BIT]
1636
        mov     [eax+mchunk_head], ecx
1637
        mov     [eax+edx+mchunk_prev_foot], edx
1638
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1639
        jb      @f
1640
        add     edx, eax
1641
        lea     eax, [ebp+malloc_state.release_list-tchunk_release_fd]
1642
        mov     ebx, [ebp+malloc_state.release_list]
1643
        cmp     [ebx+tchunk_release_bk], eax
1644
        jnz     heap_corrupted
1645
        mov     [edx+tchunk_release_fd], ebx
1646
        mov     [edx+tchunk_release_bk], eax
1647
        mov     [ebx+tchunk_release_bk], edx
1648
        mov     [eax+tchunk_release_fd], edx
1649
@@:
1650
        unlock_heap
1651
        ret
1652
.prepend_dv:
1653
        add     edx, [ebp+malloc_state.dvsize]
1654
        cmp     [ebp+malloc_state.dvsize], NSMALLBINS shl SMALLBIN_SHIFT
1655
        jb      @f
1656
        lea     ecx, [esi+edx]
1657
        mov     eax, [esi+edx+tchunk_release_fd]
1658
        mov     ebx, [esi+edx+tchunk_release_bk]
1659
        cmp     [eax+tchunk_release_bk], ecx
1660
        jnz     heap_corrupted
1661
        cmp     [ebx+tchunk_release_fd], ecx
1662
        jnz     heap_corrupted
1663
        mov     [eax+tchunk_release_bk], ebx
1664
        mov     [ebx+tchunk_release_fd], eax
1665
@@:
1666
        mov     [ebp+malloc_state.dvsize], edx
1667
        mov     [ebp+malloc_state.dv], esi
1668
        mov     [esi+edx+mchunk_prev_foot], edx
1669
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1670
        jb      @f
1671
        lea     ecx, [esi+edx]
1672
        lea     eax, [ebp+malloc_state.release_list-tchunk_release_fd]
1673
        mov     ebx, [ebp+malloc_state.release_list]
1674
        cmp     [ebx+tchunk_release_bk], eax
1675
        jnz     heap_corrupted
1676
        mov     [ecx+tchunk_release_fd], ebx
1677
        mov     [ecx+tchunk_release_bk], eax
1678
        mov     [ebx+tchunk_release_bk], ecx
1679
        mov     [eax+tchunk_release_fd], ecx
1680
@@:
1681
assert PINUSE_BIT = 1
1682
        inc     edx
1683
        mov     [esi+mchunk_head], edx
1684
        unlock_heap
1685
        ret
1686
.prepend_top:
1687
        add     edx, [ebp+malloc_state.topsize]
1688
        mov     [ebp+malloc_state.topsize], edx
1689
        mov     [ebp+malloc_state.top], esi
1690
assert PINUSE_BIT = 1
1691
        inc     edx
1692
        mov     [esi+mchunk_head], edx
1693
        cmp     esi, [ebp+malloc_state.dv]
1694
        jnz     @f
1695
        mov     [ebp+malloc_state.dv], 0
1696
        mov     [ebp+malloc_state.dvsize], 0
1697
@@:
1698
        trim_if_should
1699
        unlock_heap
1700
        ret
1701
.free_mmapped:
1702
        dec     ecx
1703
        jz      heap_corrupted
1704
        mov     ecx, eax
1705
        add     edx, [eax+mchunk_prev_foot]
1706
        add     edx, MMAP_FOOT_PAD-8
1707
        test    edx, 0xFFF
1708
        jnz     heap_corrupted
1709
        sub     [ebp+malloc_state.footprint], edx
1710
        sub     ecx, [ecx+mchunk_prev_foot]
1711
        test    ecx, 0xFFF
1712
        jnz     heap_corrupted
1713
        mov     eax, [ecx]
1714
        mov     ebx, [ecx+4]
1715
        cmp     [eax+4], ecx
1716
        jnz     heap_corrupted
1717
        cmp     [ebx], ecx
1718
        jnz     heap_corrupted
1719
        mov     [eax+4], ebx
1720
        mov     [ebx], eax
1721
        mov     eax, 68
1722
        mov     ebx, 13
1723
        call    FS_SYSCALL_PTR
1724
        unlock_heap
1725
        ret
1726
}
1727
 
1728
; in: ebp -> malloc_state
1729
; in: [n_elements] = stack var with number of elements
1730
; in: [elem_size] = stack var with element size
1731
macro do_calloc malloc_call
1732
{
1733
        mov     eax, [n_elements]
1734
        mul     [elem_size]
1735
        test    edx, edx
1736
        jnz     .fail
1737
        mov     [n_elements], eax
1738
        malloc_call
1739
        test    eax, eax
1740
        jz      .nothing
1741
        test    dword [eax+mchunk_head], INUSE_BITS
1742
        jz      .nothing
1743
        push    eax edi
1744
        mov     edi, eax
1745
        mov     ecx, [n_elements]
1746
        add     ecx, 3
1747
        shr     ecx, 2
1748
        xor     eax, eax
1749
        rep stosd
1750
        pop     edi eax
1751
.nothing:
1752
        ret
1753
.fail:
1754
        mov     FS_ERRNO, ENOMEM
1755
        xor     eax, eax
1756
        ret
1757
}
1758
 
1759
; ------------ Internal support for realloc, memalign, etc --------------
1760
; Try to realloc; only in-place unless can_move true
1761
; in: ebp -> malloc_state
1762
; in: [oldmem] = stack var with pointer to realloc
1763
; in: [bytes] = stack var with number of bytes to allocate
1764
macro try_realloc_chunk can_move
1765
{
1766
        lock_heap
1767
        mov     eax, [oldmem]
1768
        mov     ecx, MIN_CHUNK_SIZE
1769
        cmp     [bytes], MIN_REQUEST
1770
        jb      @f
1771
        mov     ecx, [bytes]
1772
        add     ecx, CHUNK_OVERHEAD + CHUNK_ALIGN_MASK
1773
        and     ecx, not CHUNK_ALIGN_MASK
1774
@@:
1775
        mov     [bytes], ecx
1776
        mov     edx, [eax+mchunk_head]
1777
        and     edx, not FLAG_BITS
1778
; Possible values of [eax+mchunk_head] and INUSE_BIT:
1779
; 0 = mmapped chunk, should be rare
1780
; 1 = illegal, trying to realloc already-freed chunk
1781
; 2,3 = normal chunk
1782
        test    dword [eax+mchunk_head], CINUSE_BIT
1783
        jz      .mmapped
1784
        test    dword [eax+edx+mchunk_head], PINUSE_BIT
1785
        jz      heap_corrupted
1786
        cmp     ecx, edx
1787
        jbe     .realloc_decrease
1788
        add     eax, edx
1789
        cmp     eax, [ebp+malloc_state.top]
1790
        jz      .move_top
1791
        cmp     eax, [ebp+malloc_state.dv]
1792
        jz      .extend_into_dv
1793
        test    dword [eax+mchunk_head], CINUSE_BIT
1794
        jnz     .failed
1795
; extend into next free chunk
1796
        push    edx
1797
        mov     edx, [eax+mchunk_head]
1798
        and     edx, not FLAG_BITS
1799
        add     [esp], edx
1800
        sub     [esp], ecx
1801
        jb      .failed_pop
1802
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1803
        jae     .large
1804
        unlink_small_chunk      ; ebp, eax, edx
1805
        jmp     .common
1806
.large:
1807
        cmp     dword [esp], NSMALLBINS shl SMALLBIN_SHIFT
1808
        jae     @f
1809
        lea     ecx, [eax+edx]
1810
        mov     ebx, [eax+edx+tchunk_release_fd]
1811
        mov     edx, [eax+edx+tchunk_release_bk]
1812
        cmp     [ebx+tchunk_release_bk], ecx
1813
        jnz     heap_corrupted
1814
        cmp     [edx+tchunk_release_fd], ecx
1815
        jnz     heap_corrupted
1816
        mov     [ebx+tchunk_release_bk], edx
1817
        mov     [edx+tchunk_release_fd], ebx
1818
@@:
1819
        unlink_large_chunk      ; ebp, eax
1820
.common:
1821
        pop     edx
1822
        mov     eax, [oldmem]
1823
        mov     ecx, [bytes]
1824
        cmp     edx, MIN_CHUNK_SIZE
1825
        jae     .add_chunk
1826
.no_new_chunk:
1827
        add     edx, ecx
1828
        mov     ebx, [eax+mchunk_head]
1829
        and     ebx, PINUSE_BIT
1830
        lea     ebx, [edx+ebx+CINUSE_BIT]
1831
        mov     [eax+mchunk_head], ebx
1832
        or      dword [eax+edx+mchunk_head], PINUSE_BIT
1833
        mark_inuse_foot eax+edx
1834
.nothing:
1835
        unlock_heap
1836
        ret
1837
.realloc_decrease:
1838
        test    dword [eax+edx+mchunk_head], CINUSE_BIT
1839
        jz      .prepend_free
1840
        sub     edx, ecx
1841
        cmp     edx, MIN_CHUNK_SIZE
1842
        jb      .nothing
1843
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1844
        jb      .add_chunk
1845
        lea     ebx, [eax+ecx]
1846
        add     ebx, edx
1847
        lea     eax, [ebp+malloc_state.release_list-tchunk_release_fd]
1848
        mov     esi, [ebp+malloc_state.release_list]
1849
        cmp     [esi+tchunk_release_bk], eax
1850
        jnz     heap_corrupted
1851
        mov     [ebx+tchunk_release_fd], esi
1852
        mov     [ebx+tchunk_release_bk], eax
1853
        mov     [esi+tchunk_release_bk], ebx
1854
        mov     [eax+tchunk_release_fd], ebx
1855
        mov     eax, [oldmem]
1856
.add_chunk:
1857
        mov     ebx, [eax+mchunk_head]
1858
        lea     esi, [eax+ecx]
1859
        and     ebx, PINUSE_BIT
1860
        lea     ebx, [ecx+ebx+CINUSE_BIT]
1861
        mov     [eax+mchunk_head], ebx
1862
        mark_inuse_foot esi
1863
        lea     ecx, [edx+PINUSE_BIT]
1864
        mov     [esi+mchunk_head], ecx
1865
        mov     [esi+edx+mchunk_prev_foot], edx
1866
        and     dword [esi+edx+mchunk_head], not PINUSE_BIT
1867
        push    edi
1868
macro unlock_ret \{
1869
        pop     edi
1870
        unlock_heap
1871
        ret
1872
\}
1873
        insert_chunk unlock_ret ; ebp, esi, edx
1874
purge unlock_ret
1875
.prepend_free:
1876
        add     eax, edx
1877
        cmp     eax, [ebp+malloc_state.top]
1878
        jz      .move_top
1879
        cmp     eax, [ebp+malloc_state.dv]
1880
        jz      .prepend_dv
1881
        sub     edx, ecx
1882
        push    edx
1883
        mov     edx, [eax+mchunk_head]
1884
        and     edx, not FLAG_BITS
1885
        add     [esp], edx
1886
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1887
        jae     .prepend_old_large
1888
        unlink_small_chunk      ; ebp, eax, edx
1889
        jmp     .prepend_old_common
1890
.prepend_old_large:
1891
        add     edx, eax
1892
        mov     ebx, [edx+tchunk_release_fd]
1893
        mov     ecx, [edx+tchunk_release_bk]
1894
        cmp     [ebx+tchunk_release_bk], edx
1895
        jnz     heap_corrupted
1896
        cmp     [ecx+tchunk_release_fd], edx
1897
        jnz     heap_corrupted
1898
        mov     [ebx+tchunk_release_bk], ecx
1899
        mov     [ecx+tchunk_release_fd], ebx
1900
        unlink_large_chunk      ; ebp, eax
1901
.prepend_old_common:
1902
        pop     edx
1903
        mov     esi, [oldmem]
1904
        add     esi, [bytes]
1905
        mov     [esi+edx+mchunk_prev_foot], edx
1906
        lea     ecx, [edx+PINUSE_BIT]
1907
        mov     [esi+mchunk_head], ecx
1908
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1909
        jb      .prepend_new_small
1910
        lea     ecx, [esi+edx]
1911
        lea     eax, [ebp+malloc_state.release_list-tchunk_release_fd]
1912
        mov     ebx, [ebp+malloc_state.release_list]
1913
        cmp     [ebx+tchunk_release_bk], eax
1914
        jnz     heap_corrupted
1915
        mov     [eax+tchunk_release_fd], ecx
1916
        mov     [ebx+tchunk_release_bk], ecx
1917
        mov     [ecx+tchunk_release_fd], ebx
1918
        mov     [ecx+tchunk_release_bk], eax
1919
        push    edi
1920
macro after_insert \{
1921
        pop     edi
1922
        jmp     .prepend_new_common
1923
\}
1924
        insert_large_chunk after_insert ; ebp, esi, edx
1925
purge after_insert
1926
.prepend_new_small:
1927
        insert_small_chunk      ; ebp, esi, edx
1928
.prepend_new_common:
1929
        mov     eax, [oldmem]
1930
        mov     ecx, [bytes]
1931
        mark_inuse_foot eax+ecx
1932
        mov     ebx, [eax+mchunk_head]
1933
        and     ebx, PINUSE_BIT
1934
        lea     ebx, [ebx+ecx+CINUSE_BIT]
1935
        mov     [eax+mchunk_head], ebx
1936
        unlock_heap
1937
        ret
1938
.prepend_dv:
1939
        add     edx, [ebp+malloc_state.dvsize]
1940
        add     eax, [ebp+malloc_state.dvsize]
1941
        sub     edx, ecx
1942
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1943
        jb      .move_dv
1944
        cmp     [ebp+malloc_state.dvsize], NSMALLBINS shl SMALLBIN_SHIFT
1945
        jb      @f
1946
        cmp     [eax+tchunk_release_fd], eax
1947
        jnz     .move_dv
1948
@@:
1949
        lea     ebx, [ebp+malloc_state.release_list-tchunk_release_fd]
1950
        mov     esi, [ebp+malloc_state.release_list]
1951
        cmp     [esi+tchunk_release_bk], ebx
1952
        jnz     heap_corrupted
1953
        mov     [eax+tchunk_release_fd], esi
1954
        mov     [eax+tchunk_release_bk], ebx
1955
        mov     [esi+tchunk_release_bk], eax
1956
        mov     [ebx+tchunk_release_fd], eax
1957
        jmp     .move_dv
1958
.extend_into_dv:
1959
        add     edx, [ebp+malloc_state.dvsize]
1960
        sub     edx, ecx
1961
        jb      .failed
1962
        cmp     [ebp+malloc_state.dvsize], NSMALLBINS shl SMALLBIN_SHIFT
1963
        jb      .move_dv
1964
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
1965
        jae     .move_dv
1966
        add     eax, [ebp+malloc_state.dvsize]
1967
        mov     ebx, [eax+tchunk_release_fd]
1968
        mov     esi, [eax+tchunk_release_bk]
1969
        cmp     [ebx+tchunk_release_bk], eax
1970
        jnz     heap_corrupted
1971
        cmp     [esi+tchunk_release_fd], eax
1972
        jnz     heap_corrupted
1973
        mov     [ebx+tchunk_release_bk], esi
1974
        mov     [esi+tchunk_release_fd], ebx
1975
.move_dv:
1976
        mov     eax, [oldmem]
1977
        cmp     edx, MIN_CHUNK_SIZE
1978
        jb      .dv_no_new_chunk
1979
        mov     ebx, [eax+mchunk_head]
1980
        and     ebx, PINUSE_BIT
1981
        lea     ebx, [ecx+ebx+CINUSE_BIT]
1982
        mov     [eax+mchunk_head], ebx
1983
        add     ecx, eax
1984
        mark_inuse_foot ecx
1985
        lea     ebx, [edx+PINUSE_BIT]
1986
        mov     [ecx+mchunk_head], ebx
1987
        mov     [ecx+edx+mchunk_prev_foot], edx
1988
        and     dword [ecx+edx+mchunk_head], not PINUSE_BIT
1989
        mov     [ebp+malloc_state.dvsize], edx
1990
        mov     [ebp+malloc_state.dv], ecx
1991
        unlock_heap
1992
        ret
1993
.dv_no_new_chunk:
1994
        add     edx, ecx
1995
        mov     ebx, [eax+mchunk_head]
1996
        and     ebx, PINUSE_BIT
1997
        lea     ebx, [edx+ebx+CINUSE_BIT]
1998
        mov     [eax+mchunk_head], ebx
1999
        or      dword [eax+edx+mchunk_head], PINUSE_BIT
2000
        mark_inuse_foot eax+edx
2001
        mov     [ebp+malloc_state.dvsize], 0
2002
        mov     [ebp+malloc_state.dv], 0
2003
        unlock_heap
2004
        ret
2005
.move_top:
2006
        add     edx, [ebp+malloc_state.topsize]
2007
        sub     edx, ecx
2008
        jbe     .failed
2009
        mov     eax, [oldmem]
2010
        mov     ebx, [eax+mchunk_head]
2011
        and     ebx, PINUSE_BIT
2012
        lea     ebx, [ecx+ebx+CINUSE_BIT]
2013
        mov     [eax+mchunk_head], ebx
2014
        add     ecx, eax
2015
        mark_inuse_foot ecx
2016
        mov     [ebp+malloc_state.top], ecx
2017
        mov     [ebp+malloc_state.topsize], edx
2018
assert PINUSE_BIT = 1
2019
        inc     edx
2020
        mov     [ecx+mchunk_head], edx
2021
        unlock_heap
2022
        ret
2023
.mmapped:
2024
        test    dword [eax+mchunk_head], PINUSE_BIT
2025
        jnz     heap_corrupted
2026
        mov     edx, eax
2027
if can_move
2028
        mov     ebx, 1
2029
else
2030
        xor     ebx, ebx
2031
end if
2032
        call    mmap_resize
2033
        test    eax, eax
2034
        jz      .failed
2035
        unlock_heap
2036
        ret
2037
.failed_pop:
2038
        pop     edx
2039
.failed:
2040
        unlock_heap
2041
}
2042
 
2043
; ------------------ Exported realloc, memalign, etc --------------------
2044
 
2045
; in: ebp -> malloc_state
2046
; in: [oldmem] = stack var with pointer to realloc
2047
; in: [bytes] = stack var with number of bytes to allocate
2048
macro do_realloc malloc_call, free_call
2049
{
2050
        cmp     [oldmem], 0
2051
        jz      .malloc
2052
        cmp     [bytes], 0
2053
        jz      .free
2054
        cmp     [bytes], MAX_REQUEST
2055
        jae     .fail
2056
if FOOTERS
2057
        mov     ecx, [oldmem]
2058
        mov     eax, [ecx+mchunk_head]
2059
        and     eax, not FLAG_BITS
2060
        mov     eax, [ecx+eax+mchunk_prev_foot]
2061
        xor     eax, [malloc_magic]
2062
        cmp     eax, ebp
2063
        jnz     heap_corrupted
2064
end if
2065
        try_realloc_chunk 1     ; ebp, [oldmem], [bytes]
2066
        sub     [bytes], CHUNK_OVERHEAD ; try_realloc_chunk has padded [bytes]
2067
        malloc_call [bytes]
2068
        test    eax, eax
2069
        jz      .justret
2070
        mov     esi, [oldmem]
2071
        push    eax
2072
        push    edi
2073
        mov     ecx, [esi+mchunk_head]
2074
        mov     edi, eax
2075
        mov     edx, MMAP_CHUNK_OVERHEAD
2076
        test    ecx, INUSE_BITS
2077
        jz      @f
2078
        mov     edx, CHUNK_OVERHEAD
2079
@@:
2080
        and     ecx, not FLAG_BITS
2081
        sub     ecx, edx
2082
        cmp     ecx, [bytes+8]
2083
        jb      @f
2084
        mov     ecx, [bytes+8]
2085
@@:
2086
        shr     ecx, 2
2087
        rep movsd
2088
        pop     edi
2089
        free_call [oldmem+4]
2090
        pop     eax
2091
.justret:
2092
        ret
2093
.malloc:
2094
        malloc_call [bytes]
2095
        ret
2096
.free:
2097
        free_call [oldmem]
2098
        xor     eax, eax
2099
        ret
2100
.fail:
2101
        mov     FS_ERRNO, ENOMEM
2102
        xor     eax, eax
2103
        ret
2104
}
2105
 
2106
; in: ebp -> malloc_state
2107
; in: [oldmem] = stack var with pointer to realloc
2108
; in: [bytes] = stack var with number of bytes to allocate
2109
macro do_realloc_in_place
2110
{
2111
        cmp     [oldmem], 0
2112
        jz      .fail
2113
        cmp     [bytes], MAX_REQUEST
2114
        jae     .fail
2115
if FOOTERS
2116
        mov     ecx, [oldmem]
2117
        mov     eax, [ecx+mchunk_head]
2118
        and     eax, not FLAG_BITS
2119
        mov     eax, [ecx+eax+mchunk_prev_foot]
2120
        xor     eax, [malloc_magic]
2121
        cmp     eax, ebp
2122
        jnz     heap_corrupted
2123
end if
2124
        try_realloc_chunk 0     ; ebp, [oldmem], [bytes]
2125
.fail:
2126
        xor     eax, eax
2127
        ret
2128
}
2129
 
2130
; in: ebp -> malloc_state
2131
; in: [alignment] = stack var with required alignment
2132
; in: [bytes] = stack var with number of bytes to allocate
2133
macro do_memalign malloc_call
2134
{
2135
        mov     eax, [alignment]
2136
        cmp     [alignment], MALLOC_ALIGNMENT
2137
        jbe     .just_malloc
2138
        lea     edx, [eax-1]
2139
        cmp     eax, MIN_CHUNK_SIZE
2140
        jb      .set_to_min_chunk_size
2141
        test    eax, edx
2142
        jnz     .set_align_pow2
2143
.alignment_ok:
2144
        add     eax, [bytes]
2145
        jc      .too_large
2146
        cmp     eax, MAX_REQUEST
2147
        jae     .too_large
2148
        mov     ecx, MIN_CHUNK_SIZE
2149
        cmp     [bytes], MIN_REQUEST
2150
        jb      @f
2151
        mov     ecx, [bytes]
2152
        add     ecx, CHUNK_OVERHEAD + CHUNK_ALIGN_MASK
2153
        and     ecx, not CHUNK_ALIGN_MASK
2154
@@:
2155
        mov     [bytes], ecx
2156
        add     ecx, [alignment]
2157
        add     ecx, MIN_CHUNK_SIZE - CHUNK_OVERHEAD
2158
        malloc_call ecx
2159
        test    eax, eax
2160
        jz      .nothing
2161
        mov     esi, eax
2162
        lock_heap
2163
        mov     eax, esi
2164
        mov     edx, [alignment]
2165
        dec     edx
2166
        test    esi, edx
2167
        jz      .aligned
2168
; Find an aligned spot inside chunk.  Since we need to give
2169
; back leading space in a chunk of at least MIN_CHUNK_SIZE, if
2170
; the first calculation places us at a spot with less than
2171
; MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
2172
; We've allocated enough total room so that this is always
2173
; possible.
2174
        not     edx
2175
        lea     eax, [esi-1]
2176
        add     eax, [alignment]
2177
        and     eax, edx
2178
        mov     edx, eax
2179
        sub     edx, esi
2180
        cmp     edx, MIN_CHUNK_SIZE
2181
        jae     @f
2182
        add     eax, [alignment]
2183
        add     edx, [alignment]
2184
@@:
2185
        test    dword [esi+mchunk_head], INUSE_BITS
2186
        jz      .unaligned_mmapped
2187
; give back leader, use the rest
2188
        mov     ecx, [esi+mchunk_head]
2189
        and     ecx, not FLAG_BITS
2190
        sub     ecx, edx
2191
        mark_inuse_foot eax+ecx
2192
        or      ecx, CINUSE_BIT
2193
        mov     [eax+mchunk_head], ecx
2194
        test    dword [esi+mchunk_head], PINUSE_BIT
2195
        jnz     .insert_before
2196
        mov     ebx, esi
2197
        mov     edx, [esi+mchunk_prev_foot]
2198
        sub     esi, [esi+mchunk_prev_foot]
2199
        cmp     esi, [ebp+malloc_state.dv]
2200
        jz      .append_dv
2201
        push    eax
2202
        mov     eax, esi
2203
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
2204
        jae     .append_large
2205
        unlink_small_chunk      ; ebp, eax, edx
2206
        jmp     .append_common
2207
.append_large:
2208
        mov     ecx, [ebx+tchunk_release_fd]
2209
        mov     esi, [ebx+tchunk_release_bk]
2210
        cmp     [ecx+tchunk_release_bk], ebx
2211
        jnz     heap_corrupted
2212
        cmp     [esi+tchunk_release_fd], ebx
2213
        jnz     heap_corrupted
2214
        mov     [ecx+tchunk_release_bk], esi
2215
        mov     [esi+tchunk_release_fd], ecx
2216
        unlink_large_chunk      ; ebp, eax
2217
.append_common:
2218
        mov     esi, eax
2219
        mov     edx, [esp]
2220
        pop     eax
2221
        sub     edx, esi
2222
.insert_before:
2223
        lea     ecx, [edx+PINUSE_BIT]
2224
        mov     [esi+mchunk_head], ecx
2225
        mov     [eax+mchunk_prev_foot], edx
2226
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
2227
        jb      .small_before
2228
        lea     ebx, [ebp+malloc_state.release_list-tchunk_release_fd]
2229
        mov     ecx, [ebp+malloc_state.release_list]
2230
        cmp     [ecx+tchunk_release_bk], ebx
2231
        jnz     heap_corrupted
2232
        mov     [ebx+tchunk_release_fd], eax
2233
        mov     [ecx+tchunk_release_bk], eax
2234
        mov     [eax+tchunk_release_fd], ecx
2235
        mov     [eax+tchunk_release_bk], ebx
2236
        push    edi
2237
macro after_insert label \{
2238
        pop     edi
2239
        jmp     label
2240
\}
2241
        insert_large_chunk         ; ebp, esi, edx
2242
.small_before:
2243
        insert_small_chunk      ; ebp, esi, edx
2244
.common_before:
2245
.aligned:
2246
; give back spare room at the end
2247
        test    dword [eax+mchunk_head], INUSE_BITS
2248
        jz      .done_after
2249
        mov     ecx, [bytes]
2250
        mov     edx, [eax+mchunk_head]
2251
        mov     ebx, edx
2252
        and     edx, not FLAG_BITS
2253
        lea     esi, [eax+ecx]
2254
        sub     edx, ecx
2255
        test    dword [esi+edx+mchunk_head], CINUSE_BIT
2256
        jz      @f
2257
        cmp     edx, MIN_CHUNK_SIZE
2258
        jb      .done_after
2259
@@:
2260
        and     ebx, INUSE_BITS
2261
        add     ebx, ecx
2262
        mov     [eax+mchunk_head], ebx
2263
        mark_inuse_foot esi
2264
        test    dword [esi+edx+mchunk_head], CINUSE_BIT
2265
        jnz     .insert_after
2266
        add     esi, edx
2267
        cmp     esi, [ebp+malloc_state.dv]
2268
        jz      .prepend_dv
2269
        cmp     esi, [ebp+malloc_state.top]
2270
        jz      .prepend_top
2271
        mov     edx, [esi+mchunk_head]
2272
        and     edx, not FLAG_BITS
2273
        push    eax
2274
        mov     eax, esi
2275
        add     esi, edx
2276
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
2277
        jae     .prepend_large
2278
        unlink_small_chunk      ; ebp, eax, edx
2279
        jmp     .prepend_common
2280
.prepend_large:
2281
        mov     ecx, [esi+tchunk_release_fd]
2282
        mov     ebx, [esi+tchunk_release_bk]
2283
        cmp     [ecx+tchunk_release_bk], esi
2284
        jnz     heap_corrupted
2285
        cmp     [ebx+tchunk_release_fd], esi
2286
        jnz     heap_corrupted
2287
        mov     [ecx+tchunk_release_bk], ebx
2288
        mov     [ebx+tchunk_release_fd], ecx
2289
        mov     ecx, esi
2290
        unlink_large_chunk      ; ebp, eax
2291
        mov     esi, ecx
2292
.prepend_common:
2293
        pop     eax
2294
        mov     edx, esi
2295
        mov     esi, [bytes]
2296
        add     esi, eax
2297
        sub     edx, esi
2298
.insert_after:
2299
        lea     ebx, [edx+PINUSE_BIT]
2300
        mov     [esi+mchunk_head], ebx
2301
        mov     [esi+edx+mchunk_prev_foot], edx
2302
        and     dword [esi+edx+mchunk_head], not PINUSE_BIT
2303
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
2304
        jb      .small_after
2305
        push    edi
2306
        lea     ebx, [ebp+malloc_state.release_list-tchunk_release_fd]
2307
        mov     ecx, [ebp+malloc_state.release_list]
2308
        lea     edi, [esi+edx]
2309
        cmp     [ecx+tchunk_release_bk], ebx
2310
        jnz     heap_corrupted
2311
        mov     [ebx+tchunk_release_fd], edi
2312
        mov     [ecx+tchunk_release_bk], edi
2313
        mov     [edi+tchunk_release_fd], ecx
2314
        mov     [edi+tchunk_release_bk], ebx
2315
        insert_large_chunk    ; ebp, esi, edx
2316
purge after_insert
2317
.small_after:
2318
        insert_small_chunk      ; ebp, esi, edx
2319
.done_after:
2320
        unlock_heap
2321
        ret
2322
.prepend_dv:
2323
        sub     esi, edx
2324
        add     edx, [ebp+malloc_state.dvsize]
2325
        mov     [ebp+malloc_state.dv], esi
2326
        lea     ecx, [edx+PINUSE_BIT]
2327
        mov     [esi+mchunk_head], ecx
2328
        mov     [esi+edx+mchunk_prev_foot], edx
2329
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
2330
        jb      .prepend_dv.norelease
2331
        add     esi, edx
2332
        cmp     [ebp+malloc_state.dvsize], NSMALLBINS shl SMALLBIN_SHIFT
2333
        jb      @f
2334
        cmp     [esi+tchunk_release_fd], esi
2335
        jnz     .prepend_dv.norelease
2336
@@:
2337
        lea     ebx, [ebp+malloc_state.release_list-tchunk_release_fd]
2338
        mov     ecx, [ebp+malloc_state.release_list]
2339
        cmp     [ecx+tchunk_release_bk], ebx
2340
        jnz     heap_corrupted
2341
        mov     [ebx+tchunk_release_fd], esi
2342
        mov     [ecx+tchunk_release_bk], esi
2343
        mov     [esi+tchunk_release_fd], ecx
2344
        mov     [esi+tchunk_release_bk], ebx
2345
.prepend_dv.norelease:
2346
        mov     [ebp+malloc_state.dvsize], edx
2347
        unlock_heap
2348
        ret
2349
.prepend_top:
2350
        sub     esi, edx
2351
        mov     [ebp+malloc_state.top], esi
2352
        add     edx, [ebp+malloc_state.topsize]
2353
        mov     [ebp+malloc_state.topsize], edx
2354
assert PINUSE_BIT = 1
2355
        inc     edx
2356
        mov     [esi+mchunk_head], edx
2357
        unlock_heap
2358
        ret
2359
.append_dv:
2360
        mov     edx, eax
2361
        sub     edx, esi
2362
        cmp     edx, NSMALLBINS shl SMALLBIN_SHIFT
2363
        jb      .append_dv.norelease
2364
        cmp     [ebp+malloc_state.dvsize], NSMALLBINS shl SMALLBIN_SHIFT
2365
        jb      @f
2366
        cmp     [eax+tchunk_release_fd], eax
2367
        jnz     .append_dv.norelease
2368
@@:
2369
        lea     ebx, [ebp+malloc_state.release_list-tchunk_release_fd]
2370
        mov     ecx, [ebp+malloc_state.release_list]
2371
        cmp     [ecx+tchunk_release_bk], ebx
2372
        jnz     heap_corrupted
2373
        mov     [ebx+tchunk_release_fd], eax
2374
        mov     [ecx+tchunk_release_bk], eax
2375
        mov     [eax+tchunk_release_fd], ecx
2376
        mov     [eax+tchunk_release_bk], ebx
2377
.append_dv.norelease:
2378
        lea     ecx, [edx+PINUSE_BIT]
2379
        mov     [ebp+malloc_state.dvsize], edx
2380
        mov     [eax+mchunk_prev_foot], edx
2381
        mov     [esi+mchunk_head], ecx
2382
        jmp     .common_before
2383
.unaligned_mmapped:
2384
; For mmapped chunks, just adjust offset
2385
        mov     ecx, [esi+mchunk_head]
2386
        sub     ecx, edx
2387
        add     edx, [esi+mchunk_prev_foot]
2388
        mov     [eax+mchunk_prev_foot], edx
2389
        mov     [eax+mchunk_head], ecx
2390
        unlock_heap
2391
        ret
2392
.too_large:
2393
        mov     FS_ERRNO, ENOMEM
2394
        xor     eax, eax
2395
.nothing:
2396
        ret
2397
.set_to_min_chunk_size:
2398
        mov     eax, MIN_CHUNK_SIZE
2399
        mov     [alignment], eax
2400
        jmp     .alignment_ok
2401
.set_align_pow2:
2402
        bsr     ecx, eax
2403
        mov     eax, 2
2404
        shl     eax, cl
2405
        mov     [alignment], eax
2406
        jmp     .alignment_ok
2407
.just_malloc:
2408
        malloc_call [bytes]
2409
        ret
2410
}
2411
 
2412
macro do_malloc_trim
2413
{
2414
        ret
2415
}
2416
 
2417
macro do_malloc_footprint
2418
{
2419
        mov     eax, [ebp+malloc_state.footprint]
2420
        ret
2421
}
2422
 
2423
macro do_malloc_max_footprint
2424
{
2425
        mov     eax, [ebp+malloc_state.max_footprint]
2426
        ret
2427
}
2428
 
2429
macro do_malloc_footprint_limit
2430
{
2431
        mov     eax, [ebp+malloc_state.footprint_limit]
2432
        test    eax, eax
2433
        jnz     @f
2434
        dec     eax
2435
@@:
2436
        ret
2437
}
2438
 
2439
macro do_malloc_set_footprint_limit
2440
{
2441
        mov     eax, [bytes]
2442
        test    eax, eax
2443
        jnz     @f
2444
        inc     eax
2445
@@:
2446
        cmp     eax, -1
2447
        jnz     @f
2448
        inc     eax
2449
@@:
2450
        add     eax, 0xFFF
2451
        and     eax, not 0xFFF
2452
        mov     [ebp+malloc_state.footprint_limit], eax
2453
        ret
2454
}
2455
 
2456
macro do_mspace_mallopt
2457
{
2458
}
2459
 
2460
; ----------------------------- user mspaces ----------------------------
2461
 
2462
malloc_state_chunk_size = (sizeof.malloc_state + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) and not CHUNK_ALIGN_MASK
2463
macro init_user_mstate
2464
{
2465
        mov     ebx, eax
2466
        add     eax, 8 + ((-8) and CHUNK_ALIGN_MASK)
2467
        mov     dword [eax+mchunk_head], malloc_state_chunk_size + INUSE_BITS
2468
        mov     [eax+malloc_state.mmap_threshold], DEFAULT_MMAP_THRESHOLD
2469
        mov     [eax+malloc_state.seg.base], ebx
2470
        mov     [eax+malloc_state.seg.size], ecx
2471
        mov     [eax+malloc_state.segment_size], ecx
2472
        mov     [eax+malloc_state.footprint], ecx
2473
        mov     [eax+malloc_state.max_footprint], ecx
2474
        lea     ecx, [ecx+ebx+8-TOP_FOOT_SIZE]
2475
if FOOTERS
2476
        mov     ebx, [malloc_magic]
2477
        mov     [eax+malloc_state.magic], ebx
2478
end if
2479
        mov     [eax+malloc_state.release_checks], RELEASE_CHECK_RATE
2480
        init_bins       ; eax
2481
        lea     ebx, [eax+malloc_state.release_list-tchunk_release_fd]
2482
        mov     [eax+malloc_state.release_list+0], ebx
2483
        mov     [eax+malloc_state.release_list+4], ebx
2484
        lea     ebx, [eax+malloc_state.mmap_list]
2485
        mov     [eax+malloc_state.mmap_list], ebx
2486
        mov     [eax+malloc_state.mmap_list+4], ebx
2487
        lea     ebx, [eax+malloc_state_chunk_size]
2488
        sub     ecx, ebx
2489
        init_top        ; eax, ebx, ecx
2490
}
2491
 
2492
; in: [capacity] = stack var with number of bytes to allocate
2493
; in: [locked] = stack var which is zero if locking is not needed
2494
; out: eax -> allocated data
2495
macro do_create_mspace
2496
{
2497
        mov     eax, 68
2498
        mov     ebx, 12
2499
        mov     ecx, [capacity]
2500
        test    ecx, ecx
2501
        jnz     @f
2502
        mov     ecx, DEFAULT_MSPACE_SIZE
2503
@@:
2504
        add     ecx, 0xFFF
2505
        and     ecx, not 0xFFF
2506
        jz      .fail
2507
        call    FS_SYSCALL_PTR
2508
        test    eax, eax
2509
        jz      .fail
2510
        init_user_mstate
2511
        and     [eax+malloc_state.mflags], not USE_LOCK_BIT
2512
        cmp     [locked], 0
2513
        jz      @f
2514
        or      [eax+malloc_state.mflags], USE_LOCK_BIT
2515
@@:
2516
        ret
2517
.fail:
2518
        xor     eax, eax
2519
        ret
2520
}
2521
 
2522
; in: [msp] = stack var with mspace to free
2523
macro do_destroy_mspace
2524
{
2525
        mov     edx, [msp]
2526
if FOOTERS
2527
        mov     ebx, [malloc_magic]
2528
        cmp     [edx+malloc_state.magic], ebx
2529
        jnz     heap_corrupted
2530
end if
2531
        add     edx, malloc_state.mmap_list
2532
        push    edx
2533
        mov     ecx, [edx]
2534
        cmp     ecx, [esp]
2535
        jz      .large_done
2536
@@:
2537
        mov     eax, 68
2538
        mov     ebx, 13
2539
        mov     edx, [ecx]
2540
        call    FS_SYSCALL_PTR
2541
        mov     ecx, edx
2542
        cmp     edx, [esp]
2543
        jnz     @b
2544
.large_done:
2545
        pop     edx
2546
        add     edx, malloc_state.seg - malloc_state.mmap_list
2547
.free_segments:
2548
        mov     eax, 68
2549
        mov     ebx, 13
2550
        mov     ecx, [edx+malloc_segment.base]
2551
        mov     edx, [edx+malloc_segment.next]
2552
        call    FS_SYSCALL_PTR
2553
        test    edx, edx
2554
        jnz     .free_segments
2555
        ret
2556
}