| 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 | #include <assert.h> |
| 20 | #include <atomic.h> |
| 21 | #include <atomic_wide_counter.h> |
| 22 | #include <dl-find_object.h> |
| 23 | #include <dlfcn.h> |
| 24 | #include <ldsodefs.h> |
| 25 | #include <link.h> |
| 26 | #include <stdbool.h> |
| 27 | #include <stddef.h> |
| 28 | #include <stdint.h> |
| 29 | |
| 30 | /* Fallback implementation of _dl_find_object. It uses a linear |
| 31 | search, needs locking, and is not async-signal-safe. It is used in |
| 32 | _dl_find_object prior to initialization, when called from audit |
| 33 | modules. It also serves as the reference implementation for |
| 34 | _dl_find_object. */ |
| 35 | static int |
| 36 | _dl_find_object_slow (void *pc, struct dl_find_object *result) |
| 37 | { |
| 38 | ElfW(Addr) addr = (ElfW(Addr)) pc; |
| 39 | for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) |
| 40 | for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; |
| 41 | l = l->l_next) |
| 42 | if (addr >= l->l_map_start && addr < l->l_map_end |
| 43 | && (l->l_contiguous || _dl_addr_inside_object (l, addr))) |
| 44 | { |
| 45 | assert (ns == l->l_ns); |
| 46 | struct dl_find_object_internal internal; |
| 47 | _dl_find_object_from_map (l, &internal); |
| 48 | _dl_find_object_to_external (&internal, result); |
| 49 | return 0; |
| 50 | } |
| 51 | |
| 52 | /* Object not found. */ |
| 53 | return -1; |
| 54 | } |
| 55 | |
| 56 | /* Data for the main executable. There is usually a large gap between |
| 57 | the main executable and initially loaded shared objects. Record |
| 58 | the main executable separately, to increase the chance that the |
| 59 | range for the non-closeable mappings below covers only the shared |
| 60 | objects (and not also the gap between main executable and shared |
| 61 | objects). */ |
| 62 | static struct dl_find_object_internal _dlfo_main attribute_relro; |
| 63 | |
| 64 | /* Data for initially loaded shared objects that cannot be unloaded. |
| 65 | (This may also contain non-contiguous mappings from the main |
| 66 | executable.) The mappings are stored in address order in the |
| 67 | _dlfo_nodelete_mappings array (containing |
| 68 | _dlfo_nodelete_mappings_size elements). It is not modified after |
| 69 | initialization. */ |
| 70 | static uintptr_t _dlfo_nodelete_mappings_end attribute_relro; |
| 71 | static size_t _dlfo_nodelete_mappings_size attribute_relro; |
| 72 | static struct dl_find_object_internal *_dlfo_nodelete_mappings |
| 73 | attribute_relro; |
| 74 | |
| 75 | /* Mappings created by dlopen can go away with dlclose, so a dynamic |
| 76 | data structure with some synchronization is needed. Individual |
| 77 | segments are similar to the _dlfo_nodelete_mappings array above. |
| 78 | The previous segment contains lower addresses and is at most half |
| 79 | as long. Checking the address of the base address of the first |
| 80 | element during a lookup can therefore approximate a binary search |
| 81 | over all segments, even though the data is not stored in one |
| 82 | contiguous array. |
| 83 | |
| 84 | During updates, the segments are overwritten in place. A software |
| 85 | transactional memory construct (involving the |
| 86 | _dlfo_loaded_mappings_version variable) is used to detect |
| 87 | concurrent modification, and retry as necessary. (This approach is |
| 88 | similar to seqlocks, except that two copies are used, and there is |
| 89 | only one writer, ever, due to the loader lock.) Technically, |
| 90 | relaxed MO loads and stores need to be used for the shared TM data, |
| 91 | to avoid data races. |
| 92 | |
| 93 | The memory allocations are never deallocated, but slots used for |
| 94 | objects that have been dlclose'd can be reused by dlopen. The |
| 95 | memory can live in the regular C malloc heap. |
| 96 | |
| 97 | The segments are populated from the start of the list, with the |
| 98 | mappings with the highest address. Only if this segment is full, |
| 99 | previous segments are used for mappings at lower addresses. The |
| 100 | remaining segments are populated as needed, but after allocating |
| 101 | further segments, some of the initial segments (at the end of the |
| 102 | linked list) can be empty (with size 0). |
| 103 | |
| 104 | Adding new elements to this data structure is another source of |
| 105 | quadratic behavior for dlopen. If the other causes of quadratic |
| 106 | behavior are eliminated, a more complicated data structure will be |
| 107 | needed. */ |
| 108 | struct dlfo_mappings_segment |
| 109 | { |
| 110 | /* The previous segment has lower base addresses. Constant after |
| 111 | initialization; read in the TM region. */ |
| 112 | struct dlfo_mappings_segment *previous; |
| 113 | |
| 114 | /* Used by __libc_freeres to deallocate malloc'ed memory. */ |
| 115 | void *to_free; |
| 116 | |
| 117 | /* Count of array elements in use and allocated. */ |
| 118 | size_t size; /* Read in the TM region. */ |
| 119 | size_t allocated; |
| 120 | |
| 121 | struct dl_find_object_internal objects[]; /* Read in the TM region. */ |
| 122 | }; |
| 123 | |
| 124 | /* To achieve async-signal-safety, two copies of the data structure |
| 125 | are used, so that a signal handler can still use this data even if |
| 126 | dlopen or dlclose modify the other copy. The the least significant |
| 127 | bit in _dlfo_loaded_mappings_version determines which array element |
| 128 | is the currently active region. */ |
| 129 | static struct dlfo_mappings_segment *_dlfo_loaded_mappings[2]; |
| 130 | |
| 131 | /* Returns the number of actually used elements in all segments |
| 132 | starting at SEG. */ |
| 133 | static inline size_t |
| 134 | _dlfo_mappings_segment_count_used (struct dlfo_mappings_segment *seg) |
| 135 | { |
| 136 | size_t count = 0; |
| 137 | for (; seg != NULL && seg->size > 0; seg = seg->previous) |
| 138 | for (size_t i = 0; i < seg->size; ++i) |
| 139 | /* Exclude elements which have been dlclose'd. */ |
| 140 | count += seg->objects[i].map != NULL; |
| 141 | return count; |
| 142 | } |
| 143 | |
| 144 | /* Compute the total number of available allocated segments linked |
| 145 | from SEG. */ |
| 146 | static inline size_t |
| 147 | _dlfo_mappings_segment_count_allocated (struct dlfo_mappings_segment *seg) |
| 148 | { |
| 149 | size_t count = 0; |
| 150 | for (; seg != NULL; seg = seg->previous) |
| 151 | count += seg->allocated; |
| 152 | return count; |
| 153 | } |
| 154 | |
| 155 | /* This is essentially an arbitrary value. dlopen allocates plenty of |
| 156 | memory anyway, so over-allocated a bit does not hurt. Not having |
| 157 | many small-ish segments helps to avoid many small binary searches. |
| 158 | Not using a power of 2 means that we do not waste an extra page |
| 159 | just for the malloc header if a mapped allocation is used in the |
| 160 | glibc allocator. */ |
| 161 | enum { dlfo_mappings_initial_segment_size = 63 }; |
| 162 | |
| 163 | /* Allocate an empty segment. This used for the first ever |
| 164 | allocation. */ |
| 165 | static struct dlfo_mappings_segment * |
| 166 | _dlfo_mappings_segment_allocate_unpadded (size_t size) |
| 167 | { |
| 168 | if (size < dlfo_mappings_initial_segment_size) |
| 169 | size = dlfo_mappings_initial_segment_size; |
| 170 | /* No overflow checks here because the size is a mapping count, and |
| 171 | struct link_map is larger than what we allocate here. */ |
| 172 | enum |
| 173 | { |
| 174 | element_size = sizeof ((struct dlfo_mappings_segment) {}.objects[0]) |
| 175 | }; |
| 176 | size_t to_allocate = (sizeof (struct dlfo_mappings_segment) |
| 177 | + size * element_size); |
| 178 | struct dlfo_mappings_segment *result = malloc (to_allocate); |
| 179 | if (result != NULL) |
| 180 | { |
| 181 | result->previous = NULL; |
| 182 | result->to_free = NULL; /* Minimal malloc memory cannot be freed. */ |
| 183 | result->size = 0; |
| 184 | result->allocated = size; |
| 185 | } |
| 186 | return result; |
| 187 | } |
| 188 | |
| 189 | /* Allocate an empty segment that is at least SIZE large. PREVIOUS |
| 190 | points to the chain of previously allocated segments and can be |
| 191 | NULL. */ |
| 192 | static struct dlfo_mappings_segment * |
| 193 | _dlfo_mappings_segment_allocate (size_t size, |
| 194 | struct dlfo_mappings_segment * previous) |
| 195 | { |
| 196 | /* Exponential sizing policies, so that lookup approximates a binary |
| 197 | search. */ |
| 198 | { |
| 199 | size_t minimum_growth; |
| 200 | if (previous == NULL) |
| 201 | minimum_growth = dlfo_mappings_initial_segment_size; |
| 202 | else |
| 203 | minimum_growth = 2* previous->allocated; |
| 204 | if (size < minimum_growth) |
| 205 | size = minimum_growth; |
| 206 | } |
| 207 | enum { cache_line_size_estimate = 128 }; |
| 208 | /* No overflow checks here because the size is a mapping count, and |
| 209 | struct link_map is larger than what we allocate here. */ |
| 210 | enum |
| 211 | { |
| 212 | element_size = sizeof ((struct dlfo_mappings_segment) {}.objects[0]) |
| 213 | }; |
| 214 | size_t to_allocate = (sizeof (struct dlfo_mappings_segment) |
| 215 | + size * element_size |
| 216 | + 2 * cache_line_size_estimate); |
| 217 | char *ptr = malloc (to_allocate); |
| 218 | if (ptr == NULL) |
| 219 | return NULL; |
| 220 | char *original_ptr = ptr; |
| 221 | /* Start and end at a (conservative) 128-byte cache line boundary. |
| 222 | Do not use memalign for compatibility with partially interposing |
| 223 | malloc implementations. */ |
| 224 | char *end = PTR_ALIGN_DOWN (ptr + to_allocate, cache_line_size_estimate); |
| 225 | ptr = PTR_ALIGN_UP (ptr, cache_line_size_estimate); |
| 226 | struct dlfo_mappings_segment *result |
| 227 | = (struct dlfo_mappings_segment *) ptr; |
| 228 | result->previous = previous; |
| 229 | result->to_free = original_ptr; |
| 230 | result->size = 0; |
| 231 | /* We may have obtained slightly more space if malloc happened |
| 232 | to provide an over-aligned pointer. */ |
| 233 | result->allocated = (((uintptr_t) (end - ptr) |
| 234 | - sizeof (struct dlfo_mappings_segment)) |
| 235 | / element_size); |
| 236 | assert (result->allocated >= size); |
| 237 | return result; |
| 238 | } |
| 239 | |
| 240 | /* Monotonic counter for software transactional memory. The lowest |
| 241 | bit indicates which element of the _dlfo_loaded_mappings contains |
| 242 | up-to-date data. */ |
| 243 | static __atomic_wide_counter _dlfo_loaded_mappings_version; |
| 244 | |
| 245 | /* TM version at the start of the read operation. */ |
| 246 | static inline uint64_t |
| 247 | _dlfo_read_start_version (void) |
| 248 | { |
| 249 | /* Acquire MO load synchronizes with the fences at the beginning and |
| 250 | end of the TM update region in _dlfo_mappings_begin_update, |
| 251 | _dlfo_mappings_end_update. */ |
| 252 | return __atomic_wide_counter_load_acquire (&_dlfo_loaded_mappings_version); |
| 253 | } |
| 254 | |
| 255 | /* Optimized variant of _dlfo_read_start_version which can be called |
| 256 | when the loader is write-locked. */ |
| 257 | static inline uint64_t |
| 258 | _dlfo_read_version_locked (void) |
| 259 | { |
| 260 | return __atomic_wide_counter_load_relaxed (&_dlfo_loaded_mappings_version); |
| 261 | } |
| 262 | |
| 263 | /* Update the version to reflect that an update is happening. This |
| 264 | does not change the bit that controls the active segment chain. */ |
| 265 | static inline void |
| 266 | _dlfo_mappings_begin_update (void) |
| 267 | { |
| 268 | /* The fence synchronizes with loads in _dlfo_read_start_version |
| 269 | (also called from _dlfo_read_success). */ |
| 270 | atomic_thread_fence_release (); |
| 271 | } |
| 272 | |
| 273 | /* Installs the just-updated version as the active version. */ |
| 274 | static inline void |
| 275 | _dlfo_mappings_end_update (void) |
| 276 | { |
| 277 | /* The fence synchronizes with loads in _dlfo_read_start_version |
| 278 | (also called from _dlfo_read_success). */ |
| 279 | atomic_thread_fence_release (); |
| 280 | /* No atomic read-modify-write update needed because of the loader |
| 281 | lock. */ |
| 282 | __atomic_wide_counter_add_relaxed (&_dlfo_loaded_mappings_version, 1); |
| 283 | } |
| 284 | |
| 285 | /* Return true if the read was successful, given the start |
| 286 | version. */ |
| 287 | static inline bool |
| 288 | _dlfo_read_success (uint64_t start_version) |
| 289 | { |
| 290 | /* See Hans Boehm, Can Seqlocks Get Along with Programming Language |
| 291 | Memory Models?, Section 4. This is necessary so that loads in |
| 292 | the TM region are not ordered past the version check below. */ |
| 293 | atomic_thread_fence_acquire (); |
| 294 | |
| 295 | /* Synchronizes with the fences in _dlfo_mappings_begin_update, |
| 296 | _dlfo_mappings_end_update. It is important that all stores from |
| 297 | the last update have become visible, and stores from the next |
| 298 | update to this version are not before the version number is |
| 299 | updated. |
| 300 | |
| 301 | Unlike with seqlocks, there is no check for odd versions here |
| 302 | because we have read the unmodified copy (confirmed to be |
| 303 | unmodified by the unchanged version). */ |
| 304 | return _dlfo_read_start_version () == start_version; |
| 305 | } |
| 306 | |
| 307 | /* Returns the active segment identified by the specified start |
| 308 | version. */ |
| 309 | static struct dlfo_mappings_segment * |
| 310 | _dlfo_mappings_active_segment (uint64_t start_version) |
| 311 | { |
| 312 | return _dlfo_loaded_mappings[start_version & 1]; |
| 313 | } |
| 314 | |
| 315 | /* Searches PC among the address-sorted array [FIRST1, FIRST1 + |
| 316 | SIZE). Assumes PC >= FIRST1->map_start. Returns a pointer to the |
| 317 | element that contains PC, or NULL if there is no such element. */ |
| 318 | static inline struct dl_find_object_internal * |
| 319 | _dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) |
| 320 | { |
| 321 | struct dl_find_object_internal *end = first1 + size; |
| 322 | |
| 323 | /* Search for a lower bound in first. */ |
| 324 | struct dl_find_object_internal *first = first1; |
| 325 | while (size > 0) |
| 326 | { |
| 327 | size_t half = size >> 1; |
| 328 | struct dl_find_object_internal *middle = first + half; |
| 329 | if (atomic_load_relaxed (&middle->map_start) < pc) |
| 330 | { |
| 331 | first = middle + 1; |
| 332 | size -= half + 1; |
| 333 | } |
| 334 | else |
| 335 | size = half; |
| 336 | } |
| 337 | |
| 338 | if (first != end && pc == atomic_load_relaxed (&first->map_start)) |
| 339 | { |
| 340 | if (pc < atomic_load_relaxed (&first->map_end)) |
| 341 | return first; |
| 342 | else |
| 343 | /* Zero-length mapping after dlclose. */ |
| 344 | return NULL; |
| 345 | } |
| 346 | else |
| 347 | { |
| 348 | /* Check to see if PC is in the previous mapping. */ |
| 349 | --first; |
| 350 | if (pc < atomic_load_relaxed (&first->map_end)) |
| 351 | /* pc >= first->map_start implied by the search above. */ |
| 352 | return first; |
| 353 | else |
| 354 | return NULL; |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | int |
| 359 | _dl_find_object (void *pc1, struct dl_find_object *result) |
| 360 | { |
| 361 | uintptr_t pc = (uintptr_t) pc1; |
| 362 | |
| 363 | if (__glibc_unlikely (_dlfo_main.map_end == 0)) |
| 364 | { |
| 365 | /* Not initialized. No locking is needed here because this can |
| 366 | only be called from audit modules, which cannot create |
| 367 | threads. */ |
| 368 | return _dl_find_object_slow (pc1, result); |
| 369 | } |
| 370 | |
| 371 | /* Main executable. */ |
| 372 | if (pc >= _dlfo_main.map_start && pc < _dlfo_main.map_end) |
| 373 | { |
| 374 | _dl_find_object_to_external (&_dlfo_main, result); |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* Other initially loaded objects. */ |
| 379 | if (pc >= _dlfo_nodelete_mappings->map_start |
| 380 | && pc < _dlfo_nodelete_mappings_end) |
| 381 | { |
| 382 | struct dl_find_object_internal *obj |
| 383 | = _dlfo_lookup (pc, _dlfo_nodelete_mappings, |
| 384 | _dlfo_nodelete_mappings_size); |
| 385 | if (obj != NULL) |
| 386 | { |
| 387 | _dl_find_object_to_external (obj, result); |
| 388 | return 0; |
| 389 | } |
| 390 | /* Fall through to the full search. The kernel may have mapped |
| 391 | the initial mappings with gaps that are later filled by |
| 392 | dlopen with other mappings. */ |
| 393 | } |
| 394 | |
| 395 | /* Handle audit modules, dlopen, dlopen objects. This uses software |
| 396 | transactional memory, with a retry loop in case the version |
| 397 | changes during execution. */ |
| 398 | while (true) |
| 399 | { |
| 400 | retry: |
| 401 | ; |
| 402 | uint64_t start_version = _dlfo_read_start_version (); |
| 403 | |
| 404 | /* The read through seg->previous assumes that the CPU |
| 405 | recognizes the load dependency, so that no invalid size |
| 406 | values is read. Furthermore, the code assumes that no |
| 407 | out-of-thin-air value for seg->size is observed. Together, |
| 408 | this ensures that the observed seg->size value is always less |
| 409 | than seg->allocated, so that _dlfo_mappings_index does not |
| 410 | read out-of-bounds. (This avoids intermediate TM version |
| 411 | verification. A concurrent version update will lead to |
| 412 | invalid lookup results, but not to out-of-memory access.) |
| 413 | |
| 414 | Either seg == NULL or seg->size == 0 terminates the segment |
| 415 | list. _dl_find_object_update does not bother to clear the |
| 416 | size on earlier unused segments. */ |
| 417 | for (struct dlfo_mappings_segment *seg |
| 418 | = _dlfo_mappings_active_segment (start_version); |
| 419 | seg != NULL; |
| 420 | seg = atomic_load_acquire (&seg->previous)) |
| 421 | { |
| 422 | size_t seg_size = atomic_load_relaxed (&seg->size); |
| 423 | if (seg_size == 0) |
| 424 | break; |
| 425 | |
| 426 | if (pc >= atomic_load_relaxed (&seg->objects[0].map_start)) |
| 427 | { |
| 428 | /* PC may lie within this segment. If it is less than the |
| 429 | segment start address, it can only lie in a previous |
| 430 | segment, due to the base address sorting. */ |
| 431 | struct dl_find_object_internal *obj |
| 432 | = _dlfo_lookup (pc, seg->objects, seg_size); |
| 433 | |
| 434 | if (obj != NULL) |
| 435 | { |
| 436 | /* Found the right mapping. Copy out the data prior to |
| 437 | checking if the read transaction was successful. */ |
| 438 | struct dl_find_object_internal copy; |
| 439 | _dl_find_object_internal_copy (obj, ©); |
| 440 | if (_dlfo_read_success (start_version)) |
| 441 | { |
| 442 | _dl_find_object_to_external (©, result); |
| 443 | return 0; |
| 444 | } |
| 445 | else |
| 446 | /* Read transaction failure. */ |
| 447 | goto retry; |
| 448 | } |
| 449 | else |
| 450 | { |
| 451 | /* PC is not covered by this mapping. */ |
| 452 | if (_dlfo_read_success (start_version)) |
| 453 | return -1; |
| 454 | else |
| 455 | /* Read transaction failure. */ |
| 456 | goto retry; |
| 457 | } |
| 458 | } /* if: PC might lie within the current seg. */ |
| 459 | } |
| 460 | |
| 461 | /* PC is not covered by any segment. */ |
| 462 | if (_dlfo_read_success (start_version)) |
| 463 | return -1; |
| 464 | } /* Transaction retry loop. */ |
| 465 | } |
| 466 | rtld_hidden_def (_dl_find_object) |
| 467 | |
| 468 | /* _dlfo_process_initial is called twice. First to compute the array |
| 469 | sizes from the initial loaded mappings. Second to fill in the |
| 470 | bases and infos arrays with the (still unsorted) data. Returns the |
| 471 | number of loaded (non-nodelete) mappings. */ |
| 472 | static size_t |
| 473 | _dlfo_process_initial (void) |
| 474 | { |
| 475 | struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; |
| 476 | |
| 477 | size_t nodelete = 0; |
| 478 | if (!main_map->l_contiguous) |
| 479 | { |
| 480 | struct dl_find_object_internal dlfo; |
| 481 | _dl_find_object_from_map (main_map, &dlfo); |
| 482 | |
| 483 | /* PT_LOAD segments for a non-contiguous are added to the |
| 484 | non-closeable mappings. */ |
| 485 | for (const ElfW(Phdr) *ph = main_map->l_phdr, |
| 486 | *ph_end = main_map->l_phdr + main_map->l_phnum; |
| 487 | ph < ph_end; ++ph) |
| 488 | if (ph->p_type == PT_LOAD) |
| 489 | { |
| 490 | if (_dlfo_nodelete_mappings != NULL) |
| 491 | { |
| 492 | /* Second pass only. */ |
| 493 | _dlfo_nodelete_mappings[nodelete] = dlfo; |
| 494 | _dlfo_nodelete_mappings[nodelete].map_start |
| 495 | = ph->p_vaddr + main_map->l_addr; |
| 496 | _dlfo_nodelete_mappings[nodelete].map_end |
| 497 | = _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz; |
| 498 | } |
| 499 | ++nodelete; |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | size_t loaded = 0; |
| 504 | for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) |
| 505 | for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; |
| 506 | l = l->l_next) |
| 507 | /* Skip the main map processed above, and proxy maps. */ |
| 508 | if (l != main_map && l == l->l_real) |
| 509 | { |
| 510 | /* lt_library link maps are implicitly NODELETE. */ |
| 511 | if (l->l_type == lt_library || l->l_nodelete_active) |
| 512 | { |
| 513 | if (_dlfo_nodelete_mappings != NULL) |
| 514 | /* Second pass only. */ |
| 515 | _dl_find_object_from_map |
| 516 | (l, _dlfo_nodelete_mappings + nodelete); |
| 517 | ++nodelete; |
| 518 | } |
| 519 | else if (l->l_type == lt_loaded) |
| 520 | { |
| 521 | if (_dlfo_loaded_mappings[0] != NULL) |
| 522 | /* Second pass only. */ |
| 523 | _dl_find_object_from_map |
| 524 | (l, &_dlfo_loaded_mappings[0]->objects[loaded]); |
| 525 | ++loaded; |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | _dlfo_nodelete_mappings_size = nodelete; |
| 530 | return loaded; |
| 531 | } |
| 532 | |
| 533 | /* Selection sort based on mapping start address. */ |
| 534 | void |
| 535 | _dlfo_sort_mappings (struct dl_find_object_internal *objects, size_t size) |
| 536 | { |
| 537 | if (size < 2) |
| 538 | return; |
| 539 | |
| 540 | for (size_t i = 0; i < size - 1; ++i) |
| 541 | { |
| 542 | /* Find minimum. */ |
| 543 | size_t min_idx = i; |
| 544 | uintptr_t min_val = objects[i].map_start; |
| 545 | for (size_t j = i + 1; j < size; ++j) |
| 546 | if (objects[j].map_start < min_val) |
| 547 | { |
| 548 | min_idx = j; |
| 549 | min_val = objects[j].map_start; |
| 550 | } |
| 551 | |
| 552 | /* Swap into place. */ |
| 553 | struct dl_find_object_internal tmp = objects[min_idx]; |
| 554 | objects[min_idx] = objects[i]; |
| 555 | objects[i] = tmp; |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | void |
| 560 | _dl_find_object_init (void) |
| 561 | { |
| 562 | /* Cover the main mapping. */ |
| 563 | { |
| 564 | struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; |
| 565 | |
| 566 | if (main_map->l_contiguous) |
| 567 | _dl_find_object_from_map (main_map, &_dlfo_main); |
| 568 | else |
| 569 | { |
| 570 | /* Non-contiguous main maps are handled in |
| 571 | _dlfo_process_initial. Mark as initialized, but not |
| 572 | coverying any valid PC. */ |
| 573 | _dlfo_main.map_start = -1; |
| 574 | _dlfo_main.map_end = -1; |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | /* Allocate the data structures. */ |
| 579 | size_t loaded_size = _dlfo_process_initial (); |
| 580 | _dlfo_nodelete_mappings = malloc (_dlfo_nodelete_mappings_size |
| 581 | * sizeof (*_dlfo_nodelete_mappings)); |
| 582 | if (loaded_size > 0) |
| 583 | _dlfo_loaded_mappings[0] |
| 584 | = _dlfo_mappings_segment_allocate_unpadded (loaded_size); |
| 585 | if (_dlfo_nodelete_mappings == NULL |
| 586 | || (loaded_size > 0 && _dlfo_loaded_mappings[0] == NULL)) |
| 587 | _dl_fatal_printf ("\ |
| 588 | Fatal glibc error: cannot allocate memory for find-object data\n" ); |
| 589 | /* Fill in the data with the second call. */ |
| 590 | _dlfo_nodelete_mappings_size = 0; |
| 591 | _dlfo_process_initial (); |
| 592 | |
| 593 | /* Sort both arrays. */ |
| 594 | if (_dlfo_nodelete_mappings_size > 0) |
| 595 | { |
| 596 | _dlfo_sort_mappings (_dlfo_nodelete_mappings, |
| 597 | _dlfo_nodelete_mappings_size); |
| 598 | size_t last_idx = _dlfo_nodelete_mappings_size - 1; |
| 599 | _dlfo_nodelete_mappings_end = _dlfo_nodelete_mappings[last_idx].map_end; |
| 600 | } |
| 601 | if (loaded_size > 0) |
| 602 | _dlfo_sort_mappings (_dlfo_loaded_mappings[0]->objects, |
| 603 | _dlfo_loaded_mappings[0]->size); |
| 604 | } |
| 605 | |
| 606 | static void |
| 607 | _dl_find_object_link_map_sort (struct link_map **loaded, size_t size) |
| 608 | { |
| 609 | /* Selection sort based on map_start. */ |
| 610 | if (size < 2) |
| 611 | return; |
| 612 | for (size_t i = 0; i < size - 1; ++i) |
| 613 | { |
| 614 | /* Find minimum. */ |
| 615 | size_t min_idx = i; |
| 616 | ElfW(Addr) min_val = loaded[i]->l_map_start; |
| 617 | for (size_t j = i + 1; j < size; ++j) |
| 618 | if (loaded[j]->l_map_start < min_val) |
| 619 | { |
| 620 | min_idx = j; |
| 621 | min_val = loaded[j]->l_map_start; |
| 622 | } |
| 623 | |
| 624 | /* Swap into place. */ |
| 625 | struct link_map *tmp = loaded[min_idx]; |
| 626 | loaded[min_idx] = loaded[i]; |
| 627 | loaded[i] = tmp; |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | /* Initializes the segment for writing. Returns the target write |
| 632 | index (plus 1) in this segment. The index is chosen so that a |
| 633 | partially filled segment still has data at index 0. */ |
| 634 | static inline size_t |
| 635 | _dlfo_update_init_seg (struct dlfo_mappings_segment *seg, |
| 636 | size_t remaining_to_add) |
| 637 | { |
| 638 | size_t new_seg_size; |
| 639 | if (remaining_to_add < seg->allocated) |
| 640 | /* Partially filled segment. */ |
| 641 | new_seg_size = remaining_to_add; |
| 642 | else |
| 643 | new_seg_size = seg->allocated; |
| 644 | atomic_store_relaxed (&seg->size, new_seg_size); |
| 645 | return new_seg_size; |
| 646 | } |
| 647 | |
| 648 | /* Invoked from _dl_find_object_update after sorting. Stores to the |
| 649 | shared data need to use relaxed MO. But plain loads can be used |
| 650 | because the loader lock prevents concurrent stores. */ |
| 651 | static bool |
| 652 | _dl_find_object_update_1 (struct link_map **loaded, size_t count) |
| 653 | { |
| 654 | int active_idx = _dlfo_read_version_locked () & 1; |
| 655 | |
| 656 | struct dlfo_mappings_segment *current_seg |
| 657 | = _dlfo_loaded_mappings[active_idx]; |
| 658 | size_t current_used = _dlfo_mappings_segment_count_used (current_seg); |
| 659 | |
| 660 | struct dlfo_mappings_segment *target_seg |
| 661 | = _dlfo_loaded_mappings[!active_idx]; |
| 662 | size_t remaining_to_add = current_used + count; |
| 663 | |
| 664 | /* Ensure that the new segment chain has enough space. */ |
| 665 | { |
| 666 | size_t new_allocated |
| 667 | = _dlfo_mappings_segment_count_allocated (target_seg); |
| 668 | if (new_allocated < remaining_to_add) |
| 669 | { |
| 670 | size_t more = remaining_to_add - new_allocated; |
| 671 | target_seg = _dlfo_mappings_segment_allocate (more, target_seg); |
| 672 | if (target_seg == NULL) |
| 673 | /* Out of memory. Do not end the update and keep the |
| 674 | current version unchanged. */ |
| 675 | return false; |
| 676 | |
| 677 | /* Start update cycle. */ |
| 678 | _dlfo_mappings_begin_update (); |
| 679 | |
| 680 | /* The barrier ensures that a concurrent TM read or fork does |
| 681 | not see a partially initialized segment. */ |
| 682 | atomic_store_release (&_dlfo_loaded_mappings[!active_idx], target_seg); |
| 683 | } |
| 684 | else |
| 685 | /* Start update cycle without allocation. */ |
| 686 | _dlfo_mappings_begin_update (); |
| 687 | } |
| 688 | |
| 689 | size_t target_seg_index1 = _dlfo_update_init_seg (target_seg, |
| 690 | remaining_to_add); |
| 691 | |
| 692 | /* Merge the current_seg segment list with the loaded array into the |
| 693 | target_set. Merging occurs backwards, in decreasing l_map_start |
| 694 | order. */ |
| 695 | size_t loaded_index1 = count; |
| 696 | size_t current_seg_index1; |
| 697 | if (current_seg == NULL) |
| 698 | current_seg_index1 = 0; |
| 699 | else |
| 700 | current_seg_index1 = current_seg->size; |
| 701 | while (true) |
| 702 | { |
| 703 | if (current_seg_index1 == 0) |
| 704 | { |
| 705 | /* Switch to the previous segment. */ |
| 706 | if (current_seg != NULL) |
| 707 | current_seg = current_seg->previous; |
| 708 | if (current_seg != NULL) |
| 709 | { |
| 710 | current_seg_index1 = current_seg->size; |
| 711 | if (current_seg_index1 == 0) |
| 712 | /* No more data in previous segments. */ |
| 713 | current_seg = NULL; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | if (current_seg != NULL |
| 718 | && (current_seg->objects[current_seg_index1 - 1].map == NULL)) |
| 719 | { |
| 720 | /* This mapping has been dlclose'd. Do not copy it. */ |
| 721 | --current_seg_index1; |
| 722 | continue; |
| 723 | } |
| 724 | |
| 725 | if (loaded_index1 == 0 && current_seg == NULL) |
| 726 | /* No more data in either source. */ |
| 727 | break; |
| 728 | |
| 729 | /* Make room for another mapping. */ |
| 730 | assert (remaining_to_add > 0); |
| 731 | if (target_seg_index1 == 0) |
| 732 | { |
| 733 | /* Switch segments and set the size of the segment. */ |
| 734 | target_seg = target_seg->previous; |
| 735 | target_seg_index1 = _dlfo_update_init_seg (target_seg, |
| 736 | remaining_to_add); |
| 737 | } |
| 738 | |
| 739 | /* Determine where to store the data. */ |
| 740 | struct dl_find_object_internal *dlfo |
| 741 | = &target_seg->objects[target_seg_index1 - 1]; |
| 742 | |
| 743 | if (loaded_index1 == 0 |
| 744 | || (current_seg != NULL |
| 745 | && (loaded[loaded_index1 - 1]->l_map_start |
| 746 | < current_seg->objects[current_seg_index1 - 1].map_start))) |
| 747 | { |
| 748 | /* Prefer mapping in current_seg. */ |
| 749 | assert (current_seg_index1 > 0); |
| 750 | _dl_find_object_internal_copy |
| 751 | (¤t_seg->objects[current_seg_index1 - 1], dlfo); |
| 752 | --current_seg_index1; |
| 753 | } |
| 754 | else |
| 755 | { |
| 756 | /* Prefer newly loaded link map. */ |
| 757 | assert (loaded_index1 > 0); |
| 758 | _dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo); |
| 759 | loaded[loaded_index1 - 1]->l_find_object_processed = 1; |
| 760 | --loaded_index1; |
| 761 | } |
| 762 | |
| 763 | /* Consume space in target segment. */ |
| 764 | --target_seg_index1; |
| 765 | |
| 766 | --remaining_to_add; |
| 767 | } |
| 768 | |
| 769 | /* Everything has been added. */ |
| 770 | assert (remaining_to_add == 0); |
| 771 | |
| 772 | /* The segment must have been filled up to the beginning. */ |
| 773 | assert (target_seg_index1 == 0); |
| 774 | |
| 775 | /* Prevent searching further into unused segments. */ |
| 776 | if (target_seg->previous != NULL) |
| 777 | atomic_store_relaxed (&target_seg->previous->size, 0); |
| 778 | |
| 779 | _dlfo_mappings_end_update (); |
| 780 | return true; |
| 781 | } |
| 782 | |
| 783 | bool |
| 784 | _dl_find_object_update (struct link_map *new_map) |
| 785 | { |
| 786 | /* Copy the newly-loaded link maps into an array for sorting. */ |
| 787 | size_t count = 0; |
| 788 | for (struct link_map *l = new_map; l != NULL; l = l->l_next) |
| 789 | /* Skip proxy maps and already-processed maps. */ |
| 790 | count += l == l->l_real && !l->l_find_object_processed; |
| 791 | if (count == 0) |
| 792 | return true; |
| 793 | |
| 794 | struct link_map **map_array = malloc (count * sizeof (*map_array)); |
| 795 | if (map_array == NULL) |
| 796 | return false; |
| 797 | { |
| 798 | size_t i = 0; |
| 799 | for (struct link_map *l = new_map; l != NULL; l = l->l_next) |
| 800 | if (l == l->l_real && !l->l_find_object_processed) |
| 801 | map_array[i++] = l; |
| 802 | } |
| 803 | |
| 804 | _dl_find_object_link_map_sort (map_array, count); |
| 805 | bool ok = _dl_find_object_update_1 (map_array, count); |
| 806 | free (map_array); |
| 807 | return ok; |
| 808 | } |
| 809 | |
| 810 | void |
| 811 | _dl_find_object_dlclose (struct link_map *map) |
| 812 | { |
| 813 | uint64_t start_version = _dlfo_read_version_locked (); |
| 814 | uintptr_t map_start = map->l_map_start; |
| 815 | |
| 816 | |
| 817 | /* Directly patch the size information in the mapping to mark it as |
| 818 | unused. See the parallel lookup logic in _dl_find_object. Do |
| 819 | not check for previous dlclose at the same mapping address |
| 820 | because that cannot happen (there would have to be an |
| 821 | intermediate dlopen, which drops size-zero mappings). */ |
| 822 | for (struct dlfo_mappings_segment *seg |
| 823 | = _dlfo_mappings_active_segment (start_version); |
| 824 | seg != NULL && seg->size > 0; seg = seg->previous) |
| 825 | if (map_start >= seg->objects[0].map_start) |
| 826 | { |
| 827 | struct dl_find_object_internal *obj |
| 828 | = _dlfo_lookup (map_start, seg->objects, seg->size); |
| 829 | if (obj == NULL) |
| 830 | /* Ignore missing link maps because of potential shutdown |
| 831 | issues around __libc_freeres. */ |
| 832 | return; |
| 833 | |
| 834 | /* Mark as closed. This does not change the overall data |
| 835 | structure, so no TM cycle is needed. */ |
| 836 | atomic_store_relaxed (&obj->map_end, obj->map_start); |
| 837 | atomic_store_relaxed (&obj->map, NULL); |
| 838 | return; |
| 839 | } |
| 840 | } |
| 841 | |
| 842 | void |
| 843 | _dl_find_object_freeres (void) |
| 844 | { |
| 845 | for (int idx = 0; idx < 2; ++idx) |
| 846 | { |
| 847 | for (struct dlfo_mappings_segment *seg = _dlfo_loaded_mappings[idx]; |
| 848 | seg != NULL; ) |
| 849 | { |
| 850 | struct dlfo_mappings_segment *previous = seg->previous; |
| 851 | free (seg->to_free); |
| 852 | seg = previous; |
| 853 | } |
| 854 | /* Stop searching in shared objects. */ |
| 855 | _dlfo_loaded_mappings[idx] = 0; |
| 856 | } |
| 857 | } |
| 858 | |