unload.m 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // xpc leaks memory in dlopen(). Disable it.
  2. // TEST_ENV XPC_SERVICES_UNAVAILABLE=1
  3. /*
  4. TEST_BUILD
  5. $C{COMPILE} $DIR/unload4.m -o unload4.dylib -dynamiclib
  6. $C{COMPILE_C} $DIR/unload3.c -o unload3.dylib -dynamiclib
  7. $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle $C{FORCE_LOAD_ARCLITE} -Xlinker -undefined -Xlinker dynamic_lookup
  8. $C{COMPILE} $DIR/unload.m -o unload.exe -framework Foundation
  9. END
  10. */
  11. /*
  12. TEST_BUILD_OUTPUT
  13. ld: warning: -undefined dynamic_lookup is deprecated on .*
  14. OR
  15. END
  16. */
  17. #include "test.h"
  18. #include <objc/runtime.h>
  19. #include <dlfcn.h>
  20. #include <unistd.h>
  21. #include "unload.h"
  22. #if __has_feature(objc_arc)
  23. int main()
  24. {
  25. testwarn("rdar://11368528 confused by Foundation");
  26. succeed(__FILE__);
  27. }
  28. #else
  29. static id forward_handler(void)
  30. {
  31. return 0;
  32. }
  33. static BOOL hasName(const char * const *names, const char *query)
  34. {
  35. const char *name;
  36. while ((name = *names++)) {
  37. if (strstr(name, query)) return YES;
  38. }
  39. return NO;
  40. }
  41. void cycle(void)
  42. {
  43. int i;
  44. char buf[100];
  45. unsigned int imageCount, imageCount0;
  46. const char **names;
  47. const char *name;
  48. names = objc_copyImageNames(&imageCount0);
  49. testassert(names);
  50. free(names);
  51. void *bundle = dlopen("unload2.bundle", RTLD_LAZY);
  52. testassert(bundle);
  53. names = objc_copyImageNames(&imageCount);
  54. testassert(names);
  55. testassert(imageCount == imageCount0 + 1);
  56. testassert(hasName(names, "unload2.bundle"));
  57. free(names);
  58. Class small = objc_getClass("SmallClass");
  59. Class big = objc_getClass("BigClass");
  60. Class missing = objc_getClass("SubclassOfMissingWeakImport");
  61. testassert(small);
  62. testassert(big);
  63. testassert(!missing);
  64. name = class_getImageName(small);
  65. testassert(name);
  66. testassert(strstr(name, "unload2.bundle"));
  67. name = class_getImageName(big);
  68. testassert(name);
  69. testassert(strstr(name, "unload2.bundle"));
  70. id o1 = [small new];
  71. id o2 = [big new];
  72. testassert(o1);
  73. testassert(o2);
  74. // give BigClass and BigClass->isa large method caches (4692641)
  75. // Flush caches part way through to test large empty caches.
  76. for (i = 0; i < 3000; i++) {
  77. sprintf(buf, "method_%d", i);
  78. SEL sel = sel_registerName(buf);
  79. ((void(*)(id, SEL))objc_msgSend)(o2, sel);
  80. ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
  81. }
  82. _objc_flush_caches(object_getClass(o2));
  83. for (i = 0; i < 17000; i++) {
  84. sprintf(buf, "method_%d", i);
  85. SEL sel = sel_registerName(buf);
  86. ((void(*)(id, SEL))objc_msgSend)(o2, sel);
  87. ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
  88. }
  89. RELEASE_VAR(o1);
  90. RELEASE_VAR(o2);
  91. testcollect();
  92. int err = dlclose(bundle);
  93. testassert(err == 0);
  94. err = dlclose(bundle);
  95. testassert(err == -1); // already closed
  96. _objc_flush_caches(nil);
  97. testassert(objc_getClass("SmallClass") == NULL);
  98. testassert(objc_getClass("BigClass") == NULL);
  99. names = objc_copyImageNames(&imageCount);
  100. testassert(names);
  101. testassert(imageCount == imageCount0);
  102. testassert(! hasName(names, "unload2.bundle"));
  103. free(names);
  104. // these selectors came from the bundle
  105. testassert(0 == strcmp("unload2_instance_method", sel_getName(sel_registerName("unload2_instance_method"))));
  106. testassert(0 == strcmp("unload2_category_method", sel_getName(sel_registerName("unload2_category_method"))));
  107. // This protocol came from the bundle.
  108. // It isn't unloaded cleanly (rdar://20664713), but neither
  109. // may it cause the protocol table to crash after unloading.
  110. testassert(objc_getProtocol("SmallProtocol"));
  111. }
  112. int main()
  113. {
  114. objc_setForwardHandler((void*)&forward_handler, (void*)&forward_handler);
  115. #if defined(__arm__) || defined(__arm64__)
  116. int count = 10;
  117. #else
  118. int count = is_guardmalloc() ? 10 : 100;
  119. #endif
  120. cycle();
  121. #if __LP64__
  122. // fixme heap use goes up 512 bytes after the 2nd cycle only - bad or not?
  123. cycle();
  124. #endif
  125. leak_mark();
  126. while (count--) {
  127. cycle();
  128. }
  129. leak_check(0);
  130. // 5359412 Make sure dylibs with nothing other than image_info can close
  131. void *dylib = dlopen("unload3.dylib", RTLD_LAZY);
  132. testassert(dylib);
  133. int err = dlclose(dylib);
  134. testassert(err == 0);
  135. err = dlclose(dylib);
  136. testassert(err == -1); // already closed
  137. // Make sure dylibs with real objc content cannot close
  138. dylib = dlopen("unload4.dylib", RTLD_LAZY);
  139. testassert(dylib);
  140. err = dlclose(dylib);
  141. testassert(err == 0);
  142. err = dlclose(dylib);
  143. testassert(err == -1); // already closed
  144. succeed(__FILE__);
  145. }
  146. #endif