1#include <dlfcn.h> 2#include <pthread.h> 3#include <stdlib.h> 4#include <sys/wait.h> 5#include "test.h" 6#include "global.h" 7 8#define SO_FOR_NO_DELETE "lib_for_no_delete.so" 9#define SO_FOR_DLOPEN "lib_for_dlopen.so" 10#define SO_LOAD_BY_LOCAL "libdlopen_for_load_by_local_dso.so" 11#define SO_LOAD_BY_GLOBAL "libdlopen_for_load_by_global_dso.so" 12#define SO_CLOSE_RECURSIVE_OPEN_SO "libdlclose_recursive_dlopen_so.so" 13#define NR_DLCLOSE_THREADS 10 14 15typedef void(*TEST_PTR)(void); 16 17void do_dlopen(const char *name, int mode) 18{ 19 void* handle = dlopen(name, mode); 20 21 if(!handle) 22 t_error("dlopen(name=%s, mode=%d) failed: %s\n", name, mode, dlerror()); 23 24 if(dlclose(handle)) 25 t_error("dlclose %s failed : %s \n", name, dlerror()); 26} 27 28void dlopen_lazy() 29{ 30 do_dlopen(SO_FOR_DLOPEN, RTLD_LAZY); 31} 32 33void dlopen_now() 34{ 35 do_dlopen(SO_FOR_DLOPEN, RTLD_NOW); 36} 37 38void dlopen_global() 39{ 40 do_dlopen(SO_FOR_DLOPEN, RTLD_GLOBAL); 41} 42 43void dlopen_local() 44{ 45 do_dlopen(SO_FOR_DLOPEN, RTLD_LOCAL); 46} 47 48void dlopen_so_used_by_dlsym() 49{ 50 void* handle1 = dlopen(SO_LOAD_BY_LOCAL, RTLD_LOCAL); 51 if(!handle1) 52 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_LOAD_BY_LOCAL, RTLD_LOCAL, dlerror()); 53 54 // dlsym can't see the so which is loaded by RTLD_LOCAL. 55 TEST_PTR for_local_ptr = dlsym(RTLD_DEFAULT, "for_local"); 56 if (for_local_ptr != NULL) { 57 t_error("dlsym RTLD_LOCAL so(%s) should failed but get succeed.\n", "for_local"); 58 } 59 60 if(dlclose(handle1)) 61 t_error("dlclose %s failed : %s \n", SO_LOAD_BY_LOCAL, dlerror()); 62 63 64 void* handle2 = dlopen(SO_LOAD_BY_GLOBAL, RTLD_GLOBAL); 65 if(!handle2) 66 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_LOAD_BY_GLOBAL, RTLD_LOCAL, dlerror()); 67 68 // dlsym can see the so which is loaded by RTLD_DEFAULT even without dependencies. 69 TEST_PTR for_global_ptr = dlsym(RTLD_DEFAULT, "for_global"); 70 if (!for_global_ptr) { 71 t_error("dlsym RTLD_GLOBAL so(%s) should succeed but get failed: %s \n", "for_global", dlerror()); 72 } 73 74 if(dlclose(handle2)) 75 t_error("dlclose %s failed : %s \n", SO_LOAD_BY_GLOBAL, dlerror()); 76} 77 78void dlopen_nodelete_and_noload() 79{ 80 void* handle1 = dlopen(SO_FOR_NO_DELETE, RTLD_NODELETE); 81 82 if(!handle1) 83 t_error("dlopen(name=%s, mode=RTLD_NODELETE) failed: %s\n", SO_FOR_NO_DELETE, dlerror()); 84 85 if(dlclose(handle1)) 86 t_error("dlclose %s failed : %s \n", SO_FOR_NO_DELETE, dlerror()); 87 88 89 void* handle2 = dlopen(SO_FOR_NO_DELETE, RTLD_NOLOAD); 90 if(!handle2) 91 t_error("dlopen(name=%s, mode=RTLD_NOLOAD) failed: %s\n", SO_FOR_NO_DELETE, dlerror()); 92 93 if (handle1 != handle2) { 94 t_error("dlopen %s by RTLD_NODELETE but get different handle when dlopen by RTLD_NOLOAD again.\n", SO_FOR_NO_DELETE); 95 } 96} 97 98void dlopen_dlclose() 99{ 100 void* handle = dlopen(SO_FOR_DLOPEN, RTLD_LOCAL); 101 if(!handle) 102 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror()); 103 104 handle = dlopen(SO_FOR_DLOPEN, RTLD_LOCAL); 105 if(!handle) 106 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror()); 107 108 if(dlclose(handle)) 109 t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror()); 110 111 // lib should still exist in memory. 112 handle = dlopen(SO_FOR_DLOPEN, RTLD_NOLOAD); 113 if(!handle) 114 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror()); 115 116 if(dlclose(handle)) 117 t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror()); 118 119 // It need to do one more dlclose because call dlopen by RTLD_NOLOAD add reference counting. 120 if(dlclose(handle)) 121 t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror()); 122 123 // dlopen and dlclose call counts match so the lib should not exist in memory. 124 handle = dlopen(SO_FOR_DLOPEN, RTLD_NOLOAD); 125 if(handle) { 126 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror()); 127 dlclose(handle); 128 } 129} 130 131#define DLOPEN_WEAK "libdlopen_weak.so" 132typedef int (*func_ptr)(); 133 134void dlopen_dlclose_weak() 135{ 136 void* handle = dlopen(DLOPEN_WEAK, RTLD_LAZY | RTLD_GLOBAL); 137 if (!handle) 138 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_WEAK, RTLD_LAZY | RTLD_GLOBAL, dlerror()); 139 func_ptr fn = (func_ptr)dlsym(handle, "test_number"); 140 if (fn) { 141 int ret = fn(); 142 if (ret != GLOBAL_VALUE) 143 t_error("weak symbol relocation error: so_name: %s, symbol: test_number\n", DLOPEN_WEAK); 144 } 145 dlclose(handle); 146} 147 148void dlclose_recursive() 149{ 150 void *handle = dlopen(SO_CLOSE_RECURSIVE_OPEN_SO, RTLD_LAZY | RTLD_LOCAL); 151 if (!handle) 152 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_CLOSE_RECURSIVE_OPEN_SO, RTLD_LAZY | RTLD_LOCAL, dlerror()); 153 154 /* close handle normally, if libc doesn't support close .so file recursivly 155 * it will be deedlock, and timed out error will happen 156 */ 157 dlclose(handle); 158} 159 160void *dlclose_recursive_thread() 161{ 162 dlclose_recursive(); 163 return NULL; 164} 165 166void dlclose_recursive_by_multipthread() 167{ 168 pthread_t testThreads[NR_DLCLOSE_THREADS] = {0}; 169 for (int i = 0; i < NR_DLCLOSE_THREADS; ++i) { 170 pthread_create(&testThreads[i], NULL, dlclose_recursive_thread, NULL); 171 } 172 173 for (int i = 0; i < NR_DLCLOSE_THREADS; ++i) { 174 pthread_join(testThreads[i], NULL); 175 } 176} 177 178#define DLOPEN_GLOBAL "libdlopen_global.so" 179#define DLOPEN_LOCAL "libdlopen_local.so" 180 181void dlopen_global_test() 182{ 183 int value; 184 void *global_handler = dlopen(DLOPEN_GLOBAL, RTLD_GLOBAL); 185 if (!global_handler) 186 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_GLOBAL, RTLD_GLOBAL, dlerror()); 187 func_ptr global_fn = (func_ptr)dlsym(global_handler, "global_caller"); 188 if (global_fn) { 189 value = global_fn(); 190 if (value != GLOBAL_VALUE) 191 t_error("global caller returned: %d, expected: %d\n", value, GLOBAL_VALUE); 192 } 193 194 void *local_handler = dlopen(DLOPEN_LOCAL, RTLD_LOCAL); 195 if (!local_handler) 196 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_LOCAL, RTLD_LOCAL, dlerror()); 197 func_ptr local_fn = (func_ptr)dlsym(local_handler, "local_caller"); 198 if (local_fn) { 199 value = local_fn(); 200 if (value != GLOBAL_VALUE) 201 t_error("local caller returned: %d, expected: %d\n", value, GLOBAL_VALUE); 202 } 203 dlclose(global_handler); 204 dlclose(local_handler); 205} 206 207#define DLCLOSE_EXIT_DEAD_LOCK "libdl_multithread_test_dso.so" 208 209void dlclose_exit_test() 210{ 211 int status; 212 void* handle; 213 int pid = fork(); 214 switch (pid) { 215 case -1: 216 t_error("fork failed: %d\n", __LINE__); 217 break; 218 case 0: 219 handle = dlopen(DLCLOSE_EXIT_DEAD_LOCK, RTLD_GLOBAL); 220 if (!handle) { 221 t_error("dlclose_exit_test dlopen %s failed: %s", DLCLOSE_EXIT_DEAD_LOCK, dlerror()); 222 exit(EXIT_FAILURE); 223 } 224 exit(EXIT_SUCCESS); 225 default: 226 waitpid(pid, &status, WUNTRACED); 227 if (WIFEXITED(status)) { 228 if (WEXITSTATUS(status) != EXIT_SUCCESS) { 229 t_error("dlclose_exit_test failed"); 230 }; 231 } 232 break; 233 } 234 return; 235} 236 237#define DLCLOSE_WITH_TLS "libdlclose_tls.so" 238typedef void *(*functype)(void); 239void dlclose_with_tls_test() 240{ 241 void* handle = dlopen(DLCLOSE_WITH_TLS, RTLD_GLOBAL); 242 if (!handle) { 243 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLCLOSE_WITH_TLS, RTLD_GLOBAL, dlerror()); 244 } 245 functype func = (functype)dlsym(handle, "foo_ctor"); 246 func(); 247 dlclose(handle); 248} 249 250int main(int argc, char *argv[]) 251{ 252 void *h, *g; 253 int *i, *i2; 254 char *s; 255 void (*f)(void); 256 char buf[512]; 257 258 if (!t_pathrel(buf, sizeof buf, argv[0], "libdlopen_dso.so")) { 259 t_error("failed to obtain relative path to dlopen_dso.so\n"); 260 return 1; 261 } 262 h = dlopen(buf, RTLD_LAZY|RTLD_LOCAL); 263 if (!h) 264 t_error("dlopen %s failed: %s\n", buf, dlerror()); 265 i = dlsym(h, "i"); 266 if (!i) 267 t_error("dlsym i failed: %s\n", dlerror()); 268 if (*i != 1) 269 t_error("initialization failed: want i=1 got i=%d\n", *i); 270 f = (void (*)(void))dlsym(h, "f"); 271 if (!f) 272 t_error("dlsym f failed: %s\n", dlerror()); 273 f(); 274 if (*i != 2) 275 t_error("f call failed: want i=2 got i=%d\n", *i); 276 277 g = dlopen(0, RTLD_LAZY|RTLD_LOCAL); 278 if (!g) 279 t_error("dlopen 0 failed: %s\n", dlerror()); 280 i2 = dlsym(g, "i"); 281 s = dlerror(); 282 if (i2 || s == 0) 283 t_error("dlsym i should have failed\n"); 284 if (dlsym(g, "main") != (void*)main) 285 t_error("dlsym main failed: %s\n", dlerror()); 286 287 /* close+open reinitializes the dso with glibc but not with musl */ 288 h = dlopen(buf, RTLD_LAZY|RTLD_GLOBAL); 289 i2 = dlsym(g, "i"); 290 if (!i2) 291 t_error("dlsym i failed: %s\n", dlerror()); 292 if (i2 != i) 293 t_error("reopened dso should have the same symbols, want %p, got %p\n", i, i2); 294 if (*i2 != 2) 295 t_error("reopened dso should have the same symbols, want i2==2, got i2==%d\n", *i2); 296 if (dlclose(g)) 297 t_error("dlclose failed: %s\n", dlerror()); 298 if (dlclose(h)) 299 t_error("dlclose failed: %s\n", dlerror()); 300 301 dlopen_lazy(); 302 dlopen_now(); 303 dlopen_global(); 304 dlopen_local(); 305 dlopen_so_used_by_dlsym(); 306 dlopen_nodelete_and_noload(); 307 dlopen_dlclose(); 308 dlopen_dlclose_weak(); 309 dlclose_recursive(); 310 dlclose_recursive_by_multipthread(); 311 dlopen_global_test(); 312 dlclose_exit_test(); 313 dlclose_with_tls_test(); 314 315 return t_status; 316} 317