1 /* Memory handling for libdw.
2    Copyright (C) 2003, 2004, 2006 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
5 
6    This file is free software; you can redistribute it and/or modify
7    it under the terms of either
8 
9      * the GNU Lesser General Public License as published by the Free
10        Software Foundation; either version 3 of the License, or (at
11        your option) any later version
12 
13    or
14 
15      * the GNU General Public License as published by the Free
16        Software Foundation; either version 2 of the License, or (at
17        your option) any later version
18 
19    or both in parallel, as here.
20 
21    elfutils is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24    General Public License for more details.
25 
26    You should have received copies of the GNU General Public License and
27    the GNU Lesser General Public License along with this program.  If
28    not, see <http://www.gnu.org/licenses/>.  */
29 
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33 
34 #include <errno.h>
35 #include <stdlib.h>
36 #include "libdwP.h"
37 #include "system.h"
38 #include "atomics.h"
39 #if USE_VG_ANNOTATIONS == 1
40 #include <helgrind.h>
41 #else
42 #define ANNOTATE_HAPPENS_BEFORE(X)
43 #define ANNOTATE_HAPPENS_AFTER(X)
44 #endif
45 
46 #define THREAD_ID_UNSET ((size_t) -1)
47 static __thread size_t thread_id = THREAD_ID_UNSET;
48 static atomic_size_t next_id = ATOMIC_VAR_INIT(0);
49 
50 struct libdw_memblock *
__libdw_alloc_tail(Dwarf * dbg)51 __libdw_alloc_tail (Dwarf *dbg)
52 {
53   if (thread_id == THREAD_ID_UNSET)
54     thread_id = atomic_fetch_add (&next_id, 1);
55 
56   pthread_rwlock_rdlock (&dbg->mem_rwl);
57   if (thread_id >= dbg->mem_stacks)
58     {
59       pthread_rwlock_unlock (&dbg->mem_rwl);
60       pthread_rwlock_wrlock (&dbg->mem_rwl);
61 
62       /* Another thread may have already reallocated. In theory using an
63          atomic would be faster, but given that this only happens once per
64          thread per Dwarf, some minor slowdown should be fine.  */
65       if (thread_id >= dbg->mem_stacks)
66         {
67           dbg->mem_tails = realloc (dbg->mem_tails, (thread_id+1)
68                                     * sizeof (struct libdw_memblock *));
69           if (dbg->mem_tails == NULL)
70             {
71               pthread_rwlock_unlock (&dbg->mem_rwl);
72               dbg->oom_handler();
73             }
74           for (size_t i = dbg->mem_stacks; i <= thread_id; i++)
75             dbg->mem_tails[i] = NULL;
76           dbg->mem_stacks = thread_id + 1;
77           ANNOTATE_HAPPENS_BEFORE (&dbg->mem_tails);
78         }
79 
80       pthread_rwlock_unlock (&dbg->mem_rwl);
81       pthread_rwlock_rdlock (&dbg->mem_rwl);
82     }
83 
84   /* At this point, we have an entry in the tail array.  */
85   ANNOTATE_HAPPENS_AFTER (&dbg->mem_tails);
86   struct libdw_memblock *result = dbg->mem_tails[thread_id];
87   if (result == NULL)
88     {
89       result = malloc (dbg->mem_default_size);
90       result->size = dbg->mem_default_size
91                      - offsetof (struct libdw_memblock, mem);
92       result->remaining = result->size;
93       result->prev = NULL;
94       dbg->mem_tails[thread_id] = result;
95     }
96   pthread_rwlock_unlock (&dbg->mem_rwl);
97   return result;
98 }
99 
100 /* Can only be called after a allocation for this thread has already
101    been done, to possibly undo it.  */
102 struct libdw_memblock *
__libdw_thread_tail(Dwarf * dbg)103 __libdw_thread_tail (Dwarf *dbg)
104 {
105   struct libdw_memblock *result;
106   pthread_rwlock_rdlock (&dbg->mem_rwl);
107   result = dbg->mem_tails[thread_id];
108   pthread_rwlock_unlock (&dbg->mem_rwl);
109   return result;
110 }
111 
112 void *
__libdw_allocate(Dwarf * dbg,size_t minsize,size_t align)113 __libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
114 {
115   size_t size = MAX (dbg->mem_default_size,
116 		     (align - 1 +
117 		      2 * minsize + offsetof (struct libdw_memblock, mem)));
118   struct libdw_memblock *newp = malloc (size);
119   if (newp == NULL)
120     dbg->oom_handler ();
121 
122   uintptr_t result = ((uintptr_t) newp->mem + align - 1) & ~(align - 1);
123 
124   newp->size = size - offsetof (struct libdw_memblock, mem);
125   newp->remaining = (uintptr_t) newp + size - (result + minsize);
126 
127   pthread_rwlock_rdlock (&dbg->mem_rwl);
128   newp->prev = dbg->mem_tails[thread_id];
129   dbg->mem_tails[thread_id] = newp;
130   pthread_rwlock_unlock (&dbg->mem_rwl);
131 
132   return (void *) result;
133 }
134 
135 
136 Dwarf_OOM
dwarf_new_oom_handler(Dwarf * dbg,Dwarf_OOM handler)137 dwarf_new_oom_handler (Dwarf *dbg, Dwarf_OOM handler)
138 {
139   Dwarf_OOM old = dbg->oom_handler;
140   dbg->oom_handler = handler;
141   return old;
142 }
143 
144 
145 void
146 __attribute ((noreturn)) attribute_hidden
__libdw_oom(void)147 __libdw_oom (void)
148 {
149   while (1)
150     error (EXIT_FAILURE, ENOMEM, "libdw");
151 }
152