xref: /netbsd-src/external/gpl3/gcc/dist/libbacktrace/mmap.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* mmap.c -- Memory allocation with mmap.
2    Copyright (C) 2012-2022 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Google.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9     (1) Redistributions of source code must retain the above copyright
10     notice, this list of conditions and the following disclaimer.
11 
12     (2) Redistributions in binary form must reproduce the above copyright
13     notice, this list of conditions and the following disclaimer in
14     the documentation and/or other materials provided with the
15     distribution.
16 
17     (3) The name of the author may not be used to
18     endorse or promote products derived from this software without
19     specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY OF SUCH DAMAGE.  */
32 
33 #include "config.h"
34 
35 #include <errno.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/mman.h>
41 
42 #include "backtrace.h"
43 #include "internal.h"
44 
45 #ifndef HAVE_DECL_GETPAGESIZE
46 extern int getpagesize (void);
47 #endif
48 
49 /* Memory allocation on systems that provide anonymous mmap.  This
50    permits the backtrace functions to be invoked from a signal
51    handler, assuming that mmap is async-signal safe.  */
52 
53 #ifndef MAP_ANONYMOUS
54 #define MAP_ANONYMOUS MAP_ANON
55 #endif
56 
57 #ifndef MAP_FAILED
58 #define MAP_FAILED ((void *)-1)
59 #endif
60 
61 /* A list of free memory blocks.  */
62 
63 struct backtrace_freelist_struct
64 {
65   /* Next on list.  */
66   struct backtrace_freelist_struct *next;
67   /* Size of this block, including this structure.  */
68   size_t size;
69 };
70 
71 /* Free memory allocated by backtrace_alloc.  */
72 
73 static void
backtrace_free_locked(struct backtrace_state * state,void * addr,size_t size)74 backtrace_free_locked (struct backtrace_state *state, void *addr, size_t size)
75 {
76   /* Just leak small blocks.  We don't have to be perfect.  Don't put
77      more than 16 entries on the free list, to avoid wasting time
78      searching when allocating a block.  If we have more than 16
79      entries, leak the smallest entry.  */
80 
81   if (size >= sizeof (struct backtrace_freelist_struct))
82     {
83       size_t c;
84       struct backtrace_freelist_struct **ppsmall;
85       struct backtrace_freelist_struct **pp;
86       struct backtrace_freelist_struct *p;
87 
88       c = 0;
89       ppsmall = NULL;
90       for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next)
91 	{
92 	  if (ppsmall == NULL || (*pp)->size < (*ppsmall)->size)
93 	    ppsmall = pp;
94 	  ++c;
95 	}
96       if (c >= 16)
97 	{
98 	  if (size <= (*ppsmall)->size)
99 	    return;
100 	  *ppsmall = (*ppsmall)->next;
101 	}
102 
103       p = (struct backtrace_freelist_struct *) addr;
104       p->next = state->freelist;
105       p->size = size;
106       state->freelist = p;
107     }
108 }
109 
110 /* Allocate memory like malloc.  If ERROR_CALLBACK is NULL, don't
111    report an error.  */
112 
113 void *
backtrace_alloc(struct backtrace_state * state,size_t size,backtrace_error_callback error_callback,void * data)114 backtrace_alloc (struct backtrace_state *state,
115 		 size_t size, backtrace_error_callback error_callback,
116 		 void *data)
117 {
118   void *ret;
119   int locked;
120   struct backtrace_freelist_struct **pp;
121   size_t pagesize;
122   size_t asksize;
123   void *page;
124 
125   ret = NULL;
126 
127   /* If we can acquire the lock, then see if there is space on the
128      free list.  If we can't acquire the lock, drop straight into
129      using mmap.  __sync_lock_test_and_set returns the old state of
130      the lock, so we have acquired it if it returns 0.  */
131 
132   if (!state->threaded)
133     locked = 1;
134   else
135     locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0;
136 
137   if (locked)
138     {
139       for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next)
140 	{
141 	  if ((*pp)->size >= size)
142 	    {
143 	      struct backtrace_freelist_struct *p;
144 
145 	      p = *pp;
146 	      *pp = p->next;
147 
148 	      /* Round for alignment; we assume that no type we care about
149 		 is more than 8 bytes.  */
150 	      size = (size + 7) & ~ (size_t) 7;
151 	      if (size < p->size)
152 		backtrace_free_locked (state, (char *) p + size,
153 				       p->size - size);
154 
155 	      ret = (void *) p;
156 
157 	      break;
158 	    }
159 	}
160 
161       if (state->threaded)
162 	__sync_lock_release (&state->lock_alloc);
163     }
164 
165   if (ret == NULL)
166     {
167       /* Allocate a new page.  */
168 
169       pagesize = getpagesize ();
170       asksize = (size + pagesize - 1) & ~ (pagesize - 1);
171       page = mmap (NULL, asksize, PROT_READ | PROT_WRITE,
172 		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
173       if (page == MAP_FAILED)
174 	{
175 	  if (error_callback)
176 	    error_callback (data, "mmap", errno);
177 	}
178       else
179 	{
180 	  size = (size + 7) & ~ (size_t) 7;
181 	  if (size < asksize)
182 	    backtrace_free (state, (char *) page + size, asksize - size,
183 			    error_callback, data);
184 
185 	  ret = page;
186 	}
187     }
188 
189   return ret;
190 }
191 
192 /* Free memory allocated by backtrace_alloc.  */
193 
194 void
backtrace_free(struct backtrace_state * state,void * addr,size_t size,backtrace_error_callback error_callback ATTRIBUTE_UNUSED,void * data ATTRIBUTE_UNUSED)195 backtrace_free (struct backtrace_state *state, void *addr, size_t size,
196 		backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
197 		void *data ATTRIBUTE_UNUSED)
198 {
199   int locked;
200 
201   /* If we are freeing a large aligned block, just release it back to
202      the system.  This case arises when growing a vector for a large
203      binary with lots of debug info.  Calling munmap here may cause us
204      to call mmap again if there is also a large shared library; we
205      just live with that.  */
206   if (size >= 16 * 4096)
207     {
208       size_t pagesize;
209 
210       pagesize = getpagesize ();
211       if (((uintptr_t) addr & (pagesize - 1)) == 0
212 	  && (size & (pagesize - 1)) == 0)
213 	{
214 	  /* If munmap fails for some reason, just add the block to
215 	     the freelist.  */
216 	  if (munmap (addr, size) == 0)
217 	    return;
218 	}
219     }
220 
221   /* If we can acquire the lock, add the new space to the free list.
222      If we can't acquire the lock, just leak the memory.
223      __sync_lock_test_and_set returns the old state of the lock, so we
224      have acquired it if it returns 0.  */
225 
226   if (!state->threaded)
227     locked = 1;
228   else
229     locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0;
230 
231   if (locked)
232     {
233       backtrace_free_locked (state, addr, size);
234 
235       if (state->threaded)
236 	__sync_lock_release (&state->lock_alloc);
237     }
238 }
239 
240 /* Grow VEC by SIZE bytes.  */
241 
242 void *
backtrace_vector_grow(struct backtrace_state * state,size_t size,backtrace_error_callback error_callback,void * data,struct backtrace_vector * vec)243 backtrace_vector_grow (struct backtrace_state *state,size_t size,
244 		       backtrace_error_callback error_callback,
245 		       void *data, struct backtrace_vector *vec)
246 {
247   void *ret;
248 
249   if (size > vec->alc)
250     {
251       size_t pagesize;
252       size_t alc;
253       void *base;
254 
255       pagesize = getpagesize ();
256       alc = vec->size + size;
257       if (vec->size == 0)
258 	alc = 16 * size;
259       else if (alc < pagesize)
260 	{
261 	  alc *= 2;
262 	  if (alc > pagesize)
263 	    alc = pagesize;
264 	}
265       else
266 	{
267 	  alc *= 2;
268 	  alc = (alc + pagesize - 1) & ~ (pagesize - 1);
269 	}
270       base = backtrace_alloc (state, alc, error_callback, data);
271       if (base == NULL)
272 	return NULL;
273       if (vec->base != NULL)
274 	{
275 	  memcpy (base, vec->base, vec->size);
276 	  backtrace_free (state, vec->base, vec->size + vec->alc,
277 			  error_callback, data);
278 	}
279       vec->base = base;
280       vec->alc = alc - vec->size;
281     }
282 
283   ret = (char *) vec->base + vec->size;
284   vec->size += size;
285   vec->alc -= size;
286   return ret;
287 }
288 
289 /* Finish the current allocation on VEC.  */
290 
291 void *
backtrace_vector_finish(struct backtrace_state * state ATTRIBUTE_UNUSED,struct backtrace_vector * vec,backtrace_error_callback error_callback ATTRIBUTE_UNUSED,void * data ATTRIBUTE_UNUSED)292 backtrace_vector_finish (
293   struct backtrace_state *state ATTRIBUTE_UNUSED,
294   struct backtrace_vector *vec,
295   backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
296   void *data ATTRIBUTE_UNUSED)
297 {
298   void *ret;
299 
300   ret = vec->base;
301   vec->base = (char *) vec->base + vec->size;
302   vec->size = 0;
303   return ret;
304 }
305 
306 /* Release any extra space allocated for VEC.  */
307 
308 int
backtrace_vector_release(struct backtrace_state * state,struct backtrace_vector * vec,backtrace_error_callback error_callback,void * data)309 backtrace_vector_release (struct backtrace_state *state,
310 			  struct backtrace_vector *vec,
311 			  backtrace_error_callback error_callback,
312 			  void *data)
313 {
314   size_t size;
315   size_t alc;
316   size_t aligned;
317 
318   /* Make sure that the block that we free is aligned on an 8-byte
319      boundary.  */
320   size = vec->size;
321   alc = vec->alc;
322   aligned = (size + 7) & ~ (size_t) 7;
323   alc -= aligned - size;
324 
325   backtrace_free (state, (char *) vec->base + aligned, alc,
326 		  error_callback, data);
327   vec->alc = 0;
328   if (vec->size == 0)
329     vec->base = NULL;
330   return 1;
331 }
332