| 1 | /* Locating objects in the process image. ld.so implementation. |
| 2 | Copyright (C) 2021-2022 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 | |