Home | History | Annotate | Download | only in tests
      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 <string>
     26 
     27 #define ASSERT_SUBSTR(needle, haystack) \
     28     ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack)
     29 
     30 static bool gCalled = false;
     31 extern "C" void DlSymTestFunction() {
     32   gCalled = true;
     33 }
     34 
     35 TEST(dlfcn, dlsym_in_self) {
     36   dlerror(); // Clear any pending errors.
     37   void* self = dlopen(NULL, RTLD_NOW);
     38   ASSERT_TRUE(self != NULL);
     39   ASSERT_TRUE(dlerror() == NULL);
     40 
     41   void* sym = dlsym(self, "DlSymTestFunction");
     42   ASSERT_TRUE(sym != NULL);
     43 
     44   void (*function)() = reinterpret_cast<void(*)()>(sym);
     45 
     46   gCalled = false;
     47   function();
     48   ASSERT_TRUE(gCalled);
     49 
     50   ASSERT_EQ(0, dlclose(self));
     51 }
     52 
     53 TEST(dlfcn, dlopen_failure) {
     54   void* self = dlopen("/does/not/exist", RTLD_NOW);
     55   ASSERT_TRUE(self == NULL);
     56 #if __BIONIC__
     57   ASSERT_STREQ("dlopen failed: library \"/does/not/exist\" not found", dlerror());
     58 #else
     59   ASSERT_STREQ("/does/not/exist: cannot open shared object file: No such file or directory", dlerror());
     60 #endif
     61 }
     62 
     63 static void* ConcurrentDlErrorFn(void*) {
     64   dlopen("/child/thread", RTLD_NOW);
     65   return reinterpret_cast<void*>(strdup(dlerror()));
     66 }
     67 
     68 TEST(dlfcn, dlerror_concurrent) {
     69   dlopen("/main/thread", RTLD_NOW);
     70   const char* main_thread_error = dlerror();
     71   ASSERT_SUBSTR("/main/thread", main_thread_error);
     72 
     73   pthread_t t;
     74   ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentDlErrorFn, NULL));
     75   void* result;
     76   ASSERT_EQ(0, pthread_join(t, &result));
     77   char* child_thread_error = static_cast<char*>(result);
     78   ASSERT_SUBSTR("/child/thread", child_thread_error);
     79   free(child_thread_error);
     80 
     81   ASSERT_SUBSTR("/main/thread", main_thread_error);
     82 }
     83 
     84 TEST(dlfcn, dlsym_failures) {
     85   dlerror(); // Clear any pending errors.
     86   void* self = dlopen(NULL, RTLD_NOW);
     87   ASSERT_TRUE(self != NULL);
     88   ASSERT_TRUE(dlerror() == NULL);
     89 
     90   void* sym;
     91 
     92   // NULL handle.
     93   sym = dlsym(NULL, "test");
     94   ASSERT_TRUE(sym == NULL);
     95 #if __BIONIC__
     96   ASSERT_SUBSTR("dlsym library handle is null", dlerror());
     97 #else
     98   ASSERT_SUBSTR("undefined symbol: test", dlerror()); // glibc isn't specific about the failure.
     99 #endif
    100 
    101   // NULL symbol name.
    102 #if __BIONIC__
    103   // glibc marks this parameter non-null and SEGVs if you cheat.
    104   sym = dlsym(self, NULL);
    105   ASSERT_TRUE(sym == NULL);
    106   ASSERT_SUBSTR("", dlerror());
    107 #endif
    108 
    109   // Symbol that doesn't exist.
    110   sym = dlsym(self, "ThisSymbolDoesNotExist");
    111   ASSERT_TRUE(sym == NULL);
    112   ASSERT_SUBSTR("undefined symbol: ThisSymbolDoesNotExist", dlerror());
    113 
    114   ASSERT_EQ(0, dlclose(self));
    115 }
    116 
    117 TEST(dlfcn, dladdr) {
    118   dlerror(); // Clear any pending errors.
    119   void* self = dlopen(NULL, RTLD_NOW);
    120   ASSERT_TRUE(self != NULL);
    121   ASSERT_TRUE(dlerror() == NULL);
    122 
    123   void* sym = dlsym(self, "DlSymTestFunction");
    124   ASSERT_TRUE(sym != NULL);
    125 
    126   // Deliberately ask dladdr for an address inside a symbol, rather than the symbol base address.
    127   void* addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(sym) + 2);
    128 
    129   Dl_info info;
    130   int rc = dladdr(addr, &info);
    131   ASSERT_NE(rc, 0); // Zero on error, non-zero on success.
    132 
    133   // Get the name of this executable.
    134   char executable_path[PATH_MAX];
    135   rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path));
    136   ASSERT_NE(rc, -1);
    137   executable_path[rc] = '\0';
    138   std::string executable_name(basename(executable_path));
    139 
    140   // The filename should be that of this executable.
    141   // Note that we don't know whether or not we have the full path, so we want an "ends_with" test.
    142   std::string dli_fname(info.dli_fname);
    143   dli_fname = basename(&dli_fname[0]);
    144   ASSERT_EQ(dli_fname, executable_name);
    145 
    146   // The symbol name should be the symbol we looked up.
    147   ASSERT_STREQ(info.dli_sname, "DlSymTestFunction");
    148 
    149   // The address should be the exact address of the symbol.
    150   ASSERT_EQ(info.dli_saddr, sym);
    151 
    152   // Look in /proc/pid/maps to find out what address we were loaded at.
    153   // TODO: factor /proc/pid/maps parsing out into a class and reuse all over bionic.
    154   void* base_address = NULL;
    155   char path[PATH_MAX];
    156   snprintf(path, sizeof(path), "/proc/%d/maps", getpid());
    157   char line[BUFSIZ];
    158   FILE* fp = fopen(path, "r");
    159   ASSERT_TRUE(fp != NULL);
    160   while (fgets(line, sizeof(line), fp) != NULL) {
    161     uintptr_t start = strtoul(line, 0, 16);
    162     line[strlen(line) - 1] = '\0'; // Chomp the '\n'.
    163     char* path = strchr(line, '/');
    164     if (path != NULL && strcmp(executable_path, path) == 0) {
    165       base_address = reinterpret_cast<void*>(start);
    166       break;
    167     }
    168   }
    169   fclose(fp);
    170 
    171   // The base address should be the address we were loaded at.
    172   ASSERT_EQ(info.dli_fbase, base_address);
    173 
    174   ASSERT_EQ(0, dlclose(self));
    175 }
    176 
    177 TEST(dlfcn, dladdr_invalid) {
    178   Dl_info info;
    179 
    180   dlerror(); // Clear any pending errors.
    181 
    182   // No symbol corresponding to NULL.
    183   ASSERT_EQ(dladdr(NULL, &info), 0); // Zero on error, non-zero on success.
    184   ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
    185 
    186   // No symbol corresponding to a stack address.
    187   ASSERT_EQ(dladdr(&info, &info), 0); // Zero on error, non-zero on success.
    188   ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
    189 }
    190 
    191 // Our dynamic linker doesn't support GNU hash tables.
    192 #if defined(__BIONIC__)
    193 // GNU-style ELF hash tables are incompatible with the MIPS ABI.
    194 // MIPS requires .dynsym to be sorted to match the GOT but GNU-style requires sorting by hash code.
    195 #if !defined(__mips__)
    196 TEST(dlfcn, dlopen_library_with_only_gnu_hash) {
    197   dlerror(); // Clear any pending errors.
    198   void* handle = dlopen("no-elf-hash-table-library.so", RTLD_NOW);
    199   ASSERT_TRUE(handle == NULL);
    200   ASSERT_STREQ("dlopen failed: empty/missing DT_HASH in \"no-elf-hash-table-library.so\" (built with --hash-style=gnu?)", dlerror());
    201 }
    202 #endif
    203 #endif
    204 
    205 TEST(dlfcn, dlopen_bad_flags) {
    206   dlerror(); // Clear any pending errors.
    207   void* handle;
    208 
    209 #ifdef __GLIBC__
    210   // glibc was smart enough not to define RTLD_NOW as 0, so it can detect missing flags.
    211   handle = dlopen(NULL, 0);
    212   ASSERT_TRUE(handle == NULL);
    213   ASSERT_SUBSTR("invalid", dlerror());
    214 #endif
    215 
    216   handle = dlopen(NULL, 0xffffffff);
    217   ASSERT_TRUE(handle == NULL);
    218   ASSERT_SUBSTR("invalid", dlerror());
    219 
    220   // glibc actually allows you to choose both RTLD_NOW and RTLD_LAZY at the same time, and so do we.
    221   handle = dlopen(NULL, RTLD_NOW|RTLD_LAZY);
    222   ASSERT_TRUE(handle != NULL);
    223   ASSERT_SUBSTR(NULL, dlerror());
    224 }
    225