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