1/* Minimal malloc implementation for dynamic linker and static
2 initialization.
3 Copyright (C) 1995-2024 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20/* Mark symbols hidden in static PIE for early self relocation to work.
21 Note: string.h may have ifuncs which cannot be hidden on i686. */
22#if BUILD_PIE_DEFAULT
23# pragma GCC visibility push(hidden)
24#endif
25#include <assert.h>
26#include <string.h>
27#include <ldsodefs.h>
28#include <malloc/malloc-internal.h>
29#include <setvmaname.h>
30
31static void *alloc_ptr, *alloc_end, *alloc_last_block;
32
33/* Allocate an aligned memory block. */
34void *
35__minimal_malloc (size_t n)
36{
37 if (alloc_end == 0)
38 {
39 /* Consume any unused space in the last page of our data segment. */
40 extern int _end attribute_hidden;
41 alloc_ptr = &_end;
42 alloc_end = (void *) 0 + ((((uintptr_t) alloc_ptr)
43 + GLRO(dl_pagesize) - 1)
44 & ~(GLRO(dl_pagesize) - 1));
45 }
46
47 /* Make sure the allocation pointer is ideally aligned. */
48 alloc_ptr = (void *) 0 + ((((uintptr_t) alloc_ptr) + MALLOC_ALIGNMENT - 1)
49 & ~(MALLOC_ALIGNMENT - 1));
50
51 if (alloc_ptr + n >= alloc_end || n >= -(uintptr_t) alloc_ptr)
52 {
53 /* Insufficient space left; allocate another page plus one extra
54 page to reduce number of mmap calls. */
55 caddr_t page;
56 size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
57 if (__glibc_unlikely (nup == 0 && n != 0))
58 return NULL;
59 nup += GLRO(dl_pagesize);
60 page = __mmap (0, nup, PROT_READ|PROT_WRITE,
61 MAP_ANON|MAP_PRIVATE, -1, 0);
62 if (page == MAP_FAILED)
63 return NULL;
64 __set_vma_name (start: page, len: nup, name: " glibc: loader malloc");
65 if (page != alloc_end)
66 alloc_ptr = page;
67 alloc_end = page + nup;
68 }
69
70 alloc_last_block = (void *) alloc_ptr;
71 alloc_ptr += n;
72 return alloc_last_block;
73}
74
75/* We use this function occasionally since the real implementation may
76 be optimized when it can assume the memory it returns already is
77 set to NUL. */
78void *
79__minimal_calloc (size_t nmemb, size_t size)
80{
81 /* New memory from the trivial malloc above is always already cleared.
82 (We make sure that's true in the rare occasion it might not be,
83 by clearing memory in free, below.) */
84 size_t bytes = nmemb * size;
85
86#define HALF_SIZE_T (((size_t) 1) << (8 * sizeof (size_t) / 2))
87 if (__builtin_expect ((nmemb | size) >= HALF_SIZE_T, 0)
88 && size != 0 && bytes / size != nmemb)
89 return NULL;
90
91 return malloc (size: bytes);
92}
93
94/* This will rarely be called. */
95void
96__minimal_free (void *ptr)
97{
98 /* We can free only the last block allocated. */
99 if (ptr == alloc_last_block)
100 {
101 /* Since this is rare, we clear the freed block here
102 so that calloc can presume malloc returns cleared memory. */
103 memset (alloc_last_block, '\0', alloc_ptr - alloc_last_block);
104 alloc_ptr = alloc_last_block;
105 }
106}
107
108/* This is only called with the most recent block returned by malloc. */
109void *
110__minimal_realloc (void *ptr, size_t n)
111{
112 if (ptr == NULL)
113 return malloc (size: n);
114 assert (ptr == alloc_last_block);
115 size_t old_size = alloc_ptr - alloc_last_block;
116 alloc_ptr = alloc_last_block;
117 void *new = malloc (size: n);
118 return new != ptr ? memcpy (new, ptr, old_size) : new;
119}
120

source code of glibc/elf/dl-minimal-malloc.c