dlfcn_test.cpp revision ef25592f14d23ce6294ea103e9edf894779d141d
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <gtest/gtest.h> 18 19#include <dlfcn.h> 20#include <libgen.h> 21#include <limits.h> 22#include <stdio.h> 23#include <stdint.h> 24 25#include "private/ScopeGuard.h" 26 27#include <string> 28 29#include "utils.h" 30 31#define ASSERT_SUBSTR(needle, haystack) \ 32 ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack) 33 34 35static bool g_called = false; 36extern "C" void DlSymTestFunction() { 37 g_called = true; 38} 39 40static int g_ctor_function_called = 0; 41 42extern "C" void ctor_function() __attribute__ ((constructor)); 43 44extern "C" void ctor_function() { 45 g_ctor_function_called = 17; 46} 47 48TEST(dlfcn, ctor_function_call) { 49 ASSERT_EQ(17, g_ctor_function_called); 50} 51 52TEST(dlfcn, dlsym_in_executable) { 53 dlerror(); // Clear any pending errors. 54 void* self = dlopen(NULL, RTLD_NOW); 55 ASSERT_TRUE(self != NULL); 56 ASSERT_TRUE(dlerror() == NULL); 57 58 void* sym = dlsym(self, "DlSymTestFunction"); 59 ASSERT_TRUE(sym != NULL); 60 61 void (*function)() = reinterpret_cast<void(*)()>(sym); 62 63 g_called = false; 64 function(); 65 ASSERT_TRUE(g_called); 66 67 ASSERT_EQ(0, dlclose(self)); 68} 69 70TEST(dlfcn, dlsym_from_sofile) { 71 void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_LAZY | RTLD_LOCAL); 72 ASSERT_TRUE(handle != nullptr) << dlerror(); 73 74 // check that we cant find '_test_dlsym_symbol' via dlsym(RTLD_DEFAULT) 75 void* symbol = dlsym(RTLD_DEFAULT, "test_dlsym_symbol"); 76 ASSERT_TRUE(symbol == nullptr); 77 ASSERT_SUBSTR("undefined symbol: test_dlsym_symbol", dlerror()); 78 79 typedef int* (*fn_t)(); 80 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_DEFAULT")); 81 82 ASSERT_TRUE(fn != nullptr) << dlerror(); 83 84 int* ptr = fn(); 85 ASSERT_TRUE(ptr != nullptr) << dlerror(); 86 ASSERT_EQ(42, *ptr); 87 88 dlclose(handle); 89} 90 91TEST(dlfcn, dlsym_with_dependencies) { 92 void* handle = dlopen("libtest_with_dependency.so", RTLD_NOW); 93 ASSERT_TRUE(handle != NULL); 94 dlerror(); 95 // This symbol is in DT_NEEDED library. 96 void* sym = dlsym(handle, "getRandomNumber"); 97 ASSERT_TRUE(sym != NULL); 98 int (*fn)(void); 99 fn = reinterpret_cast<int (*)(void)>(sym); 100 EXPECT_EQ(4, fn()); 101 dlclose(handle); 102} 103 104TEST(dlfcn, dlopen_noload) { 105 void* handle = dlopen("libtest_simple.so", RTLD_NOW | RTLD_NOLOAD); 106 ASSERT_TRUE(handle == NULL); 107 handle = dlopen("libtest_simple.so", RTLD_NOW); 108 void* handle2 = dlopen("libtest_simple.so", RTLD_NOW | RTLD_NOLOAD); 109 ASSERT_TRUE(handle != NULL); 110 ASSERT_TRUE(handle2 != NULL); 111 ASSERT_TRUE(handle == handle2); 112 ASSERT_EQ(0, dlclose(handle)); 113 ASSERT_EQ(0, dlclose(handle2)); 114} 115 116TEST(dlfcn, dlopen_by_soname) { 117 static const char* soname = "libdlext_test_soname.so"; 118 static const char* filename = "libdlext_test_different_soname.so"; 119 // 1. Make sure there is no library with soname in default search path 120 void* handle = dlopen(soname, RTLD_NOW); 121 ASSERT_TRUE(handle == nullptr); 122 123 // 2. Load a library using filename 124 handle = dlopen(filename, RTLD_NOW); 125 ASSERT_TRUE(handle != nullptr) << dlerror(); 126 127 // 3. Find library by soname 128 void* handle_soname = dlopen(soname, RTLD_NOW | RTLD_NOLOAD); 129 ASSERT_TRUE(handle_soname != nullptr) << dlerror(); 130 ASSERT_EQ(handle, handle_soname); 131 132 // 4. RTLD_NOLOAD should still work with filename 133 void* handle_filename = dlopen(filename, RTLD_NOW | RTLD_NOLOAD); 134 ASSERT_TRUE(handle_filename != nullptr) << dlerror(); 135 ASSERT_EQ(handle, handle_filename); 136 137 dlclose(handle_filename); 138 dlclose(handle_soname); 139 dlclose(handle); 140} 141 142// ifuncs are only supported on intel and arm64 for now 143#if defined (__aarch64__) || defined(__i386__) || defined(__x86_64__) 144TEST(dlfcn, ifunc) { 145 typedef const char* (*fn_ptr)(); 146 147 // ifunc's choice depends on whether IFUNC_CHOICE has a value 148 // first check the set case 149 setenv("IFUNC_CHOICE", "set", 1); 150 void* handle = dlopen("libtest_ifunc.so", RTLD_NOW); 151 ASSERT_TRUE(handle != NULL); 152 fn_ptr foo_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo")); 153 fn_ptr foo_library_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo_library")); 154 ASSERT_TRUE(foo_ptr != NULL); 155 ASSERT_TRUE(foo_library_ptr != NULL); 156 ASSERT_EQ(strncmp("set", foo_ptr(), 3), 0); 157 ASSERT_EQ(strncmp("set", foo_library_ptr(), 3), 0); 158 dlclose(handle); 159 160 // then check the unset case 161 unsetenv("IFUNC_CHOICE"); 162 handle = dlopen("libtest_ifunc.so", RTLD_NOW); 163 ASSERT_TRUE(handle != NULL); 164 foo_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo")); 165 foo_library_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo_library")); 166 ASSERT_TRUE(foo_ptr != NULL); 167 ASSERT_TRUE(foo_library_ptr != NULL); 168 ASSERT_EQ(strncmp("unset", foo_ptr(), 5), 0); 169 ASSERT_EQ(strncmp("unset", foo_library_ptr(), 3), 0); 170 dlclose(handle); 171} 172 173TEST(dlfcn, ifunc_ctor_call) { 174 typedef const char* (*fn_ptr)(); 175 176 void* handle = dlopen("libtest_ifunc.so", RTLD_NOW); 177 ASSERT_TRUE(handle != nullptr) << dlerror(); 178 fn_ptr is_ctor_called = reinterpret_cast<fn_ptr>(dlsym(handle, "is_ctor_called_irelative")); 179 ASSERT_TRUE(is_ctor_called != nullptr) << dlerror(); 180 ASSERT_STREQ("false", is_ctor_called()); 181 182 is_ctor_called = reinterpret_cast<fn_ptr>(dlsym(handle, "is_ctor_called_jump_slot")); 183 ASSERT_TRUE(is_ctor_called != nullptr) << dlerror(); 184 ASSERT_STREQ("true", is_ctor_called()); 185 dlclose(handle); 186} 187#endif 188 189TEST(dlfcn, dlopen_check_relocation_dt_needed_order) { 190 // This is the structure of the test library and 191 // its dt_needed libraries 192 // libtest_relo_check_dt_needed_order.so 193 // | 194 // +-> libtest_relo_check_dt_needed_order_1.so 195 // | 196 // +-> libtest_relo_check_dt_needed_order_2.so 197 // 198 // The root library references relo_test_get_answer_lib - which is defined 199 // in both dt_needed libraries, the correct relocation should 200 // use the function defined in libtest_relo_check_dt_needed_order_1.so 201 void* handle = nullptr; 202 auto guard = make_scope_guard([&]() { 203 dlclose(handle); 204 }); 205 206 handle = dlopen("libtest_relo_check_dt_needed_order.so", RTLD_NOW); 207 ASSERT_TRUE(handle != nullptr) << dlerror(); 208 209 typedef int (*fn_t) (void); 210 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "relo_test_get_answer")); 211 ASSERT_TRUE(fn != nullptr) << dlerror(); 212 ASSERT_EQ(1, fn()); 213} 214 215TEST(dlfcn, dlopen_check_order_dlsym) { 216 // Here is how the test library and its dt_needed 217 // libraries are arranged 218 // 219 // libtest_check_order_children.so 220 // | 221 // +-> ..._1_left.so 222 // | | 223 // | +-> ..._a.so 224 // | | 225 // | +-> ...r_b.so 226 // | 227 // +-> ..._2_right.so 228 // | | 229 // | +-> ..._d.so 230 // | | 231 // | +-> ..._b.so 232 // | 233 // +-> ..._3_c.so 234 // 235 // load order should be (1, 2, 3, a, b, d) 236 // 237 // get_answer() is defined in (2, 3, a, b, c) 238 // get_answer2() is defined in (b, d) 239 void* sym = dlsym(RTLD_DEFAULT, "check_order_dlsym_get_answer"); 240 ASSERT_TRUE(sym == nullptr); 241 void* handle = dlopen("libtest_check_order_dlsym.so", RTLD_NOW | RTLD_GLOBAL); 242 ASSERT_TRUE(handle != nullptr) << dlerror(); 243 typedef int (*fn_t) (void); 244 fn_t fn, fn2; 245 fn = reinterpret_cast<fn_t>(dlsym(RTLD_DEFAULT, "check_order_dlsym_get_answer")); 246 ASSERT_TRUE(fn != NULL) << dlerror(); 247 fn2 = reinterpret_cast<fn_t>(dlsym(RTLD_DEFAULT, "check_order_dlsym_get_answer2")); 248 ASSERT_TRUE(fn2 != NULL) << dlerror(); 249 250 ASSERT_EQ(42, fn()); 251 ASSERT_EQ(43, fn2()); 252 dlclose(handle); 253} 254 255TEST(dlfcn, dlopen_check_order_reloc_siblings) { 256 // This is how this one works: 257 // we lookup and call get_answer which is defined in '_2.so' 258 // and in turn calls external get_answer_impl() defined in _1.so and in '_[a-f].so' 259 // the correct _impl() is implemented by '_a.so'; 260 // 261 // Note that this is test for RTLD_LOCAL (TODO: test for GLOBAL?) 262 // 263 // Here is the picture: 264 // 265 // libtest_check_order_reloc_siblings.so 266 // | 267 // +-> ..._1.so <- empty 268 // | | 269 // | +-> ..._a.so <- exports correct answer_impl() 270 // | | 271 // | +-> ..._b.so <- every other letter exporting incorrect one. 272 // | 273 // +-> ..._2.so <- empty 274 // | | 275 // | +-> ..._c.so 276 // | | 277 // | +-> ..._d.so 278 // | 279 // +-> ..._3.so <- empty 280 // | 281 // +-> ..._e.so 282 // | 283 // +-> ..._f.so <- exports get_answer() that calls get_anser_impl(); 284 // implements incorrect get_answer_impl() 285 286 void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); 287 ASSERT_TRUE(handle == nullptr); 288#ifdef __BIONIC__ 289 // TODO: glibc returns nullptr on dlerror() here. Is it bug? 290 ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_siblings.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); 291#endif 292 293 handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); 294 ASSERT_TRUE(handle != nullptr) << dlerror(); 295 296 typedef int (*fn_t) (void); 297 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "check_order_reloc_get_answer")); 298 ASSERT_TRUE(fn != nullptr) << dlerror(); 299 ASSERT_EQ(42, fn()); 300 301 ASSERT_EQ(0, dlclose(handle)); 302} 303 304TEST(dlfcn, dlopen_check_order_reloc_siblings_with_preload) { 305 // This test uses the same library as dlopen_check_order_reloc_siblings. 306 // Unlike dlopen_check_order_reloc_siblings it preloads 307 // libtest_check_order_reloc_siblings_1.so (first dependency) prior to 308 // dlopen(libtest_check_order_reloc_siblings.so) 309 310 void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); 311 ASSERT_TRUE(handle == nullptr); 312 handle = dlopen("libtest_check_order_reloc_siblings_1.so", RTLD_NOW | RTLD_NOLOAD); 313 ASSERT_TRUE(handle == nullptr); 314 315 void* handle_for_1 = dlopen("libtest_check_order_reloc_siblings_1.so", RTLD_NOW | RTLD_LOCAL); 316 ASSERT_TRUE(handle_for_1 != nullptr) << dlerror(); 317 318 handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); 319 ASSERT_TRUE(handle != nullptr) << dlerror(); 320 321 ASSERT_EQ(0, dlclose(handle_for_1)); 322 323 typedef int (*fn_t) (void); 324 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "check_order_reloc_get_answer")); 325 ASSERT_TRUE(fn != nullptr) << dlerror(); 326 ASSERT_EQ(42, fn()); 327 328 ASSERT_EQ(0, dlclose(handle)); 329} 330 331TEST(dlfcn, dlopen_check_order_reloc_grandchild) { 332 // This is how this one works: 333 // we lookup and call grandchild_get_answer which is defined in '_2.so' 334 // and in turn calls external get_answer_impl() defined in '_c_1.so and _c_2.so' 335 // the correct _impl() is implemented by '_c_1.so'; 336 // 337 // Here is the picture of subtree: 338 // 339 // libtest_check_order_reloc_siblings.so 340 // | 341 // +-> ..._2.so <- grandchild_get_answer() 342 // | 343 // +-> ..._c.so <- empty 344 // | | 345 // | +-> _c_1.so <- exports correct answer_impl() 346 // | | 347 // | +-> _c_2.so <- exports incorrect answer_impl() 348 // | 349 // +-> ..._d.so <- empty 350 351 void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); 352 ASSERT_TRUE(handle == nullptr); 353#ifdef __BIONIC__ 354 // TODO: glibc returns nullptr on dlerror() here. Is it bug? 355 ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_siblings.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); 356#endif 357 358 handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); 359 ASSERT_TRUE(handle != nullptr) << dlerror(); 360 361 typedef int (*fn_t) (void); 362 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "check_order_reloc_grandchild_get_answer")); 363 ASSERT_TRUE(fn != nullptr) << dlerror(); 364 ASSERT_EQ(42, fn()); 365 366 ASSERT_EQ(0, dlclose(handle)); 367} 368 369TEST(dlfcn, dlopen_check_order_reloc_nephew) { 370 // This is how this one works: 371 // we lookup and call nephew_get_answer which is defined in '_2.so' 372 // and in turn calls external get_answer_impl() defined in '_[a-f].so' 373 // the correct _impl() is implemented by '_a.so'; 374 // 375 // Here is the picture: 376 // 377 // libtest_check_order_reloc_siblings.so 378 // | 379 // +-> ..._1.so <- empty 380 // | | 381 // | +-> ..._a.so <- exports correct answer_impl() 382 // | | 383 // | +-> ..._b.so <- every other letter exporting incorrect one. 384 // | 385 // +-> ..._2.so <- empty 386 // | | 387 // | +-> ..._c.so 388 // | | 389 // | +-> ..._d.so 390 // | 391 // +-> ..._3.so <- nephew_get_answer() that calls get_answer_impl(); 392 // | 393 // +-> ..._e.so 394 // | 395 // +-> ..._f.so 396 397 void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); 398 ASSERT_TRUE(handle == nullptr); 399#ifdef __BIONIC__ 400 // TODO: glibc returns nullptr on dlerror() here. Is it bug? 401 ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_siblings.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); 402#endif 403 404 handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); 405 ASSERT_TRUE(handle != nullptr) << dlerror(); 406 407 typedef int (*fn_t) (void); 408 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "check_order_reloc_nephew_get_answer")); 409 ASSERT_TRUE(fn != nullptr) << dlerror(); 410 ASSERT_EQ(42, fn()); 411 412 ASSERT_EQ(0, dlclose(handle)); 413} 414 415TEST(dlfcn, check_unload_after_reloc) { 416 // This is how this one works: 417 // libtest_two_parents_parent1 <- answer_impl() used by libtest_two_parents_child 418 // | 419 // +-> libtest_two_parents_child 420 // 421 // libtest_two_parents_parent2 <- answer_impl() not used by libtest_two_parents_child 422 // | 423 // +-> libtest_two_parents_child 424 // 425 // Test dlopens parent1 which loads and relocates libtest_two_parents_child.so 426 // as a second step it dlopens parent2 and dlcloses parent1... 427 428 void* handle = dlopen("libtest_two_parents_parent1.so", RTLD_NOW | RTLD_LOCAL); 429 ASSERT_TRUE(handle != nullptr) << dlerror(); 430 431 void* handle2 = dlopen("libtest_two_parents_parent2.so", RTLD_NOW | RTLD_LOCAL); 432 ASSERT_TRUE(handle2 != nullptr) << dlerror(); 433 434 typedef int (*fn_t) (void); 435 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle2, "check_order_reloc_get_answer")); 436 ASSERT_TRUE(fn != nullptr) << dlerror(); 437 ASSERT_EQ(42, fn()); 438 439 ASSERT_EQ(0, dlclose(handle)); 440 441 handle = dlopen("libtest_two_parents_parent1.so", RTLD_NOW | RTLD_LOCAL | RTLD_NOLOAD); 442 ASSERT_TRUE(handle != nullptr); 443 ASSERT_EQ(0, dlclose(handle)); 444 445 fn = reinterpret_cast<fn_t>(dlsym(handle2, "check_order_reloc_get_answer")); 446 ASSERT_TRUE(fn != nullptr) << dlerror(); 447 ASSERT_EQ(42, fn()); 448 449 ASSERT_EQ(0, dlclose(handle2)); 450 451 handle = dlopen("libtest_two_parents_parent1.so", RTLD_NOW | RTLD_LOCAL | RTLD_NOLOAD); 452 ASSERT_TRUE(handle == nullptr); 453} 454 455extern "C" int check_order_reloc_root_get_answer_impl() { 456 return 42; 457} 458 459TEST(dlfcn, dlopen_check_order_reloc_main_executable) { 460 // This is how this one works: 461 // we lookup and call get_answer3 which is defined in 'root.so' 462 // and in turn calls external root_get_answer_impl() defined in _2.so and 463 // above the correct _impl() is one in the executable. 464 // 465 // libtest_check_order_reloc_root.so 466 // | 467 // +-> ..._1.so <- empty 468 // | 469 // +-> ..._2.so <- gives incorrect answer for answer_main_impl() 470 // 471 472 void* handle = dlopen("libtest_check_order_reloc_root.so", RTLD_NOW | RTLD_NOLOAD); 473 ASSERT_TRUE(handle == nullptr); 474#ifdef __BIONIC__ 475 // TODO: glibc returns nullptr on dlerror() here. Is it bug? 476 ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_root.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); 477#endif 478 479 handle = dlopen("libtest_check_order_reloc_root.so", RTLD_NOW | RTLD_LOCAL); 480 ASSERT_TRUE(handle != nullptr) << dlerror(); 481 482 typedef int (*fn_t) (void); 483 fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "check_order_reloc_root_get_answer")); 484 ASSERT_TRUE(fn != nullptr) << dlerror(); 485 ASSERT_EQ(42, fn()); 486 487 ASSERT_EQ(0, dlclose(handle)); 488} 489 490TEST(dlfcn, dlopen_check_rtld_local) { 491 void* sym = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); 492 ASSERT_TRUE(sym == nullptr); 493 494 // implicit RTLD_LOCAL 495 void* handle = dlopen("libtest_simple.so", RTLD_NOW); 496 sym = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); 497 ASSERT_TRUE(sym == nullptr); 498 ASSERT_SUBSTR("undefined symbol: dlopen_testlib_simple_func", dlerror()); 499 sym = dlsym(handle, "dlopen_testlib_simple_func"); 500 ASSERT_TRUE(sym != nullptr); 501 ASSERT_TRUE(reinterpret_cast<bool (*)(void)>(sym)()); 502 dlclose(handle); 503 504 // explicit RTLD_LOCAL 505 handle = dlopen("libtest_simple.so", RTLD_NOW | RTLD_LOCAL); 506 sym = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); 507 ASSERT_TRUE(sym == nullptr); 508 ASSERT_SUBSTR("undefined symbol: dlopen_testlib_simple_func", dlerror()); 509 sym = dlsym(handle, "dlopen_testlib_simple_func"); 510 ASSERT_TRUE(sym != nullptr); 511 ASSERT_TRUE(reinterpret_cast<bool (*)(void)>(sym)()); 512 dlclose(handle); 513} 514 515TEST(dlfcn, dlopen_check_rtld_global) { 516 void* sym = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); 517 ASSERT_TRUE(sym == nullptr); 518 519 void* handle = dlopen("libtest_simple.so", RTLD_NOW | RTLD_GLOBAL); 520 ASSERT_TRUE(handle != nullptr) << dlerror(); 521 sym = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); 522 ASSERT_TRUE(sym != nullptr) << dlerror(); 523 ASSERT_TRUE(reinterpret_cast<bool (*)(void)>(sym)()); 524 dlclose(handle); 525 526 // RTLD_GLOBAL implies RTLD_NODELETE, let's check that 527 void* sym_after_dlclose = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); 528 ASSERT_EQ(sym, sym_after_dlclose); 529} 530 531// libtest_with_dependency_loop.so -> libtest_with_dependency_loop_a.so -> 532// libtest_with_dependency_loop_b.so -> libtest_with_dependency_loop_c.so -> 533// libtest_with_dependency_loop_a.so 534TEST(dlfcn, dlopen_check_loop) { 535 void* handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW); 536 ASSERT_TRUE(handle != nullptr) << dlerror(); 537 void* f = dlsym(handle, "dlopen_test_loopy_function"); 538 ASSERT_TRUE(f != nullptr) << dlerror(); 539 EXPECT_TRUE(reinterpret_cast<bool (*)(void)>(f)()); 540 ASSERT_EQ(0, dlclose(handle)); 541 542 // dlopen second time to make sure that the library was unloaded correctly 543 handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW | RTLD_NOLOAD); 544 ASSERT_TRUE(handle == nullptr); 545#ifdef __BIONIC__ 546 // TODO: glibc returns nullptr on dlerror() here. Is it bug? 547 ASSERT_STREQ("dlopen failed: library \"libtest_with_dependency_loop.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); 548#endif 549 550 handle = dlopen("libtest_with_dependency_a.so", RTLD_NOW | RTLD_NOLOAD); 551 ASSERT_TRUE(handle == nullptr); 552} 553 554TEST(dlfcn, dlopen_nodelete) { 555 static bool is_unloaded = false; 556 557 void* handle = dlopen("libtest_nodelete_1.so", RTLD_NOW | RTLD_NODELETE); 558 ASSERT_TRUE(handle != nullptr) << dlerror(); 559 void (*set_unload_flag_ptr)(bool*); 560 set_unload_flag_ptr = reinterpret_cast<void (*)(bool*)>(dlsym(handle, "dlopen_nodelete_1_set_unload_flag_ptr")); 561 ASSERT_TRUE(set_unload_flag_ptr != nullptr) << dlerror(); 562 set_unload_flag_ptr(&is_unloaded); 563 564 uint32_t* taxicab_number = reinterpret_cast<uint32_t*>(dlsym(handle, "dlopen_nodelete_1_taxicab_number")); 565 ASSERT_TRUE(taxicab_number != nullptr) << dlerror(); 566 ASSERT_EQ(1729U, *taxicab_number); 567 *taxicab_number = 2; 568 569 dlclose(handle); 570 ASSERT_TRUE(!is_unloaded); 571 572 uint32_t* taxicab_number_after_dlclose = reinterpret_cast<uint32_t*>(dlsym(handle, "dlopen_nodelete_1_taxicab_number")); 573 ASSERT_EQ(taxicab_number_after_dlclose, taxicab_number); 574 ASSERT_EQ(2U, *taxicab_number_after_dlclose); 575 576 577 handle = dlopen("libtest_nodelete_1.so", RTLD_NOW); 578 uint32_t* taxicab_number2 = reinterpret_cast<uint32_t*>(dlsym(handle, "dlopen_nodelete_1_taxicab_number")); 579 ASSERT_EQ(taxicab_number2, taxicab_number); 580 581 ASSERT_EQ(2U, *taxicab_number2); 582 583 dlclose(handle); 584 ASSERT_TRUE(!is_unloaded); 585} 586 587TEST(dlfcn, dlopen_nodelete_on_second_dlopen) { 588 static bool is_unloaded = false; 589 590 void* handle = dlopen("libtest_nodelete_2.so", RTLD_NOW); 591 ASSERT_TRUE(handle != nullptr) << dlerror(); 592 void (*set_unload_flag_ptr)(bool*); 593 set_unload_flag_ptr = reinterpret_cast<void (*)(bool*)>(dlsym(handle, "dlopen_nodelete_2_set_unload_flag_ptr")); 594 ASSERT_TRUE(set_unload_flag_ptr != nullptr) << dlerror(); 595 set_unload_flag_ptr(&is_unloaded); 596 597 uint32_t* taxicab_number = reinterpret_cast<uint32_t*>(dlsym(handle, "dlopen_nodelete_2_taxicab_number")); 598 ASSERT_TRUE(taxicab_number != nullptr) << dlerror(); 599 600 ASSERT_EQ(1729U, *taxicab_number); 601 *taxicab_number = 2; 602 603 // This RTLD_NODELETE should be ignored 604 void* handle1 = dlopen("libtest_nodelete_2.so", RTLD_NOW | RTLD_NODELETE); 605 ASSERT_TRUE(handle1 != nullptr) << dlerror(); 606 ASSERT_EQ(handle, handle1); 607 608 dlclose(handle1); 609 dlclose(handle); 610 611 ASSERT_TRUE(is_unloaded); 612} 613 614TEST(dlfcn, dlopen_nodelete_dt_flags_1) { 615 static bool is_unloaded = false; 616 617 void* handle = dlopen("libtest_nodelete_dt_flags_1.so", RTLD_NOW); 618 ASSERT_TRUE(handle != nullptr) << dlerror(); 619 void (*set_unload_flag_ptr)(bool*); 620 set_unload_flag_ptr = reinterpret_cast<void (*)(bool*)>(dlsym(handle, "dlopen_nodelete_dt_flags_1_set_unload_flag_ptr")); 621 ASSERT_TRUE(set_unload_flag_ptr != nullptr) << dlerror(); 622 set_unload_flag_ptr(&is_unloaded); 623 624 dlclose(handle); 625 ASSERT_TRUE(!is_unloaded); 626} 627 628TEST(dlfcn, dlsym_df_1_global) { 629#if !defined(__arm__) && !defined(__aarch64__) 630 void* handle = dlopen("libtest_dlsym_df_1_global.so", RTLD_NOW); 631 ASSERT_TRUE(handle != nullptr) << dlerror(); 632 int (*get_answer)(); 633 get_answer = reinterpret_cast<int (*)()>(dlsym(handle, "dl_df_1_global_get_answer")); 634 ASSERT_TRUE(get_answer != nullptr) << dlerror(); 635 ASSERT_EQ(42, get_answer()); 636 ASSERT_EQ(0, dlclose(handle)); 637#else 638 GTEST_LOG_(INFO) << "This test does nothing on arm/arm64 (to be reenabled once b/18137520 or b/18130452 are fixed).\n"; 639#endif 640} 641 642TEST(dlfcn, dlopen_failure) { 643 void* self = dlopen("/does/not/exist", RTLD_NOW); 644 ASSERT_TRUE(self == NULL); 645#if defined(__BIONIC__) 646 ASSERT_STREQ("dlopen failed: library \"/does/not/exist\" not found", dlerror()); 647#else 648 ASSERT_STREQ("/does/not/exist: cannot open shared object file: No such file or directory", dlerror()); 649#endif 650} 651 652static void* ConcurrentDlErrorFn(void*) { 653 dlopen("/child/thread", RTLD_NOW); 654 return reinterpret_cast<void*>(strdup(dlerror())); 655} 656 657TEST(dlfcn, dlerror_concurrent) { 658 dlopen("/main/thread", RTLD_NOW); 659 const char* main_thread_error = dlerror(); 660 ASSERT_SUBSTR("/main/thread", main_thread_error); 661 662 pthread_t t; 663 ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentDlErrorFn, NULL)); 664 void* result; 665 ASSERT_EQ(0, pthread_join(t, &result)); 666 char* child_thread_error = static_cast<char*>(result); 667 ASSERT_SUBSTR("/child/thread", child_thread_error); 668 free(child_thread_error); 669 670 ASSERT_SUBSTR("/main/thread", main_thread_error); 671} 672 673TEST(dlfcn, dlsym_failures) { 674 dlerror(); // Clear any pending errors. 675 void* self = dlopen(NULL, RTLD_NOW); 676 ASSERT_TRUE(self != NULL); 677 ASSERT_TRUE(dlerror() == NULL); 678 679 void* sym; 680 681#if defined(__BIONIC__) && !defined(__LP64__) 682 // RTLD_DEFAULT in lp32 bionic is not (void*)0 683 // so it can be distinguished from the NULL handle. 684 sym = dlsym(NULL, "test"); 685 ASSERT_TRUE(sym == NULL); 686 ASSERT_SUBSTR("dlsym library handle is null", dlerror()); 687#endif 688 689 // NULL symbol name. 690#if defined(__BIONIC__) 691 // glibc marks this parameter non-null and SEGVs if you cheat. 692 sym = dlsym(self, NULL); 693 ASSERT_TRUE(sym == NULL); 694 ASSERT_SUBSTR("", dlerror()); 695#endif 696 697 // Symbol that doesn't exist. 698 sym = dlsym(self, "ThisSymbolDoesNotExist"); 699 ASSERT_TRUE(sym == NULL); 700 ASSERT_SUBSTR("undefined symbol: ThisSymbolDoesNotExist", dlerror()); 701 702 ASSERT_EQ(0, dlclose(self)); 703} 704 705TEST(dlfcn, dladdr_executable) { 706 dlerror(); // Clear any pending errors. 707 void* self = dlopen(NULL, RTLD_NOW); 708 ASSERT_TRUE(self != NULL); 709 ASSERT_TRUE(dlerror() == NULL); 710 711 void* sym = dlsym(self, "DlSymTestFunction"); 712 ASSERT_TRUE(sym != NULL); 713 714 // Deliberately ask dladdr for an address inside a symbol, rather than the symbol base address. 715 void* addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(sym) + 2); 716 717 Dl_info info; 718 int rc = dladdr(addr, &info); 719 ASSERT_NE(rc, 0); // Zero on error, non-zero on success. 720 721 // Get the name of this executable. 722 char executable_path[PATH_MAX]; 723 rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path)); 724 ASSERT_NE(rc, -1); 725 executable_path[rc] = '\0'; 726 727 // The filename should be that of this executable. 728 char dli_realpath[PATH_MAX]; 729 ASSERT_TRUE(realpath(info.dli_fname, dli_realpath) != nullptr); 730 ASSERT_STREQ(executable_path, dli_realpath); 731 732 // The symbol name should be the symbol we looked up. 733 ASSERT_STREQ(info.dli_sname, "DlSymTestFunction"); 734 735 // The address should be the exact address of the symbol. 736 ASSERT_EQ(info.dli_saddr, sym); 737 738 std::vector<map_record> maps; 739 ASSERT_TRUE(Maps::parse_maps(&maps)); 740 741 void* base_address = nullptr; 742 for (const map_record& rec : maps) { 743 if (executable_path == rec.pathname) { 744 base_address = reinterpret_cast<void*>(rec.addr_start); 745 break; 746 } 747 } 748 749 // The base address should be the address we were loaded at. 750 ASSERT_EQ(info.dli_fbase, base_address); 751 752 ASSERT_EQ(0, dlclose(self)); 753} 754 755#if defined(__LP64__) 756#define BIONIC_PATH_TO_LIBC "/system/lib64/libc.so" 757#else 758#define BIONIC_PATH_TO_LIBC "/system/lib/libc.so" 759#endif 760 761TEST(dlfcn, dladdr_libc) { 762#if defined(__BIONIC__) 763 Dl_info info; 764 void* addr = reinterpret_cast<void*>(puts); // well-known libc function 765 ASSERT_TRUE(dladdr(addr, &info) != 0); 766 767 // /system/lib is symlink when this test is executed on host. 768 char libc_realpath[PATH_MAX]; 769 ASSERT_TRUE(realpath(BIONIC_PATH_TO_LIBC, libc_realpath) == libc_realpath); 770 771 ASSERT_STREQ(libc_realpath, info.dli_fname); 772 // TODO: add check for dfi_fbase 773 ASSERT_STREQ("puts", info.dli_sname); 774 ASSERT_EQ(addr, info.dli_saddr); 775#else 776 GTEST_LOG_(INFO) << "This test does nothing for glibc. Glibc returns path from ldconfig " 777 "for libc.so, which is symlink itself (not a realpath).\n"; 778#endif 779} 780 781TEST(dlfcn, dladdr_invalid) { 782 Dl_info info; 783 784 dlerror(); // Clear any pending errors. 785 786 // No symbol corresponding to NULL. 787 ASSERT_EQ(dladdr(NULL, &info), 0); // Zero on error, non-zero on success. 788 ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3). 789 790 // No symbol corresponding to a stack address. 791 ASSERT_EQ(dladdr(&info, &info), 0); // Zero on error, non-zero on success. 792 ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3). 793} 794 795// GNU-style ELF hash tables are incompatible with the MIPS ABI. 796// MIPS requires .dynsym to be sorted to match the GOT but GNU-style requires sorting by hash code. 797TEST(dlfcn, dlopen_library_with_only_gnu_hash) { 798#if !defined(__mips__) 799 dlerror(); // Clear any pending errors. 800 void* handle = dlopen("libgnu-hash-table-library.so", RTLD_NOW); 801 ASSERT_TRUE(handle != nullptr) << dlerror(); 802 auto guard = make_scope_guard([&]() { 803 dlclose(handle); 804 }); 805 void* sym = dlsym(handle, "getRandomNumber"); 806 ASSERT_TRUE(sym != nullptr) << dlerror(); 807 int (*fn)(void); 808 fn = reinterpret_cast<int (*)(void)>(sym); 809 EXPECT_EQ(4, fn()); 810 811 Dl_info dlinfo; 812 ASSERT_TRUE(0 != dladdr(reinterpret_cast<void*>(fn), &dlinfo)); 813 814 ASSERT_TRUE(fn == dlinfo.dli_saddr); 815 ASSERT_STREQ("getRandomNumber", dlinfo.dli_sname); 816 ASSERT_SUBSTR("libgnu-hash-table-library.so", dlinfo.dli_fname); 817#else 818 GTEST_LOG_(INFO) << "This test does nothing for mips/mips64; mips toolchain does not support '--hash-style=gnu'\n"; 819#endif 820} 821 822TEST(dlfcn, dlopen_library_with_only_sysv_hash) { 823 void* handle = dlopen("libsysv-hash-table-library.so", RTLD_NOW); 824 ASSERT_TRUE(handle != nullptr) << dlerror(); 825 auto guard = make_scope_guard([&]() { 826 dlclose(handle); 827 }); 828 void* sym = dlsym(handle, "getRandomNumber"); 829 ASSERT_TRUE(sym != nullptr) << dlerror(); 830 int (*fn)(void); 831 fn = reinterpret_cast<int (*)(void)>(sym); 832 EXPECT_EQ(4, fn()); 833 834 Dl_info dlinfo; 835 ASSERT_TRUE(0 != dladdr(reinterpret_cast<void*>(fn), &dlinfo)); 836 837 ASSERT_TRUE(fn == dlinfo.dli_saddr); 838 ASSERT_STREQ("getRandomNumber", dlinfo.dli_sname); 839 ASSERT_SUBSTR("libsysv-hash-table-library.so", dlinfo.dli_fname); 840} 841 842TEST(dlfcn, dlopen_bad_flags) { 843 dlerror(); // Clear any pending errors. 844 void* handle; 845 846#if defined(__GLIBC__) 847 // glibc was smart enough not to define RTLD_NOW as 0, so it can detect missing flags. 848 handle = dlopen(NULL, 0); 849 ASSERT_TRUE(handle == NULL); 850 ASSERT_SUBSTR("invalid", dlerror()); 851#endif 852 853 handle = dlopen(NULL, 0xffffffff); 854 ASSERT_TRUE(handle == NULL); 855 ASSERT_SUBSTR("invalid", dlerror()); 856 857 // glibc actually allows you to choose both RTLD_NOW and RTLD_LAZY at the same time, and so do we. 858 handle = dlopen(NULL, RTLD_NOW|RTLD_LAZY); 859 ASSERT_TRUE(handle != NULL); 860 ASSERT_SUBSTR(NULL, dlerror()); 861} 862 863TEST(dlfcn, rtld_default_unknown_symbol) { 864 void* addr = dlsym(RTLD_DEFAULT, "ANY_UNKNOWN_SYMBOL_NAME"); 865 ASSERT_TRUE(addr == NULL); 866} 867 868TEST(dlfcn, rtld_default_known_symbol) { 869 void* addr = dlsym(RTLD_DEFAULT, "fopen"); 870 ASSERT_TRUE(addr != NULL); 871} 872 873TEST(dlfcn, rtld_next_unknown_symbol) { 874 void* addr = dlsym(RTLD_NEXT, "ANY_UNKNOWN_SYMBOL_NAME"); 875 ASSERT_TRUE(addr == NULL); 876} 877 878TEST(dlfcn, rtld_next_known_symbol) { 879 void* addr = dlsym(RTLD_NEXT, "fopen"); 880 ASSERT_TRUE(addr != NULL); 881} 882 883TEST(dlfcn, dlsym_weak_func) { 884 dlerror(); 885 void* handle = dlopen("libtest_dlsym_weak_func.so", RTLD_NOW); 886 ASSERT_TRUE(handle != NULL); 887 888 int (*weak_func)(); 889 weak_func = reinterpret_cast<int (*)()>(dlsym(handle, "weak_func")); 890 ASSERT_TRUE(weak_func != NULL) << "dlerror: " << dlerror(); 891 EXPECT_EQ(42, weak_func()); 892 dlclose(handle); 893} 894 895TEST(dlfcn, dlopen_undefined_weak_func) { 896 void* handle = dlopen("libtest_dlopen_weak_undefined_func.so", RTLD_NOW); 897 ASSERT_TRUE(handle != nullptr) << dlerror(); 898 int (*weak_func)(); 899 weak_func = reinterpret_cast<int (*)()>(dlsym(handle, "use_weak_undefined_func")); 900 ASSERT_TRUE(weak_func != nullptr) << dlerror(); 901 EXPECT_EQ(6551, weak_func()); 902 dlclose(handle); 903} 904 905TEST(dlfcn, dlopen_symlink) { 906 void* handle1 = dlopen("libdlext_test.so", RTLD_NOW); 907 void* handle2 = dlopen("libdlext_test_v2.so", RTLD_NOW); 908 ASSERT_TRUE(handle1 != NULL); 909 ASSERT_TRUE(handle2 != NULL); 910 ASSERT_EQ(handle1, handle2); 911 dlclose(handle1); 912 dlclose(handle2); 913} 914 915// libtest_dlopen_from_ctor_main.so depends on 916// libtest_dlopen_from_ctor.so which has a constructor 917// that calls dlopen(libc...). This is to test the situation 918// described in b/7941716. 919TEST(dlfcn, dlopen_dlopen_from_ctor) { 920#if defined(__BIONIC__) 921 void* handle = dlopen("libtest_dlopen_from_ctor_main.so", RTLD_NOW); 922 ASSERT_TRUE(handle != nullptr) << dlerror(); 923 dlclose(handle); 924#else 925 GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n"; 926#endif 927} 928