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