1 | /* Locating objects in the process image. ld.so implementation. |
2 | Copyright (C) 2021-2023 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #ifndef _DL_FIND_EH_FRAME_H |
20 | #define _DL_FIND_EH_FRAME_H |
21 | |
22 | #include <assert.h> |
23 | #include <atomic.h> |
24 | #include <dlfcn.h> |
25 | #include <ldsodefs.h> |
26 | #include <stdbool.h> |
27 | #include <stdint.h> |
28 | |
29 | /* Internal version of struct dl_find_object. Does not include the |
30 | (yet unused) flags member. We need to make a copy of data also in |
31 | struct link_map to support non-contiguous mappings, and to support |
32 | software transactional memory (the link map is not covered by |
33 | transactions). */ |
34 | struct dl_find_object_internal |
35 | { |
36 | uintptr_t map_start; |
37 | uintptr_t map_end; /* Set to map_start by dlclose. */ |
38 | struct link_map *map; /* Set to NULL by dlclose. */ |
39 | void *eh_frame; |
40 | #if DLFO_STRUCT_HAS_EH_DBASE |
41 | void *eh_dbase; |
42 | #endif |
43 | #if DLFO_STRUCT_HAS_EH_COUNT |
44 | int eh_count; |
45 | #endif |
46 | }; |
47 | |
48 | /* Create a copy of *SOURCE in *COPY using relaxed MO loads and |
49 | stores. */ |
50 | static inline void |
51 | _dl_find_object_internal_copy (const struct dl_find_object_internal *source, |
52 | struct dl_find_object_internal *copy) |
53 | { |
54 | atomic_store_relaxed (©->map_start, |
55 | atomic_load_relaxed (&source->map_start)); |
56 | atomic_store_relaxed (©->map_end, |
57 | atomic_load_relaxed (&source->map_end)); |
58 | atomic_store_relaxed (©->map, |
59 | atomic_load_relaxed (&source->map)); |
60 | atomic_store_relaxed (©->eh_frame, |
61 | atomic_load_relaxed (&source->eh_frame)); |
62 | #if DLFO_STRUCT_HAS_EH_DBASE |
63 | atomic_store_relaxed (©->eh_dbase, |
64 | atomic_load_relaxed (&source->eh_dbase)); |
65 | #endif |
66 | #if DLFO_STRUCT_HAS_EH_COUNT |
67 | atomic_store_relaxed (©->eh_count, |
68 | atomic_load_relaxed (&source->eh_count)); |
69 | #endif |
70 | } |
71 | |
72 | static inline void |
73 | _dl_find_object_to_external (struct dl_find_object_internal *internal, |
74 | struct dl_find_object *external) |
75 | { |
76 | external->dlfo_flags = 0; |
77 | external->dlfo_map_start = (void *) internal->map_start; |
78 | external->dlfo_map_end = (void *) internal->map_end; |
79 | external->dlfo_link_map = internal->map; |
80 | external->dlfo_eh_frame = internal->eh_frame; |
81 | # if DLFO_STRUCT_HAS_EH_DBASE |
82 | external->dlfo_eh_dbase = internal->eh_dbase; |
83 | # endif |
84 | # if DLFO_STRUCT_HAS_EH_COUNT |
85 | external->dlfo_eh_count = internal->eh_count; |
86 | # endif |
87 | } |
88 | |
89 | /* Extract the object location data from a link map and writes it to |
90 | *RESULT using relaxed MO stores. */ |
91 | static void __attribute__ ((unused)) |
92 | _dl_find_object_from_map (struct link_map *l, |
93 | struct dl_find_object_internal *result) |
94 | { |
95 | atomic_store_relaxed (&result->map_start, (uintptr_t) l->l_map_start); |
96 | atomic_store_relaxed (&result->map_end, (uintptr_t) l->l_map_end); |
97 | atomic_store_relaxed (&result->map, l); |
98 | |
99 | #if DLFO_STRUCT_HAS_EH_DBASE |
100 | atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]); |
101 | #endif |
102 | |
103 | for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum; |
104 | ph < ph_end; ++ph) |
105 | if (ph->p_type == DLFO_EH_SEGMENT_TYPE) |
106 | { |
107 | atomic_store_relaxed (&result->eh_frame, |
108 | (void *) (ph->p_vaddr + l->l_addr)); |
109 | #if DLFO_STRUCT_HAS_EH_COUNT |
110 | atomic_store_relaxed (&result->eh_count, ph->p_memsz / 8); |
111 | #endif |
112 | return; |
113 | } |
114 | |
115 | /* Object has no exception handling segment. */ |
116 | atomic_store_relaxed (&result->eh_frame, NULL); |
117 | #if DLFO_STRUCT_HAS_EH_COUNT |
118 | atomic_store_relaxed (&result->eh_count, 0); |
119 | #endif |
120 | } |
121 | |
122 | /* Called by the dynamic linker to set up the data structures for the |
123 | initially loaded objects. This creates a few persistent |
124 | allocations, so it should be called with the minimal malloc. */ |
125 | void _dl_find_object_init (void) attribute_hidden; |
126 | |
127 | /* Called by dlopen/dlmopen to add new objects to the DWARF EH frame |
128 | data structures. NEW_MAP is the dlopen'ed link map. Link maps on |
129 | the l_next list are added if l_object_processed is 0. Needs to |
130 | be protected by loader write lock. Returns true on success, false |
131 | on malloc failure. */ |
132 | bool _dl_find_object_update (struct link_map *new_map) attribute_hidden; |
133 | |
134 | /* Called by dlclose to remove the link map from the DWARF EH frame |
135 | data structures. Needs to be protected by loader write lock. */ |
136 | void _dl_find_object_dlclose (struct link_map *l) attribute_hidden; |
137 | |
138 | /* Called from __libc_freeres to deallocate malloc'ed memory. */ |
139 | void _dl_find_object_freeres (void) attribute_hidden; |
140 | |
141 | #endif /* _DL_FIND_OBJECT_H */ |
142 | |