blob: 663f51eda976d5e6a0a5dd3b33fe77f9a39da0ae [file] [log] [blame]
Dimitry Ivanov48ec2882016-08-04 11:50:36 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include "linker_soinfo.h"
30
31#include <dlfcn.h>
32#include <elf.h>
33#include <string.h>
34#include <sys/stat.h>
35#include <unistd.h>
36
Christopher Ferris7a3681e2017-04-24 17:48:32 -070037#include <async_safe/log.h>
38
Ryan Prichardbbc2cb42020-01-02 14:59:11 -080039#include "linker.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070040#include "linker_debug.h"
41#include "linker_globals.h"
Ryan Prichardf56ac992020-01-02 16:36:06 -080042#include "linker_gnu_hash.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070043#include "linker_logger.h"
Ryan Prichardf56ac992020-01-02 16:36:06 -080044#include "linker_relocate.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070045#include "linker_utils.h"
46
Ryan Prichardf56ac992020-01-02 16:36:06 -080047// Enable the slow lookup path if symbol lookups should be logged.
48static bool is_lookup_tracing_enabled() {
49 return g_ld_debug_verbosity > LINKER_VERBOSITY_TRACE && DO_TRACE_LOOKUP;
50}
51
52SymbolLookupList::SymbolLookupList(soinfo* si)
53 : sole_lib_(si->get_lookup_lib()), begin_(&sole_lib_), end_(&sole_lib_ + 1) {
54 CHECK(si != nullptr);
55 slow_path_count_ += is_lookup_tracing_enabled();
56 slow_path_count_ += sole_lib_.needs_sysv_lookup();
57}
58
59SymbolLookupList::SymbolLookupList(const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
60 slow_path_count_ += is_lookup_tracing_enabled();
61 libs_.reserve(1 + global_group.size() + local_group.size());
62
63 // Reserve a space in front for DT_SYMBOLIC lookup.
64 libs_.push_back(SymbolLookupLib {});
65
66 global_group.for_each([this](soinfo* si) {
67 libs_.push_back(si->get_lookup_lib());
68 slow_path_count_ += libs_.back().needs_sysv_lookup();
69 });
70
71 local_group.for_each([this](soinfo* si) {
72 libs_.push_back(si->get_lookup_lib());
73 slow_path_count_ += libs_.back().needs_sysv_lookup();
74 });
75
76 begin_ = &libs_[1];
77 end_ = &libs_[0] + libs_.size();
78}
79
80/* "This element's presence in a shared object library alters the dynamic linker's
81 * symbol resolution algorithm for references within the library. Instead of starting
82 * a symbol search with the executable file, the dynamic linker starts from the shared
83 * object itself. If the shared object fails to supply the referenced symbol, the
84 * dynamic linker then searches the executable file and other shared objects as usual."
85 *
86 * http://www.sco.com/developers/gabi/2012-12-31/ch5.dynamic.html
87 *
88 * Note that this is unlikely since static linker avoids generating
89 * relocations for -Bsymbolic linked dynamic executables.
90 */
91void SymbolLookupList::set_dt_symbolic_lib(soinfo* lib) {
92 CHECK(!libs_.empty());
93 slow_path_count_ -= libs_[0].needs_sysv_lookup();
94 libs_[0] = lib ? lib->get_lookup_lib() : SymbolLookupLib();
95 slow_path_count_ += libs_[0].needs_sysv_lookup();
96 begin_ = lib ? &libs_[0] : &libs_[1];
97}
98
99// Check whether a requested version matches the version on a symbol definition. There are a few
100// special cases:
101// - If the defining DSO has no version info at all, then any version matches.
102// - If no version is requested (vi==nullptr, verneed==kVersymNotNeeded), then any non-hidden
103// version matches.
104// - If the requested version is not defined by the DSO, then verneed is kVersymGlobal, and only
105// global symbol definitions match. (This special case is handled as part of the ordinary case
106// where the version must match exactly.)
107static inline bool check_symbol_version(const ElfW(Versym)* ver_table, uint32_t sym_idx,
108 const ElfW(Versym) verneed) {
109 if (ver_table == nullptr) return true;
110 const uint32_t verdef = ver_table[sym_idx];
111 return (verneed == kVersymNotNeeded) ?
112 !(verdef & kVersymHiddenBit) :
113 verneed == (verdef & ~kVersymHiddenBit);
114}
115
116template <bool IsGeneral>
117__attribute__((noinline)) static const ElfW(Sym)*
118soinfo_do_lookup_impl(const char* name, const version_info* vi,
119 soinfo** si_found_in, const SymbolLookupList& lookup_list) {
120 const auto [ hash, name_len ] = calculate_gnu_hash(name);
121 constexpr uint32_t kBloomMaskBits = sizeof(ElfW(Addr)) * 8;
122 SymbolName elf_symbol_name(name);
123
124 const SymbolLookupLib* end = lookup_list.end();
125 const SymbolLookupLib* it = lookup_list.begin();
126
127 while (true) {
128 const SymbolLookupLib* lib;
129 uint32_t sym_idx;
130
131 // Iterate over libraries until we find one whose Bloom filter matches the symbol we're
132 // searching for.
133 while (true) {
134 if (it == end) return nullptr;
135 lib = it++;
136
137 if (IsGeneral && lib->needs_sysv_lookup()) {
138 if (const ElfW(Sym)* sym = lib->si_->find_symbol_by_name(elf_symbol_name, vi)) {
139 *si_found_in = lib->si_;
140 return sym;
141 }
142 continue;
143 }
144
145 if (IsGeneral) {
146 TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
147 name, lib->si_->get_realpath(), reinterpret_cast<void*>(lib->si_->base));
148 }
149
150 const uint32_t word_num = (hash / kBloomMaskBits) & lib->gnu_maskwords_;
151 const ElfW(Addr) bloom_word = lib->gnu_bloom_filter_[word_num];
152 const uint32_t h1 = hash % kBloomMaskBits;
153 const uint32_t h2 = (hash >> lib->gnu_shift2_) % kBloomMaskBits;
154
155 if ((1 & (bloom_word >> h1) & (bloom_word >> h2)) == 1) {
156 sym_idx = lib->gnu_bucket_[hash % lib->gnu_nbucket_];
157 if (sym_idx != 0) {
158 break;
159 }
160 }
161
162 if (IsGeneral) {
163 TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
164 name, lib->si_->get_realpath(), reinterpret_cast<void*>(lib->si_->base));
165 }
166 }
167
168 // Search the library's hash table chain.
169 ElfW(Versym) verneed = kVersymNotNeeded;
170 bool calculated_verneed = false;
171
172 uint32_t chain_value = 0;
173 const ElfW(Sym)* sym = nullptr;
174
175 do {
176 sym = lib->symtab_ + sym_idx;
177 chain_value = lib->gnu_chain_[sym_idx];
178 if ((chain_value >> 1) == (hash >> 1)) {
179 if (vi != nullptr && !calculated_verneed) {
180 calculated_verneed = true;
181 verneed = find_verdef_version_index(lib->si_, vi);
182 }
183 if (check_symbol_version(lib->versym_, sym_idx, verneed) &&
184 static_cast<size_t>(sym->st_name) + name_len + 1 <= lib->strtab_size_ &&
185 memcmp(lib->strtab_ + sym->st_name, name, name_len + 1) == 0 &&
186 is_symbol_global_and_defined(lib->si_, sym)) {
187 *si_found_in = lib->si_;
188 if (IsGeneral) {
189 TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
190 name, lib->si_->get_realpath(), reinterpret_cast<void*>(sym->st_value),
191 static_cast<size_t>(sym->st_size));
192 }
193 return sym;
194 }
195 }
196 ++sym_idx;
197 } while ((chain_value & 1) == 0);
198
199 if (IsGeneral) {
200 TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
201 name, lib->si_->get_realpath(), reinterpret_cast<void*>(lib->si_->base));
202 }
203 }
204}
205
206const ElfW(Sym)* soinfo_do_lookup(const char* name, const version_info* vi,
207 soinfo** si_found_in, const SymbolLookupList& lookup_list) {
208 return lookup_list.needs_slow_path() ?
209 soinfo_do_lookup_impl<true>(name, vi, si_found_in, lookup_list) :
210 soinfo_do_lookup_impl<false>(name, vi, si_found_in, lookup_list);
211}
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700212
213soinfo::soinfo(android_namespace_t* ns, const char* realpath,
214 const struct stat* file_stat, off64_t file_offset,
215 int rtld_flags) {
216 memset(this, 0, sizeof(*this));
217
218 if (realpath != nullptr) {
219 realpath_ = realpath;
220 }
221
222 flags_ = FLAG_NEW_SOINFO;
223 version_ = SOINFO_VERSION;
224
225 if (file_stat != nullptr) {
226 this->st_dev_ = file_stat->st_dev;
227 this->st_ino_ = file_stat->st_ino;
228 this->file_offset_ = file_offset;
229 }
230
231 this->rtld_flags_ = rtld_flags;
232 this->primary_namespace_ = ns;
233}
234
235soinfo::~soinfo() {
236 g_soinfo_handles_map.erase(handle_);
237}
238
239void soinfo::set_dt_runpath(const char* path) {
240 if (!has_min_version(3)) {
241 return;
242 }
243
244 std::vector<std::string> runpaths;
245
246 split_path(path, ":", &runpaths);
247
248 std::string origin = dirname(get_realpath());
Jiyong Park57b9d1e2019-01-17 03:14:45 +0900249 // FIXME: add $PLATFORM.
250 std::vector<std::pair<std::string, std::string>> params = {
251 {"ORIGIN", origin},
252#if defined(LIB_PATH)
253 {"LIB", LIB_PATH},
254#else
255#error "LIB_PATH not defined"
256#endif
257 };
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700258 for (auto&& s : runpaths) {
Dimitry Ivanov2a6d9b22017-03-11 14:35:38 -0800259 format_string(&s, params);
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700260 }
261
262 resolve_paths(runpaths, &dt_runpath_);
263}
264
265const ElfW(Versym)* soinfo::get_versym(size_t n) const {
Ryan Prichardf56ac992020-01-02 16:36:06 -0800266 auto table = get_versym_table();
267 return table ? table + n : nullptr;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700268}
269
270ElfW(Addr) soinfo::get_verneed_ptr() const {
271 if (has_min_version(2)) {
272 return verneed_ptr_;
273 }
274
275 return 0;
276}
277
278size_t soinfo::get_verneed_cnt() const {
279 if (has_min_version(2)) {
280 return verneed_cnt_;
281 }
282
283 return 0;
284}
285
286ElfW(Addr) soinfo::get_verdef_ptr() const {
287 if (has_min_version(2)) {
288 return verdef_ptr_;
289 }
290
291 return 0;
292}
293
294size_t soinfo::get_verdef_cnt() const {
295 if (has_min_version(2)) {
296 return verdef_cnt_;
297 }
298
299 return 0;
300}
301
Ryan Prichardf56ac992020-01-02 16:36:06 -0800302SymbolLookupLib soinfo::get_lookup_lib() {
303 SymbolLookupLib result {};
304 result.si_ = this;
305
306 // For libs that only have SysV hashes, leave the gnu_bloom_filter_ field NULL to signal that
307 // the fallback code path is needed.
308 if (!is_gnu_hash()) {
309 return result;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700310 }
311
Ryan Prichardf56ac992020-01-02 16:36:06 -0800312 result.gnu_maskwords_ = gnu_maskwords_;
313 result.gnu_shift2_ = gnu_shift2_;
314 result.gnu_bloom_filter_ = gnu_bloom_filter_;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700315
Ryan Prichardf56ac992020-01-02 16:36:06 -0800316 result.strtab_ = strtab_;
317 result.strtab_size_ = strtab_size_;
318 result.symtab_ = symtab_;
319 result.versym_ = get_versym_table();
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700320
Ryan Prichardf56ac992020-01-02 16:36:06 -0800321 result.gnu_chain_ = gnu_chain_;
322 result.gnu_nbucket_ = gnu_nbucket_;
323 result.gnu_bucket_ = gnu_bucket_;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700324
Ryan Prichardf56ac992020-01-02 16:36:06 -0800325 return result;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700326}
327
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800328const ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name,
329 const version_info* vi) const {
330 return is_gnu_hash() ? gnu_lookup(symbol_name, vi) : elf_lookup(symbol_name, vi);
331}
332
333const ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name, const version_info* vi) const {
Ryan Prichardf56ac992020-01-02 16:36:06 -0800334 const uint32_t hash = symbol_name.gnu_hash();
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700335
Ryan Prichardf56ac992020-01-02 16:36:06 -0800336 constexpr uint32_t kBloomMaskBits = sizeof(ElfW(Addr)) * 8;
337 const uint32_t word_num = (hash / kBloomMaskBits) & gnu_maskwords_;
338 const ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
339 const uint32_t h1 = hash % kBloomMaskBits;
340 const uint32_t h2 = (hash >> gnu_shift2_) % kBloomMaskBits;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700341
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700342 TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
343 symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
344
345 // test against bloom filter
Ryan Prichardf56ac992020-01-02 16:36:06 -0800346 if ((1 & (bloom_word >> h1) & (bloom_word >> h2)) == 0) {
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700347 TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
348 symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
349
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800350 return nullptr;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700351 }
352
353 // bloom test says "probably yes"...
354 uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
355
356 if (n == 0) {
357 TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
358 symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
359
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800360 return nullptr;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700361 }
362
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800363 const ElfW(Versym) verneed = find_verdef_version_index(this, vi);
Ryan Prichardf56ac992020-01-02 16:36:06 -0800364 const ElfW(Versym)* versym = get_versym_table();
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700365
366 do {
367 ElfW(Sym)* s = symtab_ + n;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700368 if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
Ryan Prichardf56ac992020-01-02 16:36:06 -0800369 check_symbol_version(versym, n, verneed) &&
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700370 strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
371 is_symbol_global_and_defined(this, s)) {
372 TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
373 symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value),
374 static_cast<size_t>(s->st_size));
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800375 return symtab_ + n;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700376 }
377 } while ((gnu_chain_[n++] & 1) == 0);
378
379 TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
380 symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
381
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800382 return nullptr;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700383}
384
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800385const ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name, const version_info* vi) const {
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700386 uint32_t hash = symbol_name.elf_hash();
387
388 TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
389 symbol_name.get_name(), get_realpath(),
390 reinterpret_cast<void*>(base), hash, hash % nbucket_);
391
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800392 const ElfW(Versym) verneed = find_verdef_version_index(this, vi);
Ryan Prichardf56ac992020-01-02 16:36:06 -0800393 const ElfW(Versym)* versym = get_versym_table();
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700394
395 for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
396 ElfW(Sym)* s = symtab_ + n;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700397
Ryan Prichardf56ac992020-01-02 16:36:06 -0800398 if (check_symbol_version(versym, n, verneed) &&
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700399 strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
400 is_symbol_global_and_defined(this, s)) {
401 TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
402 symbol_name.get_name(), get_realpath(),
403 reinterpret_cast<void*>(s->st_value),
404 static_cast<size_t>(s->st_size));
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800405 return symtab_ + n;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700406 }
407 }
408
409 TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
410 symbol_name.get_name(), get_realpath(),
411 reinterpret_cast<void*>(base), hash, hash % nbucket_);
412
Ryan Prichardbbc2cb42020-01-02 14:59:11 -0800413 return nullptr;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700414}
415
416ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) {
417 return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr);
418}
419
420static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
Ryan Prichard3f05d3e2019-04-01 17:42:14 -0700421 // Skip TLS symbols. A TLS symbol's value is relative to the start of the TLS segment rather than
422 // to the start of the solib. The solib only reserves space for the initialized part of the TLS
423 // segment. (i.e. .tdata is followed by .tbss, and .tbss overlaps other sections.)
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700424 return sym->st_shndx != SHN_UNDEF &&
Ryan Prichard3f05d3e2019-04-01 17:42:14 -0700425 ELF_ST_TYPE(sym->st_info) != STT_TLS &&
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700426 soaddr >= sym->st_value &&
427 soaddr < sym->st_value + sym->st_size;
428}
429
430ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
431 ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
432
433 for (size_t i = 0; i < gnu_nbucket_; ++i) {
434 uint32_t n = gnu_bucket_[i];
435
436 if (n == 0) {
437 continue;
438 }
439
440 do {
441 ElfW(Sym)* sym = symtab_ + n;
442 if (symbol_matches_soaddr(sym, soaddr)) {
443 return sym;
444 }
445 } while ((gnu_chain_[n++] & 1) == 0);
446 }
447
448 return nullptr;
449}
450
451ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) {
452 ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
453
454 // Search the library's symbol table for any defined symbol which
455 // contains this address.
456 for (size_t i = 0; i < nchain_; ++i) {
457 ElfW(Sym)* sym = symtab_ + i;
458 if (symbol_matches_soaddr(sym, soaddr)) {
459 return sym;
460 }
461 }
462
463 return nullptr;
464}
465
466static void call_function(const char* function_name __unused,
467 linker_ctor_function_t function,
468 const char* realpath __unused) {
469 if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
470 return;
471 }
472
473 TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
474 function(g_argc, g_argv, g_envp);
475 TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
476}
477
478static void call_function(const char* function_name __unused,
479 linker_dtor_function_t function,
480 const char* realpath __unused) {
481 if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
482 return;
483 }
484
485 TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
486 function();
487 TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
488}
489
490template <typename F>
491static void call_array(const char* array_name __unused,
492 F* functions,
493 size_t count,
494 bool reverse,
495 const char* realpath) {
496 if (functions == nullptr) {
497 return;
498 }
499
500 TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath);
501
502 int begin = reverse ? (count - 1) : 0;
503 int end = reverse ? -1 : count;
504 int step = reverse ? -1 : 1;
505
506 for (int i = begin; i != end; i += step) {
507 TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]);
508 call_function("function", functions[i], realpath);
509 }
510
511 TRACE("[ Done calling %s for '%s' ]", array_name, realpath);
512}
513
514void soinfo::call_pre_init_constructors() {
515 // DT_PREINIT_ARRAY functions are called before any other constructors for executables,
516 // but ignored in a shared library.
517 call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath());
518}
519
520void soinfo::call_constructors() {
521 if (constructors_called) {
522 return;
523 }
524
525 // We set constructors_called before actually calling the constructors, otherwise it doesn't
526 // protect against recursive constructor calls. One simple example of constructor recursion
527 // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
528 // 1. The program depends on libc, so libc's constructor is called here.
529 // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
530 // 3. dlopen() calls the constructors on the newly created
531 // soinfo for libc_malloc_debug_leak.so.
532 // 4. The debug .so depends on libc, so CallConstructors is
533 // called again with the libc soinfo. If it doesn't trigger the early-
534 // out above, the libc constructor will be called again (recursively!).
535 constructors_called = true;
536
537 if (!is_main_executable() && preinit_array_ != nullptr) {
538 // The GNU dynamic linker silently ignores these, but we warn the developer.
539 PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath());
540 }
541
542 get_children().for_each([] (soinfo* si) {
543 si->call_constructors();
544 });
545
Dimitry Ivanov5c4a5802017-03-17 16:41:34 -0700546 if (!is_linker()) {
547 bionic_trace_begin((std::string("calling constructors: ") + get_realpath()).c_str());
548 }
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700549
550 // DT_INIT should be called before DT_INIT_ARRAY if both are present.
551 call_function("DT_INIT", init_func_, get_realpath());
552 call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
Dimitry Ivanov5c4a5802017-03-17 16:41:34 -0700553
554 if (!is_linker()) {
555 bionic_trace_end();
556 }
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700557}
558
559void soinfo::call_destructors() {
560 if (!constructors_called) {
561 return;
562 }
Dimitry Ivanov6705e8c2017-03-21 10:29:06 -0700563
564 ScopedTrace trace((std::string("calling destructors: ") + get_realpath()).c_str());
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700565
566 // DT_FINI_ARRAY must be parsed in reverse order.
567 call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath());
568
569 // DT_FINI should be called after DT_FINI_ARRAY if both are present.
570 call_function("DT_FINI", fini_func_, get_realpath());
571}
572
573void soinfo::add_child(soinfo* child) {
574 if (has_min_version(0)) {
575 child->parents_.push_back(this);
576 this->children_.push_back(child);
577 }
578}
579
580void soinfo::remove_all_links() {
581 if (!has_min_version(0)) {
582 return;
583 }
584
585 // 1. Untie connected soinfos from 'this'.
586 children_.for_each([&] (soinfo* child) {
587 child->parents_.remove_if([&] (const soinfo* parent) {
588 return parent == this;
589 });
590 });
591
592 parents_.for_each([&] (soinfo* parent) {
593 parent->children_.remove_if([&] (const soinfo* child) {
594 return child == this;
595 });
596 });
597
598 // 2. Remove from the primary namespace
599 primary_namespace_->remove_soinfo(this);
600 primary_namespace_ = nullptr;
601
602 // 3. Remove from secondary namespaces
603 secondary_namespaces_.for_each([&](android_namespace_t* ns) {
604 ns->remove_soinfo(this);
605 });
606
607
608 // 4. Once everything untied - clear local lists.
609 parents_.clear();
610 children_.clear();
611 secondary_namespaces_.clear();
612}
613
614dev_t soinfo::get_st_dev() const {
615 if (has_min_version(0)) {
616 return st_dev_;
617 }
618
619 return 0;
620};
621
622ino_t soinfo::get_st_ino() const {
623 if (has_min_version(0)) {
624 return st_ino_;
625 }
626
627 return 0;
628}
629
630off64_t soinfo::get_file_offset() const {
631 if (has_min_version(1)) {
632 return file_offset_;
633 }
634
635 return 0;
636}
637
638uint32_t soinfo::get_rtld_flags() const {
639 if (has_min_version(1)) {
640 return rtld_flags_;
641 }
642
643 return 0;
644}
645
646uint32_t soinfo::get_dt_flags_1() const {
647 if (has_min_version(1)) {
648 return dt_flags_1_;
649 }
650
651 return 0;
652}
653
654void soinfo::set_dt_flags_1(uint32_t dt_flags_1) {
655 if (has_min_version(1)) {
656 if ((dt_flags_1 & DF_1_GLOBAL) != 0) {
657 rtld_flags_ |= RTLD_GLOBAL;
658 }
659
660 if ((dt_flags_1 & DF_1_NODELETE) != 0) {
661 rtld_flags_ |= RTLD_NODELETE;
662 }
663
664 dt_flags_1_ = dt_flags_1;
665 }
666}
667
668void soinfo::set_nodelete() {
669 rtld_flags_ |= RTLD_NODELETE;
670}
671
672const char* soinfo::get_realpath() const {
673#if defined(__work_around_b_24465209__)
674 if (has_min_version(2)) {
675 return realpath_.c_str();
676 } else {
677 return old_name_;
678 }
679#else
680 return realpath_.c_str();
681#endif
682}
683
684void soinfo::set_soname(const char* soname) {
685#if defined(__work_around_b_24465209__)
686 if (has_min_version(2)) {
687 soname_ = soname;
688 }
689 strlcpy(old_name_, soname_, sizeof(old_name_));
690#else
691 soname_ = soname;
692#endif
693}
694
695const char* soinfo::get_soname() const {
696#if defined(__work_around_b_24465209__)
697 if (has_min_version(2)) {
698 return soname_;
699 } else {
700 return old_name_;
701 }
702#else
703 return soname_;
704#endif
705}
706
707// This is a return on get_children()/get_parents() if
708// 'this->flags' does not have FLAG_NEW_SOINFO set.
709static soinfo_list_t g_empty_list;
710
711soinfo_list_t& soinfo::get_children() {
712 if (has_min_version(0)) {
713 return children_;
714 }
715
716 return g_empty_list;
717}
718
719const soinfo_list_t& soinfo::get_children() const {
720 if (has_min_version(0)) {
721 return children_;
722 }
723
724 return g_empty_list;
725}
726
727soinfo_list_t& soinfo::get_parents() {
728 if (has_min_version(0)) {
729 return parents_;
730 }
731
732 return g_empty_list;
733}
734
735static std::vector<std::string> g_empty_runpath;
736
737const std::vector<std::string>& soinfo::get_dt_runpath() const {
738 if (has_min_version(3)) {
739 return dt_runpath_;
740 }
741
742 return g_empty_runpath;
743}
744
745android_namespace_t* soinfo::get_primary_namespace() {
746 if (has_min_version(3)) {
747 return primary_namespace_;
748 }
749
750 return &g_default_namespace;
751}
752
753void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) {
754 CHECK(has_min_version(3));
755 secondary_namespaces_.push_back(secondary_ns);
756}
757
Dimitry Ivanov7a34b9d2017-02-03 14:07:34 -0800758android_namespace_list_t& soinfo::get_secondary_namespaces() {
759 CHECK(has_min_version(3));
760 return secondary_namespaces_;
761}
762
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700763const char* soinfo::get_string(ElfW(Word) index) const {
764 if (has_min_version(1) && (index >= strtab_size_)) {
Christopher Ferris7a3681e2017-04-24 17:48:32 -0700765 async_safe_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d",
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700766 get_realpath(), strtab_size_, index);
767 }
768
769 return strtab_ + index;
770}
771
772bool soinfo::is_gnu_hash() const {
773 return (flags_ & FLAG_GNU_HASH) != 0;
774}
775
776bool soinfo::can_unload() const {
dimitry06016f22018-01-05 11:39:28 +0100777 return !is_linked() ||
778 (
dimitry55547db2018-05-25 14:17:37 +0200779 (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0
dimitry06016f22018-01-05 11:39:28 +0100780 );
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700781}
782
783bool soinfo::is_linked() const {
784 return (flags_ & FLAG_LINKED) != 0;
785}
786
dimitry965d06d2017-11-28 16:03:07 +0100787bool soinfo::is_image_linked() const {
788 return (flags_ & FLAG_IMAGE_LINKED) != 0;
789}
790
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700791bool soinfo::is_main_executable() const {
792 return (flags_ & FLAG_EXE) != 0;
793}
794
795bool soinfo::is_linker() const {
796 return (flags_ & FLAG_LINKER) != 0;
797}
798
799void soinfo::set_linked() {
800 flags_ |= FLAG_LINKED;
801}
802
dimitry965d06d2017-11-28 16:03:07 +0100803void soinfo::set_image_linked() {
804 flags_ |= FLAG_IMAGE_LINKED;
805}
806
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700807void soinfo::set_linker_flag() {
808 flags_ |= FLAG_LINKER;
809}
810
811void soinfo::set_main_executable() {
812 flags_ |= FLAG_EXE;
813}
814
dimitry965d06d2017-11-28 16:03:07 +0100815size_t soinfo::increment_ref_count() {
816 return ++local_group_root_->ref_count_;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700817}
818
819size_t soinfo::decrement_ref_count() {
820 return --local_group_root_->ref_count_;
821}
822
dimitry06016f22018-01-05 11:39:28 +0100823size_t soinfo::get_ref_count() const {
824 return local_group_root_->ref_count_;
825}
826
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700827soinfo* soinfo::get_local_group_root() const {
828 return local_group_root_;
829}
830
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700831void soinfo::set_mapped_by_caller(bool mapped_by_caller) {
832 if (mapped_by_caller) {
833 flags_ |= FLAG_MAPPED_BY_CALLER;
834 } else {
835 flags_ &= ~FLAG_MAPPED_BY_CALLER;
836 }
837}
838
839bool soinfo::is_mapped_by_caller() const {
840 return (flags_ & FLAG_MAPPED_BY_CALLER) != 0;
841}
842
843// This function returns api-level at the time of
844// dlopen/load. Note that libraries opened by system
845// will always have 'current' api level.
Elliott Hughesff1428a2018-11-12 16:01:37 -0800846int soinfo::get_target_sdk_version() const {
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700847 if (!has_min_version(2)) {
848 return __ANDROID_API__;
849 }
850
851 return local_group_root_->target_sdk_version_;
852}
853
854uintptr_t soinfo::get_handle() const {
855 CHECK(has_min_version(3));
856 CHECK(handle_ != 0);
857 return handle_;
858}
859
860void* soinfo::to_handle() {
Elliott Hughes5bc78c82016-11-16 11:35:43 -0800861 if (get_application_target_sdk_version() < __ANDROID_API_N__ || !has_min_version(3)) {
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700862 return this;
863 }
864
865 return reinterpret_cast<void*>(get_handle());
866}
867
868void soinfo::generate_handle() {
869 CHECK(has_min_version(3));
870 CHECK(handle_ == 0); // Make sure this is the first call
871
872 // Make sure the handle is unique and does not collide
873 // with special values which are RTLD_DEFAULT and RTLD_NEXT.
874 do {
Tom Cherry66bc4282018-11-08 13:40:52 -0800875 if (!is_first_stage_init()) {
Jiyong Park31cd08f2018-06-01 19:18:56 +0900876 arc4random_buf(&handle_, sizeof(handle_));
877 } else {
878 // arc4random* is not available in init because /dev/urandom hasn't yet been
879 // created. So, when running with init, use the monotonically increasing
880 // numbers as handles
881 handle_ += 2;
882 }
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700883 // the least significant bit for the handle is always 1
884 // making it easy to test the type of handle passed to
885 // dl* functions.
886 handle_ = handle_ | 1;
887 } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) ||
888 handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) ||
889 g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end());
890
891 g_soinfo_handles_map[handle_] = this;
892}
893
894// TODO(dimitry): Move SymbolName methods to a separate file.
895
896uint32_t calculate_elf_hash(const char* name) {
897 const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name);
898 uint32_t h = 0, g;
899
900 while (*name_bytes) {
901 h = (h << 4) + *name_bytes++;
902 g = h & 0xf0000000;
903 h ^= g;
904 h ^= g >> 24;
905 }
906
907 return h;
908}
909
910uint32_t SymbolName::elf_hash() {
911 if (!has_elf_hash_) {
912 elf_hash_ = calculate_elf_hash(name_);
913 has_elf_hash_ = true;
914 }
915
916 return elf_hash_;
917}
918
919uint32_t SymbolName::gnu_hash() {
920 if (!has_gnu_hash_) {
Ryan Prichardf56ac992020-01-02 16:36:06 -0800921 gnu_hash_ = calculate_gnu_hash(name_).first;
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700922 has_gnu_hash_ = true;
923 }
924
925 return gnu_hash_;
926}