runtime_loader: Properly unload libraries whose dependencies fail to load.

If some (but not all) of the dependencies failed to load, they
will be in an inconsistent state (some NEEDED unset/uninitialized, etc.)
In order to neither leak this data nor have it cause problems later,
we should unload actually call unload_library() and thus put_image()
to have the unused/uninitialized dependencies be propery unloaded.

To make this work correctly, adjust unload_library to only call
the exit hooks if the initialization hooks were also called.

Fixes a crash in Firefox startup when some dependencies aren't installed.
This commit is contained in:
Augustin Cavalier
2024-08-14 15:45:50 -04:00
parent 78c0fc35a7
commit 1f1be52007
2 changed files with 28 additions and 29 deletions
+21 -26
View File
@@ -716,8 +716,7 @@ load_library(char const *path, uint32 flags, bool addOn, void* caller,
err: err:
KTRACE("rld: load_library(\"%s\") failed: %s", path, strerror(status)); KTRACE("rld: load_library(\"%s\") failed: %s", path, strerror(status));
dequeue_loaded_image(image); unload_library(image, -1, addOn);
delete_image(image);
return status; return status;
} }
@@ -744,28 +743,24 @@ unload_library(void* handle, image_id imageID, bool addOn)
// we only check images that have been already initialized // we only check images that have been already initialized
status_t status = B_BAD_IMAGE_ID;
if (handle != NULL) { if (handle != NULL) {
image = (image_t*)handle; image = (image_t*)handle;
put_image(image); put_image(image);
status = B_OK;
} else { } else {
image = find_loaded_image_by_id(imageID, true); image = find_loaded_image_by_id(imageID, true);
if (image != NULL) { if (image == NULL)
// unload image return B_BAD_IMAGE_ID;
if (type == image->type) {
put_image(image); // unload image
status = B_OK; if (type != image->type)
} else return B_BAD_VALUE;
status = B_BAD_VALUE; put_image(image);
}
} }
if (status == B_OK) { while ((image = get_disposable_images().head) != NULL) {
while ((image = get_disposable_images().head) != NULL) { dequeue_disposable_image(image);
dequeue_disposable_image(image);
if ((image->flags & RFLAG_INITIALIZED) != 0) {
// Call the exit hooks that live in this image. // Call the exit hooks that live in this image.
// Note: With the Itanium ABI this shouldn't really be done this // Note: With the Itanium ABI this shouldn't really be done this
// way anymore, since global destructors are registered via // way anymore, since global destructors are registered via
@@ -778,7 +773,7 @@ unload_library(void* handle, image_id imageID, bool addOn)
// probably more expensive than calling // probably more expensive than calling
// call_atexit_hooks_for_range() only here, which happens only when // call_atexit_hooks_for_range() only here, which happens only when
// libraries are unloaded dynamically. // libraries are unloaded dynamically.
if (gRuntimeLoader.call_atexit_hooks_for_range) { if (gRuntimeLoader.call_atexit_hooks_for_range != NULL) {
gRuntimeLoader.call_atexit_hooks_for_range( gRuntimeLoader.call_atexit_hooks_for_range(
image->regions[0].vmstart, image->regions[0].vmsize); image->regions[0].vmstart, image->regions[0].vmsize);
} }
@@ -786,18 +781,18 @@ unload_library(void* handle, image_id imageID, bool addOn)
image_event(image, IMAGE_EVENT_UNINITIALIZING); image_event(image, IMAGE_EVENT_UNINITIALIZING);
call_term_functions(image); call_term_functions(image);
TLSBlockTemplates::Get().Unregister(image->dso_tls_id);
unmap_image(image);
image_event(image, IMAGE_EVENT_UNLOADING);
delete_image(image);
} }
TLSBlockTemplates::Get().Unregister(image->dso_tls_id);
unmap_image(image);
image_event(image, IMAGE_EVENT_UNLOADING);
delete_image(image);
} }
return status; return B_OK;
} }
+7 -3
View File
@@ -277,14 +277,18 @@ put_image(image_t* image)
// and remove all dependencies // and remove all dependencies
if (atomic_add(&image->ref_count, -1) == 1) { if (atomic_add(&image->ref_count, -1) == 1) {
size_t i;
dequeue_image(&sLoadedImages, image); dequeue_image(&sLoadedImages, image);
enqueue_image(&sDisposableImages, image); enqueue_image(&sDisposableImages, image);
sLoadedImageCount--; sLoadedImageCount--;
for (i = 0; i < image->num_needed; i++) // If the image wasn't fully loaded, its NEEDED may be incomplete.
if (image->needed == NULL)
image->num_needed = 0;
for (size_t i = 0; i < image->num_needed; i++) {
if (image->needed[i] == NULL)
continue;
put_image(image->needed[i]); put_image(image->needed[i]);
}
} }
} }