summaryrefslogtreecommitdiff
path: root/elf/dl-find_object.h
blob: d9d75c4ad9d5784c9d7420fe62cc9822099683e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/* Locating objects in the process image.  ld.so implementation.
   Copyright (C) 2021-2025 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#ifndef _DL_FIND_EH_FRAME_H
#define _DL_FIND_EH_FRAME_H

#include <assert.h>
#include <atomic.h>
#include <dlfcn.h>
#include <ldsodefs.h>
#include <stdbool.h>
#include <stdint.h>

/* Internal version of struct dl_find_object.  Does not include the
   (yet unused) flags member.  We need to make a copy of data also in
   struct link_map to support non-contiguous mappings, and to support
   software transactional memory (the link map is not covered by
   transactions).  */
struct dl_find_object_internal
{
  uintptr_t map_start;
  uintptr_t map_end;            /* Set to map_start by dlclose.  */
  struct link_map *map;         /* Set to NULL by dlclose.  */
  void *eh_frame;
#if DLFO_STRUCT_HAS_EH_DBASE
  void *eh_dbase;
#endif
#if DLFO_STRUCT_HAS_EH_COUNT
  int eh_count;
#endif
  void *sframe;
};

/* Create a copy of *SOURCE in *COPY using relaxed MO loads and
   stores.  */
static inline void
_dl_find_object_internal_copy (const struct dl_find_object_internal *source,
                               struct dl_find_object_internal *copy)
{
  atomic_store_relaxed (&copy->map_start,
                        atomic_load_relaxed (&source->map_start));
  atomic_store_relaxed (&copy->map_end,
                        atomic_load_relaxed (&source->map_end));
  atomic_store_relaxed (&copy->map,
                        atomic_load_relaxed (&source->map));
  atomic_store_relaxed (&copy->eh_frame,
                        atomic_load_relaxed (&source->eh_frame));
#if DLFO_STRUCT_HAS_EH_DBASE
  atomic_store_relaxed (&copy->eh_dbase,
                        atomic_load_relaxed (&source->eh_dbase));
#endif
#if DLFO_STRUCT_HAS_EH_COUNT
  atomic_store_relaxed (&copy->eh_count,
                        atomic_load_relaxed (&source->eh_count));
#endif
  atomic_store_relaxed (&copy->sframe,
                        atomic_load_relaxed (&source->sframe));
}

static inline void
_dl_find_object_to_external (struct dl_find_object_internal *internal,
                             struct dl_find_object *external)
{
  external->dlfo_map_start = (void *) internal->map_start;
  external->dlfo_map_end = (void *) internal->map_end;
  external->dlfo_link_map = internal->map;
  external->dlfo_eh_frame = internal->eh_frame;
# if DLFO_STRUCT_HAS_EH_DBASE
  external->dlfo_eh_dbase = internal->eh_dbase;
# endif
# if DLFO_STRUCT_HAS_EH_COUNT
  external->dlfo_eh_count = internal->eh_count;
# endif
  external->dlfo_sframe = internal->sframe;
  if (internal->sframe != NULL)
    external->dlfo_flags = DLFO_FLAG_SFRAME;
  else
    external->dlfo_flags = 0;
}

/* Extract the object location data from a link map and writes it to
   *RESULT using relaxed MO stores.  Set L->l_find_object_processed.  */
static void __attribute__ ((unused))
_dl_find_object_from_map (struct link_map *l,
                          struct dl_find_object_internal *result)
{
  /* A mask to find out which segment has been read out.  */
  unsigned int read_seg = 0;

  atomic_store_relaxed (&result->map_start, (uintptr_t) l->l_map_start);
  atomic_store_relaxed (&result->map_end, (uintptr_t) l->l_map_end);
  atomic_store_relaxed (&result->map, l);

#if DLFO_STRUCT_HAS_EH_DBASE
  atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]);
#endif

  /* Initialize object's exception handling segment and SFrame segment
     data.  */
  atomic_store_relaxed (&result->sframe, NULL);
  atomic_store_relaxed (&result->eh_frame, NULL);
#if DLFO_STRUCT_HAS_EH_COUNT
  atomic_store_relaxed (&result->eh_count, 0);
#endif

  for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum;
       ph < ph_end; ++ph)
    {
      switch (ph->p_type)
        {
        case DLFO_EH_SEGMENT_TYPE:
          atomic_store_relaxed (&result->eh_frame,
                                (void *) (ph->p_vaddr + l->l_addr));
#if DLFO_STRUCT_HAS_EH_COUNT
          atomic_store_relaxed (&result->eh_count, ph->p_memsz / 8);
#endif
          read_seg |= 1;
          break;

        case PT_GNU_SFRAME:
          atomic_store_relaxed (&result->sframe,
                                (void *) (ph->p_vaddr + l->l_addr));
          read_seg |= 2;
          /* Fall through.  */
        default:
          break;
        }
      if (read_seg == 3)
        goto done;
   }

 done:
  l->l_find_object_processed = 1;
}

/* Called by the dynamic linker to set up the data structures for the
   initially loaded objects.  This creates a few persistent
   allocations, so it should be called with the minimal malloc.  */
void _dl_find_object_init (void) attribute_hidden;

/* Called by dlopen/dlmopen to add new objects to the DWARF EH frame
   data structures.  NEW_MAP is the dlopen'ed link map.  Link maps on
   the l_next list are added if l_object_processed is 0.  Needs to
   be protected by loader write lock.  Returns true on success, false
   on malloc failure.  */
bool _dl_find_object_update (struct link_map *new_map) attribute_hidden;

/* Called by dlclose to remove the link map from the DWARF EH frame
   data structures.  Needs to be protected by loader write lock.  */
void _dl_find_object_dlclose (struct link_map *l) attribute_hidden;

/* Called from __libc_freeres to deallocate malloc'ed memory.  */
void _dl_find_object_freeres (void) attribute_hidden;

#endif /* _DL_FIND_OBJECT_H */